]> git.proxmox.com Git - systemd.git/commitdiff
New upstream version 246
authorMichael Biebl <biebl@debian.org>
Thu, 30 Jul 2020 19:55:53 +0000 (21:55 +0200)
committerMichael Biebl <biebl@debian.org>
Thu, 30 Jul 2020 19:55:53 +0000 (21:55 +0200)
1730 files changed:
.clang-format [new file with mode: 0644]
.editorconfig
.github/ISSUE_TEMPLATE/Bug_report.md
.github/workflows/build_test.yml [new file with mode: 0644]
.github/workflows/cifuzz.yml [new file with mode: 0644]
.github/workflows/ubuntu-build-check.sh [new file with mode: 0755]
.gitignore
.mailmap
.mkosi/mkosi.arch
.mkosi/mkosi.debian
.mkosi/mkosi.fedora
.mkosi/mkosi.opensuse [new file with mode: 0644]
.mkosi/mkosi.ubuntu
.travis.yml
NEWS
README
README.md
TODO
catalog/systemd.catalog.in
catalog/systemd.pl.catalog.in
coccinelle/set_ensure_put.cocci [new file with mode: 0644]
coccinelle/strempty.cocci
docs/AUTOMATIC_BOOT_ASSESSMENT.md
docs/BOOT_LOADER_SPECIFICATION.md
docs/CGROUP_DELEGATION.md
docs/CODE_QUALITY.md
docs/CODING_STYLE.md
docs/CONTAINER_INTERFACE.md
docs/CONTRIBUTING.md
docs/CONVERTING_TO_HOMED.md [new file with mode: 0644]
docs/DESKTOP_ENVIRONMENTS.md [new file with mode: 0644]
docs/ENVIRONMENT.md
docs/GROUP_RECORD.md
docs/HACKING.md
docs/HOME_DIRECTORY.md
docs/INITRD_INTERFACE.md
docs/JOURNAL_FILE_FORMAT.md [new file with mode: 0644]
docs/PASSWORD_AGENTS.md [new file with mode: 0644]
docs/PORTABILITY_AND_STABILITY.md
docs/RANDOM_SEEDS.md
docs/SECURITY.md
docs/TRANSIENT-SETTINGS.md
docs/UIDS-GIDS.md
docs/USERDB_AND_DESKTOPS.md [new file with mode: 0644]
docs/USER_GROUP_API.md
docs/USER_NAMES.md [new file with mode: 0644]
docs/USER_RECORD.md
factory/etc/nsswitch.conf
fuzzbuzz.yaml [deleted file]
hwdb.d/60-autosuspend.hwdb [new file with mode: 0644]
hwdb.d/60-keyboard.hwdb
hwdb.d/60-sensor.hwdb
hwdb.d/meson.build
hwdb.d/parse_hwdb.py
man/bootctl.xml
man/bootup.xml
man/coredump.conf.xml
man/coredumpctl.xml
man/crypttab.xml
man/custom-entities.ent.in
man/daemon.xml
man/directives-template.xml [new file with mode: 0644]
man/environment.d.xml
man/file-hierarchy.xml
man/homectl.xml
man/homed.conf.xml [new file with mode: 0644]
man/hostnamectl.xml
man/html.in
man/hwdb-usb-device.c [new file with mode: 0644]
man/journal-remote.conf.xml
man/journal-upload.conf.xml
man/journalctl.xml
man/journald.conf.xml
man/kernel-command-line.xml
man/kernel-install.xml
man/loader.conf.xml
man/logind.conf.xml
man/machine-id.xml
man/machine-info.xml
man/machinectl.xml
man/man.in
man/meson.build
man/networkctl.xml
man/networkd.conf.xml
man/nss-myhostname.xml
man/nss-mymachines.xml
man/nss-resolve.xml
man/nss-systemd.xml
man/org.freedesktop.LogControl1.xml [new file with mode: 0644]
man/org.freedesktop.home1.xml [new file with mode: 0644]
man/org.freedesktop.hostname1.xml [new file with mode: 0644]
man/org.freedesktop.import1.xml [new file with mode: 0644]
man/org.freedesktop.locale1.xml [new file with mode: 0644]
man/org.freedesktop.login1.xml [new file with mode: 0644]
man/org.freedesktop.machine1.xml [new file with mode: 0644]
man/org.freedesktop.resolve1.xml [new file with mode: 0644]
man/org.freedesktop.systemd1.xml [new file with mode: 0644]
man/org.freedesktop.timedate1.xml [new file with mode: 0644]
man/os-release.xml
man/pam_systemd.xml
man/pam_systemd_home.xml
man/path-documents.c [new file with mode: 0644]
man/portablectl.xml
man/pstore.conf.xml
man/repart.d.xml
man/resolvectl.xml
man/resolved.conf.xml
man/rules/meson.build
man/sd-bus-container-append.c [new file with mode: 0644]
man/sd-bus-container-read.c [new file with mode: 0644]
man/sd-bus.xml
man/sd-hwdb.xml [new file with mode: 0644]
man/sd-login.xml
man/sd_bus_add_match.xml
man/sd_bus_add_node_enumerator.xml [new file with mode: 0644]
man/sd_bus_add_object.xml [new file with mode: 0644]
man/sd_bus_add_object_manager.xml [new file with mode: 0644]
man/sd_bus_add_object_vtable.xml [deleted file]
man/sd_bus_call.xml [new file with mode: 0644]
man/sd_bus_call_method.xml [new file with mode: 0644]
man/sd_bus_can_send.xml [new file with mode: 0644]
man/sd_bus_close.xml
man/sd_bus_creds_get_pid.xml
man/sd_bus_default.xml
man/sd_bus_emit_signal.xml [new file with mode: 0644]
man/sd_bus_enqueue_for_read.xml
man/sd_bus_get_current_handler.xml [new file with mode: 0644]
man/sd_bus_get_fd.xml
man/sd_bus_get_name_creds.xml [new file with mode: 0644]
man/sd_bus_get_name_machine_id.xml [new file with mode: 0644]
man/sd_bus_interface_name_is_valid.xml [new file with mode: 0644]
man/sd_bus_is_open.xml
man/sd_bus_list_names.xml [new file with mode: 0644]
man/sd_bus_message_append.xml
man/sd_bus_message_at_end.xml [new file with mode: 0644]
man/sd_bus_message_dump.xml
man/sd_bus_message_get_type.xml
man/sd_bus_message_new_method_call.xml
man/sd_bus_message_new_method_error.xml
man/sd_bus_message_new_signal.xml
man/sd_bus_message_open_container.xml [new file with mode: 0644]
man/sd_bus_message_read.xml
man/sd_bus_message_read_array.xml
man/sd_bus_message_read_strv.xml [new file with mode: 0644]
man/sd_bus_message_seal.xml [new file with mode: 0644]
man/sd_bus_message_sensitive.xml
man/sd_bus_message_set_destination.xml
man/sd_bus_message_set_expect_reply.xml
man/sd_bus_negotiate_fds.xml
man/sd_bus_new.xml
man/sd_bus_query_sender_creds.xml [new file with mode: 0644]
man/sd_bus_reply_method_error.xml
man/sd_bus_reply_method_return.xml [new file with mode: 0644]
man/sd_bus_request_name.xml
man/sd_bus_send.xml [new file with mode: 0644]
man/sd_bus_set_address.xml [new file with mode: 0644]
man/sd_bus_set_close_on_exit.xml
man/sd_bus_set_connected_signal.xml
man/sd_bus_set_description.xml
man/sd_bus_set_exit_on_disconnect.xml [new file with mode: 0644]
man/sd_bus_set_method_call_timeout.xml [new file with mode: 0644]
man/sd_bus_set_property.xml [new file with mode: 0644]
man/sd_bus_set_server.xml [new file with mode: 0644]
man/sd_bus_set_watch_bind.xml
man/sd_bus_slot_get_bus.xml [new file with mode: 0644]
man/sd_bus_slot_ref.xml
man/sd_bus_slot_set_destroy_callback.xml
man/sd_bus_slot_set_floating.xml
man/sd_bus_start.xml [new file with mode: 0644]
man/sd_event_new.xml
man/sd_event_source_set_destroy_callback.xml
man/sd_hwdb_get.xml [new file with mode: 0644]
man/sd_hwdb_new.xml [new file with mode: 0644]
man/sd_id128_get_machine.xml
man/sd_journal_get_data.xml
man/sd_journal_has_runtime_files.xml
man/sd_journal_open.xml
man/sd_journal_print.xml
man/sd_journal_query_unique.xml
man/sd_login_monitor_new.xml
man/sd_machine_get_class.xml
man/sd_notify.xml
man/sd_path_lookup.xml [new file with mode: 0644]
man/sd_pid_get_owner_uid.xml
man/sd_seat_get_active.xml
man/standard-conf.xml
man/standard-specifiers.xml [new file with mode: 0644]
man/sysctl.d.xml
man/systemctl.xml
man/systemd-analyze.xml
man/systemd-bless-boot-generator.xml
man/systemd-bless-boot.service.xml
man/systemd-boot-check-no-failures.service.xml
man/systemd-boot.xml
man/systemd-cryptsetup@.service.xml
man/systemd-detect-virt.xml
man/systemd-environment-d-generator.xml
man/systemd-firstboot.xml
man/systemd-fstab-generator.xml
man/systemd-gpt-auto-generator.xml
man/systemd-homed.service.xml
man/systemd-hostnamed.service.xml
man/systemd-importd.service.xml
man/systemd-initctl.service.xml
man/systemd-journal-gatewayd.service.xml
man/systemd-journal-remote.service.xml
man/systemd-journal-upload.service.xml
man/systemd-journald.service.xml
man/systemd-localed.service.xml
man/systemd-logind.service.xml
man/systemd-machined.service.xml
man/systemd-modules-load.service.xml
man/systemd-mount.xml
man/systemd-network-generator.service.xml
man/systemd-networkd.service.xml
man/systemd-notify.xml
man/systemd-nspawn.xml
man/systemd-pstore.service.xml
man/systemd-random-seed.service.xml
man/systemd-repart.xml
man/systemd-resolved.service.xml
man/systemd-run.xml
man/systemd-sleep.conf.xml
man/systemd-socket-activate.xml
man/systemd-socket-proxyd.xml
man/systemd-suspend.service.xml
man/systemd-system.conf.xml
man/systemd-sysv-generator.xml
man/systemd-time-wait-sync.service.xml
man/systemd-timedated.service.xml
man/systemd-tmpfiles.xml
man/systemd-udevd.service.xml
man/systemd-update-done.service.xml
man/systemd-xdg-autostart-generator.xml [new file with mode: 0644]
man/systemd.automount.xml
man/systemd.device.xml
man/systemd.dnssd.xml
man/systemd.exec.xml
man/systemd.generator.xml
man/systemd.journal-fields.xml
man/systemd.kill.xml
man/systemd.link.xml
man/systemd.mount.xml
man/systemd.net-naming-scheme.xml
man/systemd.netdev.xml
man/systemd.network.xml
man/systemd.nspawn.xml
man/systemd.offline-updates.xml
man/systemd.path.xml
man/systemd.resource-control.xml
man/systemd.scope.xml
man/systemd.service.xml
man/systemd.slice.xml
man/systemd.socket.xml
man/systemd.special.xml
man/systemd.swap.xml
man/systemd.syntax.xml
man/systemd.target.xml
man/systemd.time.xml
man/systemd.timer.xml
man/systemd.unit.xml
man/systemd.xml
man/sysusers.d.xml
man/tc.xml [new file with mode: 0644]
man/timesyncd.conf.xml
man/tmpfiles.d.xml
man/udev.conf.xml
man/user@.service.xml
man/userdbctl.xml
man/vtable-example.c
man/yubikey-crypttab.sh
meson.build
meson_options.txt
mkosi.build
mkosi.default [deleted symlink]
network/80-vm-vt.network [new file with mode: 0644]
network/meson.build
po/be.po
po/be@latin.po
po/bg.po
po/ca.po
po/cs.po
po/da.po
po/de.po
po/el.po
po/es.po
po/fr.po
po/gl.po
po/hr.po
po/hu.po
po/id.po
po/it.po
po/ja.po
po/ko.po
po/lt.po
po/pl.po
po/pt_BR.po
po/ro.po
po/ru.po
po/sk.po
po/sr.po
po/sv.po
po/tr.po
po/uk.po
po/zh_CN.po
po/zh_TW.po
presets/90-systemd.preset
presets/user/90-systemd.preset
rules.d/50-udev-default.rules.in
rules.d/60-autosuspend.rules [new file with mode: 0644]
rules.d/60-persistent-storage.rules
rules.d/60-serial.rules
rules.d/61-autosuspend-manual.rules [deleted file]
rules.d/meson.build
semaphoreci/semaphore-runner.sh
shell-completion/bash/bootctl
shell-completion/bash/systemctl.in
shell-completion/bash/systemd-analyze
shell-completion/bash/systemd-cgls
shell-completion/bash/systemd-nspawn
shell-completion/bash/systemd-run
shell-completion/bash/udevadm
shell-completion/zsh/_bootctl
shell-completion/zsh/_systemctl.in
shell-completion/zsh/_systemd-run
src/analyze/analyze-condition.c
src/analyze/analyze-security.c
src/analyze/analyze-verify.c
src/analyze/analyze.c
src/basic/blockdev-util.c
src/basic/blockdev-util.h
src/basic/btrfs-util.c
src/basic/btrfs-util.h
src/basic/build.h
src/basic/cap-list.c
src/basic/capability-util.c
src/basic/capability-util.h
src/basic/cgroup-util.c
src/basic/cgroup-util.h
src/basic/conf-files.c
src/basic/copy.c
src/basic/device-nodes.c
src/basic/device-nodes.h
src/basic/dlfcn-util.h [new file with mode: 0644]
src/basic/efivars.c
src/basic/errno-util.h
src/basic/escape.c
src/basic/escape.h
src/basic/fd-util.c
src/basic/fileio.c
src/basic/fileio.h
src/basic/fs-util.c
src/basic/fs-util.h
src/basic/hash-funcs.c
src/basic/hash-funcs.h
src/basic/hashmap.c
src/basic/hashmap.h
src/basic/hostname-util.c
src/basic/hostname-util.h
src/basic/in-addr-util.c
src/basic/in-addr-util.h
src/basic/io-util.c
src/basic/label.c
src/basic/label.h
src/basic/limits-util.c
src/basic/linux/can/netlink.h [new file with mode: 0644]
src/basic/linux/hdlc/ioctl.h [new file with mode: 0644]
src/basic/linux/if.h
src/basic/linux/if_bonding.h
src/basic/linux/if_bridge.h
src/basic/linux/if_link.h
src/basic/linux/if_macsec.h
src/basic/linux/in.h
src/basic/linux/pkt_sched.h
src/basic/linux/rtnetlink.h
src/basic/locale-util.c
src/basic/locale-util.h
src/basic/log.c
src/basic/log.h
src/basic/macro.h
src/basic/memory-util.h
src/basic/meson.build
src/basic/missing_random.h
src/basic/missing_socket.h
src/basic/mkdir-label.c
src/basic/mkdir.c
src/basic/mkdir.h
src/basic/mountpoint-util.c
src/basic/mountpoint-util.h
src/basic/namespace-util.c
src/basic/namespace-util.h
src/basic/ordered-set.h
src/basic/parse-util.c
src/basic/path-lookup.c [new file with mode: 0644]
src/basic/path-lookup.h [new file with mode: 0644]
src/basic/path-util.c
src/basic/proc-cmdline.c
src/basic/proc-cmdline.h
src/basic/process-util.c
src/basic/process-util.h
src/basic/pthread-util.h [new file with mode: 0644]
src/basic/random-util.c
src/basic/random-util.h
src/basic/selinux-util.c
src/basic/selinux-util.h
src/basic/set.h
src/basic/siphash24.c
src/basic/siphash24.h
src/basic/smack-util.c
src/basic/smack-util.h
src/basic/socket-util.c
src/basic/socket-util.h
src/basic/sort-util.h
src/basic/special.h
src/basic/stat-util.c
src/basic/stat-util.h
src/basic/string-table.c
src/basic/string-util.c
src/basic/string-util.h
src/basic/strv.c
src/basic/strv.h
src/basic/terminal-util.h
src/basic/time-util.c
src/basic/time-util.h
src/basic/tmpfile-util.c
src/basic/unit-def.c
src/basic/unit-def.h
src/basic/unit-name.c
src/basic/user-util.c
src/basic/virt.c
src/basic/virt.h
src/boot/bootctl.c
src/boot/efi/boot.c
src/boot/efi/measure.c
src/busctl/busctl.c
src/cgls/cgls.c
src/cgtop/cgtop.c
src/core/apparmor-setup.c [new file with mode: 0644]
src/core/apparmor-setup.h [new file with mode: 0644]
src/core/automount.c
src/core/bpf-devices.c
src/core/bpf-devices.h
src/core/bpf-firewall.c
src/core/cgroup.c
src/core/cgroup.h
src/core/dbus-automount.c
src/core/dbus-cgroup.c
src/core/dbus-execute.c
src/core/dbus-job.c
src/core/dbus-job.h
src/core/dbus-kill.c
src/core/dbus-manager.c
src/core/dbus-manager.h
src/core/dbus-mount.c
src/core/dbus-path.c
src/core/dbus-scope.c
src/core/dbus-service.c
src/core/dbus-socket.c
src/core/dbus-swap.c
src/core/dbus-timer.c
src/core/dbus-unit.c
src/core/dbus-unit.h
src/core/dbus.c
src/core/dbus.h
src/core/device.c
src/core/efi-random.c
src/core/emergency-action.c
src/core/execute.c
src/core/execute.h
src/core/generator-setup.c [new file with mode: 0644]
src/core/generator-setup.h [new file with mode: 0644]
src/core/hostname-setup.c
src/core/job.c
src/core/load-dropin.c
src/core/load-dropin.h
src/core/load-fragment-gperf.gperf.m4
src/core/load-fragment.c
src/core/load-fragment.h
src/core/machine-id-setup.c
src/core/main.c
src/core/manager.c
src/core/manager.h
src/core/meson.build
src/core/mount-setup.c
src/core/mount-setup.h
src/core/mount.c
src/core/mount.h
src/core/namespace.c
src/core/namespace.h
src/core/org.freedesktop.systemd1.conf
src/core/path.c
src/core/path.h
src/core/scope.c
src/core/selinux-access.c
src/core/selinux-access.h
src/core/service.c
src/core/service.h
src/core/slice.c
src/core/socket.c
src/core/socket.h
src/core/swap.c
src/core/system.conf.in
src/core/systemd.pc.in
src/core/target.c
src/core/timer.c
src/core/transaction.c
src/core/unit-printf.c
src/core/unit.c
src/core/unit.h
src/core/user.conf.in
src/coredump/coredump.c
src/coredump/coredumpctl.c
src/cryptsetup/cryptsetup-generator.c
src/cryptsetup/cryptsetup-pkcs11.c
src/cryptsetup/cryptsetup-pkcs11.h
src/cryptsetup/cryptsetup-util.c [new file with mode: 0644]
src/cryptsetup/cryptsetup-util.h [new file with mode: 0644]
src/cryptsetup/cryptsetup.c
src/delta/delta.c
src/detect-virt/detect-virt.c
src/dissect/dissect.c
src/environment-d-generator/environment-d-generator.c
src/escape/escape.c
src/firstboot/firstboot.c
src/fstab-generator/fstab-generator.c
src/fuzz/fuzz-udev-rules.c
src/fuzz/fuzz-unit-file.c
src/fuzz/fuzz-xdg-desktop.c [new file with mode: 0644]
src/fuzz/fuzz.h
src/fuzz/fuzzer-entry-point.c [deleted file]
src/fuzz/meson.build
src/gpt-auto-generator/gpt-auto-generator.c
src/home/home-util.c
src/home/home-util.h
src/home/homectl-fido2.c [new file with mode: 0644]
src/home/homectl-fido2.h [new file with mode: 0644]
src/home/homectl-pkcs11.c [new file with mode: 0644]
src/home/homectl-pkcs11.h [new file with mode: 0644]
src/home/homectl.c
src/home/homed-conf.c [new file with mode: 0644]
src/home/homed-conf.h [new file with mode: 0644]
src/home/homed-gperf.gperf [new file with mode: 0644]
src/home/homed-home-bus.c
src/home/homed-home-bus.h
src/home/homed-home.c
src/home/homed-manager-bus.c
src/home/homed-manager-bus.h
src/home/homed-manager.c
src/home/homed-manager.h
src/home/homed.c
src/home/homed.conf [new file with mode: 0644]
src/home/homework-cifs.c
src/home/homework-cifs.h
src/home/homework-directory.c
src/home/homework-directory.h
src/home/homework-fido2.c [new file with mode: 0644]
src/home/homework-fido2.h [new file with mode: 0644]
src/home/homework-fscrypt.c
src/home/homework-fscrypt.h
src/home/homework-luks.c
src/home/homework-luks.h
src/home/homework-mount.c
src/home/homework-mount.h
src/home/homework-pkcs11.c
src/home/homework.c
src/home/homework.h
src/home/meson.build
src/home/pam_systemd_home.c
src/home/user-record-util.c
src/home/user-record-util.h
src/hostname/hostnamectl.c
src/hostname/hostnamed.c
src/hostname/org.freedesktop.hostname1.policy
src/hwdb/hwdb.c
src/id128/id128.c
src/import/curl-util.c
src/import/import-tar.c
src/import/importd.c
src/import/pull-tar.c
src/initctl/initctl.c
src/journal-remote/journal-gatewayd.c
src/journal-remote/journal-remote-main.c
src/journal-remote/journal-upload.c
src/journal-remote/microhttpd-util.c
src/journal/cat.c
src/journal/catalog.c
src/journal/compress.c
src/journal/compress.h
src/journal/journal-def.h
src/journal/journal-file.c
src/journal/journal-file.h
src/journal/journal-internal.h
src/journal/journal-send.c
src/journal/journal-verify.c
src/journal/journalctl.c
src/journal/journald-audit.c
src/journal/journald-kmsg.c
src/journal/journald-server.c
src/journal/journald-stream.c
src/journal/journald-syslog.c
src/journal/lookup3.h
src/journal/mmap-cache.c
src/journal/sd-journal.c
src/journal/test-catalog.c
src/journal/test-compress-benchmark.c
src/journal/test-compress.c
src/journal/test-journal-send.c
src/journal/test-journal-stream.c
src/journal/test-journal.c
src/kernel-install/meson.build
src/libsystemd-network/dhcp-identifier.c
src/libsystemd-network/dhcp-identifier.h
src/libsystemd-network/dhcp-internal.h
src/libsystemd-network/dhcp-lease-internal.h
src/libsystemd-network/dhcp-server-internal.h
src/libsystemd-network/dhcp6-internal.h
src/libsystemd-network/dhcp6-network.c
src/libsystemd-network/dhcp6-option.c
src/libsystemd-network/dhcp6-protocol.h
src/libsystemd-network/icmp6-util.c
src/libsystemd-network/icmp6-util.h
src/libsystemd-network/lldp-neighbor.c
src/libsystemd-network/lldp-neighbor.h
src/libsystemd-network/network-internal.c
src/libsystemd-network/network-internal.h
src/libsystemd-network/sd-dhcp-client.c
src/libsystemd-network/sd-dhcp-lease.c
src/libsystemd-network/sd-dhcp-server.c
src/libsystemd-network/sd-dhcp6-client.c
src/libsystemd-network/sd-dhcp6-lease.c
src/libsystemd-network/sd-ipv4ll.c
src/libsystemd-network/sd-radv.c
src/libsystemd-network/test-dhcp-client.c
src/libsystemd-network/test-dhcp-option.c
src/libsystemd-network/test-dhcp-server.c
src/libsystemd-network/test-dhcp6-client.c
src/libsystemd-network/test-ipv4ll.c
src/libsystemd-network/test-ndisc-ra.c
src/libsystemd-network/test-ndisc-rs.c
src/libsystemd/libsystemd.sym
src/libsystemd/sd-bus/bus-common-errors.c
src/libsystemd/sd-bus/bus-common-errors.h
src/libsystemd/sd-bus/bus-convenience.c
src/libsystemd/sd-bus/bus-creds.c
src/libsystemd/sd-bus/bus-dump.c
src/libsystemd/sd-bus/bus-internal.c
src/libsystemd/sd-bus/bus-internal.h
src/libsystemd/sd-bus/bus-introspect.c
src/libsystemd/sd-bus/bus-introspect.h
src/libsystemd/sd-bus/bus-message.c
src/libsystemd/sd-bus/bus-message.h
src/libsystemd/sd-bus/bus-objects.c
src/libsystemd/sd-bus/bus-protocol.h
src/libsystemd/sd-bus/bus-socket.c
src/libsystemd/sd-bus/bus-track.c
src/libsystemd/sd-bus/bus-type.c
src/libsystemd/sd-bus/sd-bus.c
src/libsystemd/sd-bus/test-bus-introspect.c
src/libsystemd/sd-daemon/sd-daemon.c
src/libsystemd/sd-device/device-enumerator.c
src/libsystemd/sd-device/device-internal.h
src/libsystemd/sd-device/device-monitor.c
src/libsystemd/sd-device/device-private.c
src/libsystemd/sd-device/device-util.h
src/libsystemd/sd-device/sd-device.c
src/libsystemd/sd-event/sd-event.c
src/libsystemd/sd-login/sd-login.c
src/libsystemd/sd-login/test-login.c
src/libsystemd/sd-netlink/netlink-message.c
src/libsystemd/sd-netlink/netlink-socket.c
src/libsystemd/sd-netlink/netlink-types.c
src/libsystemd/sd-netlink/netlink-types.h
src/libsystemd/sd-netlink/netlink-util.c
src/libsystemd/sd-netlink/netlink-util.h
src/libsystemd/sd-netlink/rtnl-message.c
src/libsystemd/sd-netlink/sd-netlink.c
src/libsystemd/sd-network/sd-network.c
src/libsystemd/sd-path/sd-path.c
src/libudev/libudev-monitor.c
src/libudev/libudev-util.c
src/libudev/libudev.c
src/locale/keymap-util.c
src/locale/localectl.c
src/locale/localed.c
src/locale/meson.build
src/login/71-seat.rules.in
src/login/inhibit.c
src/login/loginctl.c
src/login/logind-acl.c
src/login/logind-brightness.c
src/login/logind-core.c
src/login/logind-dbus.c
src/login/logind-dbus.h
src/login/logind-gperf.gperf
src/login/logind-seat-dbus.c
src/login/logind-seat-dbus.h
src/login/logind-seat.c
src/login/logind-seat.h
src/login/logind-session-dbus.c
src/login/logind-session-dbus.h
src/login/logind-session.c
src/login/logind-session.h
src/login/logind-user-dbus.c
src/login/logind-user-dbus.h
src/login/logind.c
src/login/logind.conf.in
src/login/logind.h
src/login/org.freedesktop.login1.conf
src/login/pam_systemd.c
src/login/user-runtime-dir.c
src/machine/image-dbus.c
src/machine/image-dbus.h
src/machine/machine-dbus.c
src/machine/machine-dbus.h
src/machine/machine.c
src/machine/machine.h
src/machine/machinectl.c
src/machine/machined-core.c
src/machine/machined-dbus.c
src/machine/machined-varlink.c [new file with mode: 0644]
src/machine/machined-varlink.h [new file with mode: 0644]
src/machine/machined.c
src/machine/machined.h
src/machine/meson.build
src/mount/mount-tool.c
src/network/meson.build
src/network/netdev/bond.c
src/network/netdev/bond.h
src/network/netdev/bridge.c
src/network/netdev/bridge.h
src/network/netdev/fou-tunnel.c
src/network/netdev/geneve.c
src/network/netdev/ipvlan.c
src/network/netdev/ipvlan.h
src/network/netdev/l2tp-tunnel.c
src/network/netdev/macsec.c
src/network/netdev/macvlan.c
src/network/netdev/macvlan.h
src/network/netdev/netdev-gperf.gperf
src/network/netdev/netdev.c
src/network/netdev/tunnel.c
src/network/netdev/vxlan.c
src/network/netdev/wireguard.c
src/network/networkctl.c
src/network/networkd-address-label.c
src/network/networkd-address.c
src/network/networkd-address.h
src/network/networkd-brvlan.c
src/network/networkd-can.c
src/network/networkd-can.h
src/network/networkd-conf.c
src/network/networkd-dhcp-common.c
src/network/networkd-dhcp-common.h
src/network/networkd-dhcp-server-bus.c [new file with mode: 0644]
src/network/networkd-dhcp-server-bus.h [new file with mode: 0644]
src/network/networkd-dhcp-server.c
src/network/networkd-dhcp-server.h
src/network/networkd-dhcp4.c
src/network/networkd-dhcp4.h
src/network/networkd-dhcp6.c
src/network/networkd-dhcp6.h
src/network/networkd-fdb.c
src/network/networkd-gperf.gperf
src/network/networkd-ipv4ll.c
src/network/networkd-ipv6-proxy-ndp.c
src/network/networkd-link-bus.c
src/network/networkd-link-bus.h
src/network/networkd-link.c
src/network/networkd-link.h
src/network/networkd-lldp-tx.c
src/network/networkd-lldp-tx.h
src/network/networkd-manager-bus.c
src/network/networkd-manager.c
src/network/networkd-manager.h
src/network/networkd-ndisc.c
src/network/networkd-ndisc.h
src/network/networkd-neighbor.c
src/network/networkd-network-gperf.gperf
src/network/networkd-network.c
src/network/networkd-network.h
src/network/networkd-nexthop.c
src/network/networkd-radv.c
src/network/networkd-radv.h
src/network/networkd-route.c
src/network/networkd-route.h
src/network/networkd-routing-policy-rule.c
src/network/networkd-sriov.c [new file with mode: 0644]
src/network/networkd-sriov.h [new file with mode: 0644]
src/network/networkd-util.c
src/network/networkd-util.h
src/network/networkd.c
src/network/networkd.conf
src/network/org.freedesktop.network1.policy
src/network/tc/cake.c [new file with mode: 0644]
src/network/tc/cake.h [new file with mode: 0644]
src/network/tc/codel.c
src/network/tc/drr.c [new file with mode: 0644]
src/network/tc/drr.h [new file with mode: 0644]
src/network/tc/ets.c [new file with mode: 0644]
src/network/tc/ets.h [new file with mode: 0644]
src/network/tc/fifo.c [new file with mode: 0644]
src/network/tc/fifo.h [new file with mode: 0644]
src/network/tc/fq-codel.c
src/network/tc/fq.c
src/network/tc/gred.c [new file with mode: 0644]
src/network/tc/gred.h [new file with mode: 0644]
src/network/tc/hhf.c [new file with mode: 0644]
src/network/tc/hhf.h [new file with mode: 0644]
src/network/tc/htb.c [new file with mode: 0644]
src/network/tc/htb.h [new file with mode: 0644]
src/network/tc/netem.c
src/network/tc/pie.c [new file with mode: 0644]
src/network/tc/pie.h [new file with mode: 0644]
src/network/tc/qdisc.c
src/network/tc/qdisc.h
src/network/tc/qfq.c [new file with mode: 0644]
src/network/tc/qfq.h [new file with mode: 0644]
src/network/tc/sfb.c [new file with mode: 0644]
src/network/tc/sfb.h [new file with mode: 0644]
src/network/tc/sfq.c
src/network/tc/tbf.c
src/network/tc/tbf.h
src/network/tc/tc-util.c
src/network/tc/tc-util.h
src/network/tc/tc.c [new file with mode: 0644]
src/network/tc/tc.h [new file with mode: 0644]
src/network/tc/tclass.c [new file with mode: 0644]
src/network/tc/tclass.h [new file with mode: 0644]
src/network/tc/teql.c
src/network/test-routing-policy-rule.c
src/network/wait-online/manager.c
src/network/wait-online/wait-online.c
src/notify/notify.c
src/nspawn/nspawn-cgroup.c
src/nspawn/nspawn-def.h
src/nspawn/nspawn-expose-ports.c
src/nspawn/nspawn-mount.c
src/nspawn/nspawn-mount.h
src/nspawn/nspawn-oci.c
src/nspawn/nspawn-patch-uid.c
src/nspawn/nspawn-register.c
src/nspawn/nspawn-seccomp.c
src/nspawn/nspawn-seccomp.h
src/nspawn/nspawn-settings.c
src/nspawn/nspawn-settings.h
src/nspawn/nspawn-setuid.c
src/nspawn/nspawn.c
src/nss-myhostname/nss-myhostname.c
src/nss-mymachines/nss-mymachines.c
src/nss-resolve/nss-resolve.c
src/nss-systemd/nss-systemd.c
src/nss-systemd/nss-systemd.h [new file with mode: 0644]
src/nss-systemd/nss-systemd.sym
src/nss-systemd/userdb-glue.c
src/partition/growfs.c
src/partition/makefs.c
src/partition/meson.build
src/partition/repart.c
src/partition/test-repart.sh [new file with mode: 0755]
src/path/path.c
src/portable/portable.c
src/portable/portablectl.c
src/portable/portabled-image-bus.c
src/portable/portabled.c
src/pstore/pstore.c
src/random-seed/random-seed.c
src/resolve/meson.build
src/resolve/resolvectl.c
src/resolve/resolved-bus.c
src/resolve/resolved-bus.h
src/resolve/resolved-conf.c
src/resolve/resolved-conf.h
src/resolve/resolved-dns-dnssec.c
src/resolve/resolved-dns-dnssec.h
src/resolve/resolved-dns-packet.c
src/resolve/resolved-dns-query.c
src/resolve/resolved-dns-query.h
src/resolve/resolved-dns-rr.c
src/resolve/resolved-dns-rr.h
src/resolve/resolved-dns-scope.c
src/resolve/resolved-dns-scope.h
src/resolve/resolved-dns-search-domain.c
src/resolve/resolved-dns-search-domain.h
src/resolve/resolved-dns-server.c
src/resolve/resolved-dns-server.h
src/resolve/resolved-dns-stream.c
src/resolve/resolved-dns-stream.h
src/resolve/resolved-dns-stub.c
src/resolve/resolved-dns-transaction.c
src/resolve/resolved-dns-transaction.h
src/resolve/resolved-dns-trust-anchor.c
src/resolve/resolved-dns-zone.c
src/resolve/resolved-dnssd-bus.c
src/resolve/resolved-dnssd-bus.h
src/resolve/resolved-dnssd.c
src/resolve/resolved-dnstls-gnutls.c
src/resolve/resolved-dnstls-openssl.c
src/resolve/resolved-dnstls.h
src/resolve/resolved-etc-hosts.c
src/resolve/resolved-gperf.gperf
src/resolve/resolved-link-bus.c
src/resolve/resolved-link-bus.h
src/resolve/resolved-link.c
src/resolve/resolved-link.h
src/resolve/resolved-manager.c
src/resolve/resolved-manager.h
src/resolve/resolved-resolv-conf.c
src/resolve/resolved-util.c [deleted file]
src/resolve/resolved-util.h [deleted file]
src/resolve/resolved.c
src/resolve/resolved.conf.in
src/resolve/test-dns-packet.c
src/resolve/test-dnssec.c
src/resolve/test-resolved-etc-hosts.c
src/resolve/test-resolved-util.c [deleted file]
src/run/run.c
src/shared/acl-util.c
src/shared/acpi-fpdt.c
src/shared/ask-password-api.c
src/shared/barrier.c
src/shared/bond-util.c [new file with mode: 0644]
src/shared/bond-util.h [new file with mode: 0644]
src/shared/bootspec.c
src/shared/bootspec.h
src/shared/bridge-util.c [new file with mode: 0644]
src/shared/bridge-util.h [new file with mode: 0644]
src/shared/bus-get-properties.c [new file with mode: 0644]
src/shared/bus-get-properties.h [new file with mode: 0644]
src/shared/bus-locator.c [new file with mode: 0644]
src/shared/bus-locator.h [new file with mode: 0644]
src/shared/bus-log-control-api.c [new file with mode: 0644]
src/shared/bus-log-control-api.h [new file with mode: 0644]
src/shared/bus-map-properties.c [new file with mode: 0644]
src/shared/bus-map-properties.h [new file with mode: 0644]
src/shared/bus-message-util.c [new file with mode: 0644]
src/shared/bus-message-util.h [new file with mode: 0644]
src/shared/bus-object.c [new file with mode: 0644]
src/shared/bus-object.h [new file with mode: 0644]
src/shared/bus-print-properties.c [new file with mode: 0644]
src/shared/bus-print-properties.h [new file with mode: 0644]
src/shared/bus-unit-util.c
src/shared/bus-util.c
src/shared/bus-util.h
src/shared/bus-wait-for-jobs.c
src/shared/bus-wait-for-units.c
src/shared/calendarspec.c
src/shared/calendarspec.h
src/shared/cgroup-show.c
src/shared/chown-recursive.c
src/shared/condition.c
src/shared/condition.h
src/shared/conf-parser.c
src/shared/conf-parser.h
src/shared/coredump-util.c [new file with mode: 0644]
src/shared/coredump-util.h [new file with mode: 0644]
src/shared/dev-setup.c
src/shared/dissect-image.c
src/shared/dissect-image.h
src/shared/dm-util.c
src/shared/dm-util.h
src/shared/dropin.c
src/shared/dropin.h
src/shared/efi-loader.c
src/shared/efi-loader.h
src/shared/ethtool-util.c
src/shared/ethtool-util.h
src/shared/format-table.c
src/shared/format-table.h
src/shared/fstab-util.c
src/shared/geneve-util.c [new file with mode: 0644]
src/shared/geneve-util.h [new file with mode: 0644]
src/shared/group-record-nss.c
src/shared/group-record-nss.h
src/shared/install-printf.c
src/shared/install.c
src/shared/install.h
src/shared/ipvlan-util.c [new file with mode: 0644]
src/shared/ipvlan-util.h [new file with mode: 0644]
src/shared/json.c
src/shared/json.h
src/shared/linux/nl80211.h
src/shared/logs-show.c
src/shared/loop-util.c
src/shared/machine-image.c
src/shared/macvlan-util.c [new file with mode: 0644]
src/shared/macvlan-util.h [new file with mode: 0644]
src/shared/meson.build
src/shared/module-util.c
src/shared/mount-util.c
src/shared/mount-util.h
src/shared/nscd-flush.c
src/shared/offline-passwd.c [new file with mode: 0644]
src/shared/offline-passwd.h [new file with mode: 0644]
src/shared/os-util.c
src/shared/os-util.h
src/shared/path-lookup.c [deleted file]
src/shared/path-lookup.h [deleted file]
src/shared/pkcs11-util.c
src/shared/pkcs11-util.h
src/shared/pretty-print.c
src/shared/pretty-print.h
src/shared/reboot-util.c
src/shared/resize-fs.c
src/shared/resolve-util.h
src/shared/seccomp-util.c
src/shared/seccomp-util.h
src/shared/service-util.c [new file with mode: 0644]
src/shared/service-util.h [new file with mode: 0644]
src/shared/sleep-config.c
src/shared/socket-netlink.c
src/shared/socket-netlink.h
src/shared/specifier.c
src/shared/specifier.h
src/shared/sysctl-util.h
src/shared/tests.c
src/shared/tests.h
src/shared/udev-util.c
src/shared/udev-util.h
src/shared/unit-file.c
src/shared/unit-file.h
src/shared/user-record-nss.c
src/shared/user-record-nss.h
src/shared/user-record-show.c
src/shared/user-record.c
src/shared/user-record.h
src/shared/userdb.c
src/shared/userdb.h
src/shared/utmp-wtmp.c
src/shared/utmp-wtmp.h
src/shared/varlink.c
src/shared/verbs.c
src/shared/verbs.h
src/shared/watchdog.c
src/shared/watchdog.h
src/shared/wifi-util.c
src/shutdown/shutdown.c
src/sleep/sleep.c
src/socket-proxy/socket-proxyd.c
src/stdio-bridge/stdio-bridge.c
src/sysctl/sysctl.c
src/systemctl/systemctl.c
src/systemd/meson.build
src/systemd/sd-bus-vtable.h
src/systemd/sd-bus.h
src/systemd/sd-daemon.h
src/systemd/sd-dhcp-client.h
src/systemd/sd-dhcp-lease.h
src/systemd/sd-dhcp-server.h
src/systemd/sd-dhcp6-client.h
src/systemd/sd-dhcp6-lease.h
src/systemd/sd-dhcp6-option.h [new file with mode: 0644]
src/systemd/sd-journal.h
src/systemd/sd-lldp.h
src/systemd/sd-login.h
src/systemd/sd-messages.h
src/systemd/sd-ndisc.h
src/systemd/sd-netlink.h
src/systemd/sd-network.h
src/systemd/sd-path.h
src/systemd/sd-radv.h
src/sysusers/sysusers.c
src/sysv-generator/sysv-generator.c
src/test/meson.build
src/test/test-bpf-devices.c
src/test/test-bpf-firewall.c
src/test/test-cap-list.c
src/test/test-capability.c
src/test/test-cgroup-mask.c
src/test/test-cgroup-setup.c
src/test/test-cgroup-unit-default.c
src/test/test-cgroup-util.c
src/test/test-cgroup.c
src/test/test-chase-symlinks.c
src/test/test-clock.c
src/test/test-condition.c
src/test/test-conf-parser.c
src/test/test-copy.c
src/test/test-coredump-util.c [new file with mode: 0644]
src/test/test-dev-setup.c
src/test/test-dissect-image.c
src/test/test-engine.c
src/test/test-escape.c
src/test/test-exec-util.c
src/test/test-execute.c
src/test/test-fileio.c
src/test/test-fs-util.c
src/test/test-hashmap-plain.c
src/test/test-hashmap.c
src/test/test-hostname-util.c
src/test/test-install-root.c
src/test/test-journal-importer.c
src/test/test-json.c
src/test/test-load-fragment.c
src/test/test-locale-util.c
src/test/test-namespace.c
src/test/test-ns.c
src/test/test-offline-passwd.c [new file with mode: 0644]
src/test/test-ordered-set.c
src/test/test-path-lookup.c
src/test/test-path.c
src/test/test-prioq.c
src/test/test-proc-cmdline.c
src/test/test-process-util.c
src/test/test-procfs-util.c
src/test/test-random-util.c
src/test/test-sched-prio.c
src/test/test-sd-hwdb.c
src/test/test-sd-path.c [new file with mode: 0644]
src/test/test-seccomp.c
src/test/test-set.c
src/test/test-sizeof.c
src/test/test-sleep.c
src/test/test-socket-util.c
src/test/test-specifier.c
src/test/test-strv.c
src/test/test-terminal-util.c
src/test/test-time-util.c
src/test/test-udev.c
src/test/test-umount.c
src/test/test-unit-name.c
src/test/test-util.c
src/test/test-watch-pid.c
src/test/test-xdg-autostart.c [new file with mode: 0644]
src/timedate/timedatectl.c
src/timedate/timedated.c
src/timesync/timesyncd-bus.c
src/timesync/timesyncd-conf.c
src/timesync/timesyncd-manager.c
src/timesync/timesyncd-manager.h
src/timesync/timesyncd.c
src/tmpfiles/meson.build [new file with mode: 0644]
src/tmpfiles/tmpfiles.c
src/tty-ask-password-agent/tty-ask-password-agent.c
src/udev/ata_id/ata_id.c
src/udev/meson.build
src/udev/net/link-config-gperf.gperf
src/udev/net/link-config.c
src/udev/net/link-config.h
src/udev/scsi_id/scsi_id.c
src/udev/udev-builtin-hwdb.c
src/udev/udev-builtin-input_id.c
src/udev/udev-builtin-net_id.c
src/udev/udev-builtin-path_id.c
src/udev/udev-ctrl.c
src/udev/udev-event.c
src/udev/udev-event.h
src/udev/udev-node.c
src/udev/udev-rules.c
src/udev/udev-rules.h
src/udev/udev.conf
src/udev/udev.pc.in
src/udev/udevadm-info.c
src/udev/udevadm-monitor.c
src/udev/udevadm-settle.c
src/udev/udevadm-test.c
src/udev/udevadm-trigger.c
src/udev/udevadm.c
src/udev/udevd.c
src/udev/udevd.h [new file with mode: 0644]
src/update-done/update-done.c
src/update-utmp/update-utmp.c
src/user-sessions/user-sessions.c
src/userdb/userdbctl.c
src/userdb/userdbd.c
src/userdb/userwork.c
src/veritysetup/veritysetup.c
src/version/version.h.in
src/volatile-root/volatile-root.c
src/xdg-autostart-generator/xdg-autostart-condition.c [new file with mode: 0644]
src/xdg-autostart-generator/xdg-autostart-generator.c [new file with mode: 0644]
src/xdg-autostart-generator/xdg-autostart-service.c [new file with mode: 0644]
src/xdg-autostart-generator/xdg-autostart-service.h [new file with mode: 0644]
sysctl.d/50-coredump.conf.in
test/README.testsuite
test/TEST-01-BASIC/Makefile
test/TEST-01-BASIC/test.sh
test/TEST-02-CRYPTSETUP/test.sh
test/TEST-03-JOBS/test-jobs.sh [deleted file]
test/TEST-03-JOBS/test.sh
test/TEST-04-JOURNAL/test-journal.sh [deleted file]
test/TEST-04-JOURNAL/test.sh
test/TEST-05-RLIMITS/test-rlimits.sh [deleted file]
test/TEST-05-RLIMITS/test.sh
test/TEST-06-SELINUX/test-selinux-checks.sh [deleted file]
test/TEST-06-SELINUX/test.sh
test/TEST-07-ISSUE-1981/test-segfault.sh [deleted file]
test/TEST-07-ISSUE-1981/test.sh
test/TEST-08-ISSUE-2730/test.sh
test/TEST-09-ISSUE-2691/test.sh
test/TEST-10-ISSUE-2467/test.sh
test/TEST-11-ISSUE-3166/test.sh
test/TEST-12-ISSUE-3171/test.sh
test/TEST-13-NSPAWN-SMOKE/Makefile [changed from file to symlink]
test/TEST-13-NSPAWN-SMOKE/create-busybox-container [deleted file]
test/TEST-13-NSPAWN-SMOKE/test.sh
test/TEST-14-MACHINE-ID/test.sh
test/TEST-15-DROPIN/test-dropin.sh [deleted file]
test/TEST-15-DROPIN/test.sh
test/TEST-15-DROPIN/testsuite.service [deleted file]
test/TEST-16-EXTEND-TIMEOUT/assess.sh [deleted file]
test/TEST-16-EXTEND-TIMEOUT/extend_timeout_test_service.sh [deleted file]
test/TEST-16-EXTEND-TIMEOUT/test.sh
test/TEST-16-EXTEND-TIMEOUT/testsuite-fail-runtime.service [deleted file]
test/TEST-16-EXTEND-TIMEOUT/testsuite-fail-start.service [deleted file]
test/TEST-16-EXTEND-TIMEOUT/testsuite-fail-stop.service [deleted file]
test/TEST-16-EXTEND-TIMEOUT/testsuite-success-all.service [deleted file]
test/TEST-16-EXTEND-TIMEOUT/testsuite-success-runtime.service [deleted file]
test/TEST-16-EXTEND-TIMEOUT/testsuite-success-start.service [deleted file]
test/TEST-16-EXTEND-TIMEOUT/testsuite-success-stop.service [deleted file]
test/TEST-16-EXTEND-TIMEOUT/testsuite.service [deleted file]
test/TEST-17-UDEV-WANTS/test.sh
test/TEST-17-UDEV-WANTS/testsuite.sh [deleted file]
test/TEST-18-FAILUREACTION/test.sh
test/TEST-18-FAILUREACTION/testsuite.sh [deleted file]
test/TEST-19-DELEGATE/test.sh
test/TEST-19-DELEGATE/testsuite.sh [deleted file]
test/TEST-20-MAINPIDGAMES/test.sh
test/TEST-20-MAINPIDGAMES/testsuite.sh [deleted file]
test/TEST-21-SYSUSERS/test.sh
test/TEST-22-TMPFILES/run-tmpfiles-tests.sh [deleted file]
test/TEST-22-TMPFILES/test-01.sh [deleted file]
test/TEST-22-TMPFILES/test-02.sh [deleted file]
test/TEST-22-TMPFILES/test-03.sh [deleted file]
test/TEST-22-TMPFILES/test-04.sh [deleted file]
test/TEST-22-TMPFILES/test-05.sh [deleted file]
test/TEST-22-TMPFILES/test-06.sh [deleted file]
test/TEST-22-TMPFILES/test-07.sh [deleted file]
test/TEST-22-TMPFILES/test-08.sh [deleted file]
test/TEST-22-TMPFILES/test-09.sh [deleted file]
test/TEST-22-TMPFILES/test.sh
test/TEST-22-TMPFILES/testsuite.service [deleted file]
test/TEST-23-TYPE-EXEC/test.sh
test/TEST-23-TYPE-EXEC/testsuite.sh [deleted file]
test/TEST-24-UNIT-TESTS/test.sh
test/TEST-24-UNIT-TESTS/testsuite.sh [deleted file]
test/TEST-25-IMPORT/test.sh
test/TEST-25-IMPORT/testsuite.sh [deleted file]
test/TEST-26-SETENV/test.sh
test/TEST-26-SETENV/testsuite.sh [deleted file]
test/TEST-27-STDOUTFILE/test.sh
test/TEST-27-STDOUTFILE/testsuite.sh [deleted file]
test/TEST-28-PERCENTJ-WANTEDBY/test.sh
test/TEST-29-UDEV-ID_RENAMING/test.sh
test/TEST-29-UDEV-ID_RENAMING/testsuite.sh [deleted file]
test/TEST-30-ONCLOCKCHANGE/test.sh
test/TEST-30-ONCLOCKCHANGE/testsuite.sh [deleted file]
test/TEST-31-DEVICE-ENUMERATION/test.sh
test/TEST-31-DEVICE-ENUMERATION/testsuite.sh [deleted file]
test/TEST-32-OOMPOLICY/test.sh
test/TEST-32-OOMPOLICY/testsuite.sh [deleted file]
test/TEST-33-CLEAN-UNIT/test.sh
test/TEST-33-CLEAN-UNIT/testsuite.sh [deleted file]
test/TEST-34-DYNAMICUSERMIGRATE/test.sh
test/TEST-34-DYNAMICUSERMIGRATE/testsuite.sh [deleted file]
test/TEST-35-NETWORK-GENERATOR/Makefile [deleted symlink]
test/TEST-35-NETWORK-GENERATOR/test-01-dhcp.expected/91-default.network [deleted file]
test/TEST-35-NETWORK-GENERATOR/test-01-dhcp.input [deleted file]
test/TEST-35-NETWORK-GENERATOR/test-02-bridge.expected/90-bridge99.netdev [deleted file]
test/TEST-35-NETWORK-GENERATOR/test-02-bridge.expected/90-bridge99.network [deleted file]
test/TEST-35-NETWORK-GENERATOR/test-02-bridge.expected/90-eth0.network [deleted file]
test/TEST-35-NETWORK-GENERATOR/test-02-bridge.expected/90-eth1.network [deleted file]
test/TEST-35-NETWORK-GENERATOR/test-02-bridge.input [deleted file]
test/TEST-35-NETWORK-GENERATOR/test-03-issue-14319.expected/90-enp3s0.network [deleted file]
test/TEST-35-NETWORK-GENERATOR/test-03-issue-14319.input [deleted file]
test/TEST-35-NETWORK-GENERATOR/test.sh [deleted file]
test/TEST-36-NUMAPOLICY/test.sh
test/TEST-36-NUMAPOLICY/testsuite.sh [deleted file]
test/TEST-37-RUNTIMEDIRECTORYPRESERVE/test.sh
test/TEST-37-RUNTIMEDIRECTORYPRESERVE/testsuite.sh [deleted file]
test/TEST-38-FREEZER/Makefile [new symlink]
test/TEST-38-FREEZER/test.sh [new file with mode: 0755]
test/TEST-39-EXECRELOAD/test.sh
test/TEST-39-EXECRELOAD/testsuite.sh [deleted file]
test/TEST-40-EXEC-COMMAND-EX/test.sh
test/TEST-40-EXEC-COMMAND-EX/testsuite.sh [deleted file]
test/TEST-41-ONESHOT-RESTART/test.sh
test/TEST-41-ONESHOT-RESTART/testsuite.sh [deleted file]
test/TEST-42-EXECSTOPPOST/test.sh
test/TEST-42-EXECSTOPPOST/testsuite.sh [deleted file]
test/TEST-43-PRIVATEUSER-UNPRIV/test.sh
test/TEST-43-PRIVATEUSER-UNPRIV/testsuite.sh [deleted file]
test/TEST-44-LOG-NAMESPACE/test.sh
test/TEST-44-LOG-NAMESPACE/testsuite.sh [deleted file]
test/TEST-45-REPART/Makefile [deleted symlink]
test/TEST-45-REPART/test.sh [deleted file]
test/TEST-45-REPART/testsuite.sh [deleted file]
test/TEST-46-HOMED/test.sh
test/TEST-46-HOMED/testsuite.sh [deleted file]
test/TEST-47-ISSUE-14566/repro.sh [deleted file]
test/TEST-47-ISSUE-14566/test.sh
test/TEST-47-ISSUE-14566/testsuite.sh [deleted file]
test/TEST-48-START-STOP-NO-RELOAD/Makefile [new symlink]
test/TEST-48-START-STOP-NO-RELOAD/test.sh [new file with mode: 0755]
test/TEST-49-UDEV-EVENT-TIMEOUT/Makefile [new symlink]
test/TEST-49-UDEV-EVENT-TIMEOUT/test.sh [new file with mode: 0755]
test/TEST-50-DISSECT/Makefile [new symlink]
test/TEST-50-DISSECT/test.sh [new file with mode: 0755]
test/TEST-51-ISSUE-16115/Makefile [new symlink]
test/TEST-51-ISSUE-16115/test.sh [new file with mode: 0755]
test/TEST-52-HONORFIRSTSHUTDOWN/Makefile [new file with mode: 0644]
test/TEST-52-HONORFIRSTSHUTDOWN/fini.sh [new file with mode: 0755]
test/TEST-52-HONORFIRSTSHUTDOWN/test.sh [new file with mode: 0755]
test/TEST-53-ISSUE-16347/Makefile [new symlink]
test/TEST-53-ISSUE-16347/test.sh [new file with mode: 0755]
test/a-conj.service [deleted file]
test/a.service [deleted file]
test/b.service [deleted file]
test/basic.target [deleted symlink]
test/c.service [deleted file]
test/create-busybox-container [new file with mode: 0755]
test/d.service [deleted file]
test/daughter.service [deleted file]
test/dml-discard-empty.service [deleted file]
test/dml-discard-set-ml.service [deleted file]
test/dml-discard.slice [deleted file]
test/dml-override-empty.service [deleted file]
test/dml-override.slice [deleted file]
test/dml-passthrough-empty.service [deleted file]
test/dml-passthrough-set-dml.service [deleted file]
test/dml-passthrough-set-ml.service [deleted file]
test/dml-passthrough.slice [deleted file]
test/dml.slice [deleted file]
test/e.service [deleted file]
test/end.service [deleted file]
test/f.service [deleted file]
test/fuzz/fuzz-journal-remote/oss-fuzz-21122 [new file with mode: 0644]
test/fuzz/fuzz-json/github-15907 [new file with mode: 0644]
test/fuzz/fuzz-link-parser/directives.link
test/fuzz/fuzz-netdev-parser/directives.netdev
test/fuzz/fuzz-netdev-parser/github-15968 [new file with mode: 0644]
test/fuzz/fuzz-netdev-parser/wireguard-duplicated-endpoint [new file with mode: 0644]
test/fuzz/fuzz-network-parser/directives.network
test/fuzz/fuzz-network-parser/dns-trust-anchor-duplicate.network [new file with mode: 0644]
test/fuzz/fuzz-network-parser/github-15885 [new file with mode: 0644]
test/fuzz/fuzz-network-parser/github-15951 [new file with mode: 0644]
test/fuzz/fuzz-network-parser/oss-fuzz-15678
test/fuzz/fuzz-network-parser/oss-fuzz-23895 [new file with mode: 0644]
test/fuzz/fuzz-network-parser/oss-fuzz-23950 [new file with mode: 0644]
test/fuzz/fuzz-udev-rules/line-too-long [new file with mode: 0644]
test/fuzz/fuzz-unit-file/directives.service
test/fuzz/fuzz-xdg-desktop/full.desktop [new file with mode: 0644]
test/fuzz/fuzz-xdg-desktop/org.gnome.SettingsDaemon.Power.desktop [new file with mode: 0644]
test/fuzz/fuzz-xdg-desktop/oss-fuzz-22812 [new file with mode: 0644]
test/fuzz/fuzz-xdg-desktop/valid.desktop [new file with mode: 0644]
test/fuzz/meson.build
test/g.service [deleted file]
test/grandchild.service [deleted file]
test/h.service [deleted file]
test/hello-after-sleep.target [deleted file]
test/hello.service [deleted file]
test/i.service [deleted file]
test/loopy.service [deleted file]
test/loopy.service.d/compat.conf [deleted file]
test/loopy2.service [deleted symlink]
test/loopy3.service [deleted file]
test/loopy4.service [deleted symlink]
test/meson.build
test/networkd-test.py
test/nomem.slice [deleted file]
test/nomemleaf.service [deleted file]
test/parent-deep.slice [deleted file]
test/parent.slice [deleted file]
test/run-integration-tests.sh
test/sched_idle_bad.service [deleted file]
test/sched_idle_ok.service [deleted file]
test/sched_rr_bad.service [deleted file]
test/sched_rr_change.service [deleted file]
test/sched_rr_ok.service [deleted file]
test/shutdown.target [deleted symlink]
test/sleep.service [deleted file]
test/sockets.target [deleted symlink]
test/son.service [deleted file]
test/sysinit.target [deleted symlink]
test/test-execute/exec-restrictnamespaces-mnt-blacklist.service [deleted file]
test/test-execute/exec-restrictnamespaces-mnt-deny-list.service [new file with mode: 0644]
test/test-execute/exec-specifier.service
test/test-execute/exec-specifier@.service
test/test-functions
test/test-network-generator-conversion.sh [new file with mode: 0755]
test/test-network-generator-conversion/test-01-dhcp.expected/91-default.network [new file with mode: 0644]
test/test-network-generator-conversion/test-01-dhcp.input [new file with mode: 0644]
test/test-network-generator-conversion/test-02-bridge.expected/90-bridge99.netdev [new file with mode: 0644]
test/test-network-generator-conversion/test-02-bridge.expected/90-bridge99.network [new file with mode: 0644]
test/test-network-generator-conversion/test-02-bridge.expected/90-eth0.network [new file with mode: 0644]
test/test-network-generator-conversion/test-02-bridge.expected/90-eth1.network [new file with mode: 0644]
test/test-network-generator-conversion/test-02-bridge.input [new file with mode: 0644]
test/test-network-generator-conversion/test-03-issue-14319.expected/90-enp3s0.network [new file with mode: 0644]
test/test-network-generator-conversion/test-03-issue-14319.input [new file with mode: 0644]
test/test-network/conf/23-bond199.network
test/test-network/conf/23-emit-lldp.network
test/test-network/conf/24-keep-configuration-static.network
test/test-network/conf/24-lldp.network
test/test-network/conf/24-search-domain.network
test/test-network/conf/25-address-link-section.network
test/test-network/conf/25-address-preferred-lifetime-zero.network
test/test-network/conf/25-fibrule-invert.network
test/test-network/conf/25-fibrule-port-range.network
test/test-network/conf/25-fibrule-uidrange.network
test/test-network/conf/25-gateway-next-static.network
test/test-network/conf/25-gateway-static.network
test/test-network/conf/25-ipv6-address-label-section.network
test/test-network/conf/25-netdevsim.netdev [deleted file]
test/test-network/conf/25-prefix-route-with-vrf.network [new file with mode: 0644]
test/test-network/conf/25-prefix-route-without-vrf.network [new file with mode: 0644]
test/test-network/conf/25-qdisc-cake.network [new file with mode: 0644]
test/test-network/conf/25-qdisc-clsact-and-htb.network [new file with mode: 0644]
test/test-network/conf/25-qdisc-clsact-root-compat.network [deleted file]
test/test-network/conf/25-qdisc-drr.network [new file with mode: 0644]
test/test-network/conf/25-qdisc-ets.network [new file with mode: 0644]
test/test-network/conf/25-qdisc-fq-codel.network [deleted file]
test/test-network/conf/25-qdisc-hhf.network [new file with mode: 0644]
test/test-network/conf/25-qdisc-ingress-root.network [deleted file]
test/test-network/conf/25-qdisc-netem-and-fqcodel.network [deleted file]
test/test-network/conf/25-qdisc-pie.network [new file with mode: 0644]
test/test-network/conf/25-qdisc-qfq.network [new file with mode: 0644]
test/test-network/conf/25-qdisc-tbf-and-sfq.network [deleted file]
test/test-network/conf/25-qdisc-teql.network [deleted file]
test/test-network/conf/25-route-vrf.network
test/test-network/conf/25-sriov.network [new file with mode: 0644]
test/test-network/conf/25-sysctl.network
test/test-network/conf/25-test1.network [new file with mode: 0644]
test/test-network/conf/25-test1.network.d/configure-without-carrier.conf [new file with mode: 0644]
test/test-network/conf/25-test1.network.d/ignore-carrier-loss-no.conf [new file with mode: 0644]
test/test-network/conf/25-vrf.network
test/test-network/conf/25-wireguard-no-peer.netdev [new file with mode: 0644]
test/test-network/conf/25-wireguard-no-peer.network [new file with mode: 0644]
test/test-network/conf/26-bridge-configure-without-carrier.network [new file with mode: 0644]
test/test-network/conf/6rd.network
test/test-network/conf/configure-without-carrier.network [deleted file]
test/test-network/conf/dhcp-client-ipv4-use-routes-no.network [deleted file]
test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network [new file with mode: 0644]
test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-dns-routes-False.conf [new file with mode: 0644]
test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-dns-routes-True.conf [new file with mode: 0644]
test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-gateway-False.conf [new file with mode: 0644]
test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-gateway-True.conf [new file with mode: 0644]
test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-routes-False.conf [new file with mode: 0644]
test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-routes-True.conf [new file with mode: 0644]
test/test-network/conf/erspan.network
test/test-network/conf/gretap.network
test/test-network/conf/gretun.network
test/test-network/conf/ip6gretap.network
test/test-network/conf/ip6gretun.network
test/test-network/conf/ip6tnl.network
test/test-network/conf/ipip.network
test/test-network/conf/ipv6-prefix-veth-token-static-multiple.network
test/test-network/conf/ipv6ra-prefix.network
test/test-network/conf/ipvlan.network
test/test-network/conf/ipvtap.network
test/test-network/conf/isatap.network
test/test-network/conf/macvlan.network
test/test-network/conf/macvtap.network
test/test-network/conf/routing-policy-rule-dummy98.network
test/test-network/conf/routing-policy-rule-test1.network
test/test-network/conf/sit.network
test/test-network/conf/state-file-tests.network
test/test-network/conf/vti.network
test/test-network/conf/vti6.network
test/test-network/systemd-networkd-tests.py
test/test-path/basic.target [changed from symlink to file mode: 0644]
test/test-path/path-changed.service [changed from symlink to file mode: 0644]
test/test-path/path-directorynotempty.service [changed from symlink to file mode: 0644]
test/test-path/path-exists.service [changed from symlink to file mode: 0644]
test/test-path/path-existsglob.service [changed from symlink to file mode: 0644]
test/test-path/path-makedirectory.service [changed from symlink to file mode: 0644]
test/test-path/path-modified.service [changed from symlink to file mode: 0644]
test/test-path/path-mycustomunit.service
test/test-path/paths.target [changed from symlink to file mode: 0644]
test/test-path/sysinit.target [changed from symlink to file mode: 0644]
test/test-resolve/com~20200417.pkts [new file with mode: 0644]
test/test-resolve/google.com.pkts [deleted file]
test/test-resolve/google.com~20160131.pkts [new file with mode: 0644]
test/test-resolve/google.com~20200417.pkts [new file with mode: 0644]
test/test-resolve/michigan.gov~20200417.pkts [new file with mode: 0644]
test/test-resolve/org~20200417.pkts [new file with mode: 0644]
test/test-resolve/vdwaa.nl~20200417.pkts [new file with mode: 0644]
test/testdata [new symlink]
test/testsuite-04.units/forever-print-hola.service [new file with mode: 0644]
test/testsuite-06.units/hola.service [new file with mode: 0644]
test/testsuite-06.units/load-systemd-test-module.service [new file with mode: 0644]
test/testsuite-08.units/-.mount [new file with mode: 0644]
test/testsuite-08.units/local-fs.target.wants/-.mount [new symlink]
test/testsuite-08.units/root.mount [new symlink]
test/testsuite-08.units/systemd-remount-fs.service [new file with mode: 0644]
test/testsuite-10.units/test10.service [new file with mode: 0644]
test/testsuite-10.units/test10.socket [new file with mode: 0644]
test/testsuite-11.units/fail-on-restart.service [new file with mode: 0644]
test/testsuite-16.units/extend-timeout.sh [new file with mode: 0755]
test/testsuite-16.units/fail-runtime.service [new file with mode: 0644]
test/testsuite-16.units/fail-start.service [new file with mode: 0644]
test/testsuite-16.units/fail-stop.service [new file with mode: 0644]
test/testsuite-16.units/success-all.service [new file with mode: 0644]
test/testsuite-16.units/success-runtime.service [new file with mode: 0644]
test/testsuite-16.units/success-start.service [new file with mode: 0644]
test/testsuite-16.units/success-stop.service [new file with mode: 0644]
test/testsuite-28.units/specifier-j-depends-wants.service [new file with mode: 0644]
test/testsuite-28.units/specifier-j-wants.service [new file with mode: 0644]
test/testsuite-28.units/testsuite-28-pre.service [new file with mode: 0644]
test/testsuite-30.units/systemd-timedated.service.d/watchdog.conf [new file with mode: 0644]
test/testsuite-52.units/testsuite-52.service [new file with mode: 0755]
test/testsuite-52.units/testsuite-52.sh [new file with mode: 0755]
test/testsuite.target [deleted file]
test/timers.target [deleted symlink]
test/unit-.service.d/10-override.conf [deleted file]
test/unit-with-.service.d/20-override.conf [deleted file]
test/unit-with-multiple-.service.d/20-override.conf [deleted file]
test/unit-with-multiple-.service.d/30-override.conf [deleted file]
test/unit-with-multiple-dashes.service [deleted file]
test/unit-with-multiple-dashes.service.d/10-override.conf [deleted file]
test/units/a-conj.service [new file with mode: 0644]
test/units/a.service [new file with mode: 0644]
test/units/autorelabel.service [new file with mode: 0644]
test/units/b.service [new file with mode: 0644]
test/units/basic.target [new file with mode: 0644]
test/units/c.service [new file with mode: 0644]
test/units/d.service [new file with mode: 0644]
test/units/daughter.service [new file with mode: 0644]
test/units/dml-discard-empty.service [new file with mode: 0644]
test/units/dml-discard-set-ml.service [new file with mode: 0644]
test/units/dml-discard.slice [new file with mode: 0644]
test/units/dml-override-empty.service [new file with mode: 0644]
test/units/dml-override.slice [new file with mode: 0644]
test/units/dml-passthrough-empty.service [new file with mode: 0644]
test/units/dml-passthrough-set-dml.service [new file with mode: 0644]
test/units/dml-passthrough-set-ml.service [new file with mode: 0644]
test/units/dml-passthrough.slice [new file with mode: 0644]
test/units/dml.slice [new file with mode: 0644]
test/units/e.service [new file with mode: 0644]
test/units/end.service [new file with mode: 0644]
test/units/f.service [new file with mode: 0644]
test/units/g.service [new file with mode: 0644]
test/units/grandchild.service [new file with mode: 0644]
test/units/h.service [new file with mode: 0644]
test/units/hello-after-sleep.target [new file with mode: 0644]
test/units/hello.service [new file with mode: 0644]
test/units/i.service [new file with mode: 0644]
test/units/loopy.service [new file with mode: 0644]
test/units/loopy.service.d/compat.conf [new file with mode: 0644]
test/units/loopy2.service [new file with mode: 0644]
test/units/loopy3.service [new file with mode: 0644]
test/units/loopy4.service [new file with mode: 0644]
test/units/nomem.slice [new file with mode: 0644]
test/units/nomemleaf.service [new file with mode: 0644]
test/units/parent-deep.slice [new file with mode: 0644]
test/units/parent.slice [new file with mode: 0644]
test/units/sched_idle_bad.service [new file with mode: 0644]
test/units/sched_idle_ok.service [new file with mode: 0644]
test/units/sched_rr_bad.service [new file with mode: 0644]
test/units/sched_rr_change.service [new file with mode: 0644]
test/units/sched_rr_ok.service [new file with mode: 0644]
test/units/shutdown.target [new file with mode: 0644]
test/units/sleep.service [new file with mode: 0644]
test/units/sockets.target [new file with mode: 0644]
test/units/son.service [new file with mode: 0644]
test/units/sysinit.target [new file with mode: 0644]
test/units/test-honor-first-shutdown.service [new file with mode: 0644]
test/units/test-honor-first-shutdown.sh [new file with mode: 0755]
test/units/testsuite-01.service [new file with mode: 0644]
test/units/testsuite-02.service [new file with mode: 0644]
test/units/testsuite-03.service [new file with mode: 0644]
test/units/testsuite-03.sh [new file with mode: 0755]
test/units/testsuite-04.service [new file with mode: 0644]
test/units/testsuite-04.sh [new file with mode: 0755]
test/units/testsuite-05.service [new file with mode: 0644]
test/units/testsuite-05.sh [new file with mode: 0755]
test/units/testsuite-06.service [new file with mode: 0644]
test/units/testsuite-06.sh [new file with mode: 0755]
test/units/testsuite-07.service [new file with mode: 0644]
test/units/testsuite-07.sh [new file with mode: 0755]
test/units/testsuite-08.service [new file with mode: 0644]
test/units/testsuite-09.service [new file with mode: 0644]
test/units/testsuite-10.service [new file with mode: 0644]
test/units/testsuite-11.service [new file with mode: 0644]
test/units/testsuite-11.sh [new file with mode: 0755]
test/units/testsuite-12.service [new file with mode: 0644]
test/units/testsuite-12.sh [new file with mode: 0755]
test/units/testsuite-13.service [new file with mode: 0644]
test/units/testsuite-13.sh [new file with mode: 0755]
test/units/testsuite-14.service [new file with mode: 0644]
test/units/testsuite-14.sh [new file with mode: 0755]
test/units/testsuite-15.service [new file with mode: 0644]
test/units/testsuite-15.sh [new file with mode: 0755]
test/units/testsuite-16.service [new file with mode: 0644]
test/units/testsuite-16.sh [new file with mode: 0755]
test/units/testsuite-17.service [new file with mode: 0644]
test/units/testsuite-17.sh [new file with mode: 0755]
test/units/testsuite-18.service [new file with mode: 0644]
test/units/testsuite-18.sh [new file with mode: 0755]
test/units/testsuite-19.service [new file with mode: 0644]
test/units/testsuite-19.sh [new file with mode: 0755]
test/units/testsuite-20.service [new file with mode: 0644]
test/units/testsuite-20.sh [new file with mode: 0755]
test/units/testsuite-22.01.sh [new file with mode: 0755]
test/units/testsuite-22.02.sh [new file with mode: 0755]
test/units/testsuite-22.03.sh [new file with mode: 0755]
test/units/testsuite-22.04.sh [new file with mode: 0755]
test/units/testsuite-22.05.sh [new file with mode: 0755]
test/units/testsuite-22.06.sh [new file with mode: 0755]
test/units/testsuite-22.07.sh [new file with mode: 0755]
test/units/testsuite-22.08.sh [new file with mode: 0755]
test/units/testsuite-22.09.sh [new file with mode: 0755]
test/units/testsuite-22.service [new file with mode: 0644]
test/units/testsuite-22.sh [new file with mode: 0755]
test/units/testsuite-23.service [new file with mode: 0644]
test/units/testsuite-23.sh [new file with mode: 0755]
test/units/testsuite-24.service [new file with mode: 0644]
test/units/testsuite-24.sh [new file with mode: 0755]
test/units/testsuite-25.service [new file with mode: 0644]
test/units/testsuite-25.sh [new file with mode: 0755]
test/units/testsuite-26.service [new file with mode: 0644]
test/units/testsuite-26.sh [new file with mode: 0755]
test/units/testsuite-27.service [new file with mode: 0644]
test/units/testsuite-27.sh [new file with mode: 0755]
test/units/testsuite-28.service [new file with mode: 0644]
test/units/testsuite-29.service [new file with mode: 0644]
test/units/testsuite-29.sh [new file with mode: 0755]
test/units/testsuite-30.service [new file with mode: 0644]
test/units/testsuite-30.sh [new file with mode: 0755]
test/units/testsuite-31.service [new file with mode: 0644]
test/units/testsuite-31.sh [new file with mode: 0755]
test/units/testsuite-32.service [new file with mode: 0644]
test/units/testsuite-32.sh [new file with mode: 0755]
test/units/testsuite-33.service [new file with mode: 0644]
test/units/testsuite-33.sh [new file with mode: 0755]
test/units/testsuite-34.service [new file with mode: 0644]
test/units/testsuite-34.sh [new file with mode: 0755]
test/units/testsuite-36.service [new file with mode: 0644]
test/units/testsuite-36.sh [new file with mode: 0755]
test/units/testsuite-37.service [new file with mode: 0644]
test/units/testsuite-37.sh [new file with mode: 0755]
test/units/testsuite-38-sleep.service [new file with mode: 0644]
test/units/testsuite-38.service [new file with mode: 0644]
test/units/testsuite-38.sh [new file with mode: 0755]
test/units/testsuite-39.service [new file with mode: 0644]
test/units/testsuite-39.sh [new file with mode: 0755]
test/units/testsuite-40.service [new file with mode: 0644]
test/units/testsuite-40.sh [new file with mode: 0755]
test/units/testsuite-41.service [new file with mode: 0644]
test/units/testsuite-41.sh [new file with mode: 0755]
test/units/testsuite-42.service [new file with mode: 0644]
test/units/testsuite-42.sh [new file with mode: 0755]
test/units/testsuite-43.service [new file with mode: 0644]
test/units/testsuite-43.sh [new file with mode: 0755]
test/units/testsuite-44.service [new file with mode: 0644]
test/units/testsuite-44.sh [new file with mode: 0755]
test/units/testsuite-46.service [new file with mode: 0644]
test/units/testsuite-46.sh [new file with mode: 0755]
test/units/testsuite-47-repro.service [new file with mode: 0644]
test/units/testsuite-47-repro.sh [new file with mode: 0755]
test/units/testsuite-47.service [new file with mode: 0644]
test/units/testsuite-47.sh [new file with mode: 0755]
test/units/testsuite-48.service [new file with mode: 0644]
test/units/testsuite-48.sh [new file with mode: 0755]
test/units/testsuite-49.service [new file with mode: 0644]
test/units/testsuite-49.sh [new file with mode: 0755]
test/units/testsuite-50.service [new file with mode: 0644]
test/units/testsuite-50.sh [new file with mode: 0755]
test/units/testsuite-51-repro-1.service [new file with mode: 0644]
test/units/testsuite-51-repro-2.service [new file with mode: 0644]
test/units/testsuite-51.service [new file with mode: 0644]
test/units/testsuite-51.sh [new file with mode: 0755]
test/units/testsuite-53.service [new file with mode: 0644]
test/units/testsuite-53.sh [new file with mode: 0755]
test/units/testsuite.target [new file with mode: 0644]
test/units/timers.target [new file with mode: 0644]
test/units/unit-.service.d/10-override.conf [new file with mode: 0644]
test/units/unit-with-.service.d/20-override.conf [new file with mode: 0644]
test/units/unit-with-multiple-.service.d/20-override.conf [new file with mode: 0644]
test/units/unit-with-multiple-.service.d/30-override.conf [new file with mode: 0644]
test/units/unit-with-multiple-dashes.service [new file with mode: 0644]
test/units/unit-with-multiple-dashes.service.d/10-override.conf [new file with mode: 0644]
test/units/unstoppable.service [new file with mode: 0644]
test/unstoppable.service [deleted file]
tmpfiles.d/meson.build
tmpfiles.d/systemd-pstore.conf [new file with mode: 0644]
tools/autosuspend-update.sh [new file with mode: 0755]
tools/check-api-docs.sh [new file with mode: 0755]
tools/check-compilation.sh [new file with mode: 0755]
tools/check-help.sh [new file with mode: 0755]
tools/gdb-sd_dump_hashmaps.py
tools/git-contrib.sh [new file with mode: 0755]
tools/hwdb-update.sh [new file with mode: 0755]
tools/make-autosuspend-rules.py
tools/make-directive-index.py
tools/make-man-index.py
tools/make-man-rules.py [deleted file]
tools/meson-autosuspend-update.sh [deleted file]
tools/meson-check-api-docs.sh [deleted file]
tools/meson-check-compilation.sh [deleted file]
tools/meson-check-help.sh [deleted file]
tools/meson-git-contrib.sh [deleted file]
tools/meson-hwdb-update.sh [deleted file]
tools/meson-make-symlink.sh
tools/oss-fuzz.sh
tools/update-dbus-docs.py [new file with mode: 0755]
tools/update-man-rules.py [new file with mode: 0755]
travis-ci/managers/debian.sh
travis-ci/managers/fedora.sh
travis-ci/managers/fuzzbuzz.sh
travis-ci/managers/fuzzit.sh [deleted file]
units/console-getty.service.m4
units/container-getty@.service.m4
units/emergency.service.in
units/getty@.service.m4
units/initrd-udevadm-cleanup-db.service
units/meson.build
units/rescue.service.in
units/serial-getty@.service.m4
units/systemd-backlight@.service.in
units/systemd-coredump@.service.in
units/systemd-homed.service.in
units/systemd-journal-remote.service.in
units/systemd-journald.service.in
units/systemd-logind.service.in
units/systemd-network-generator.service.in
units/systemd-networkd.service.in
units/systemd-networkd.socket
units/systemd-pstore.service.in
units/systemd-random-seed.service.in
units/systemd-repart.service.in
units/systemd-resolved.service.in
units/systemd-rfkill.service.in
units/systemd-timesyncd.service.in
units/systemd-udev-settle.service
units/systemd-udev-trigger.service
units/systemd-udevd.service.in
units/systemd-userdbd.service.in
units/systemd-userdbd.socket
units/tmp.mount
units/user/meson.build
units/user/xdg-desktop-autostart.target [new file with mode: 0644]

diff --git a/.clang-format b/.clang-format
new file mode 100644 (file)
index 0000000..ab27960
--- /dev/null
@@ -0,0 +1,126 @@
+# This configuration file can be used to auto-format the code base.
+# Not all guidelines specified in CODING_STYLE are followed, so the
+# result MUST NOT be committed indiscriminately, but each automated
+# change should be reviewed and only the appropriate ones commited.
+#
+# The easiest way to apply the formatting to your changes ONLY,
+# is to use the git-clang-format script (usually installed with clang-format).
+#
+# -  Fix up formatting before committing
+# 1. Edit and stage your files.
+# 2. Run `git clang-format`.
+# 3. Verify + correct + (un)stage changes.
+# 4. Commit.
+#
+# -  Fix up formatting after committing
+# 1. Commit your changes.
+# 2. Run `git clang-format HEAD~` - Refer the commit *before* your changes here.
+# 3. Verify + correct changes, `git difftool -d` can help here.
+# 4. Stage + commit, potentially with `--amend` (means to fixup the last commit).
+#
+# To run clang-format on all sourcefiles, use the following line:
+# $ git ls-files 'src/*.[ch]' 'src/*.cc' | xargs clang-format -i -style=file
+#
+# You can find more information on the different config parameters in this file here:
+# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
+---
+AccessModifierOffset: -4
+AlignAfterOpenBracket: AlwaysBreak
+AlignEscapedNewlines: Left
+AlignOperands:   false
+AllowShortFunctionsOnASingleLine: None
+AlwaysBreakBeforeMultilineStrings: true
+AlwaysBreakTemplateDeclarations: Yes
+BinPackArguments: false
+BinPackParameters: false
+BraceWrapping:
+  AfterEnum:       false
+  SplitEmptyFunction: false
+  SplitEmptyRecord: false
+  SplitEmptyNamespace: false
+BreakBeforeBraces: Custom
+BreakInheritanceList: BeforeComma
+BreakBeforeTernaryOperators: false
+BreakStringLiterals: false
+ColumnLimit:     109
+CompactNamespaces: true
+ConstructorInitializerAllOnOneLineOrOnePerLine: true
+ConstructorInitializerIndentWidth: 8
+ContinuationIndentWidth: 8
+Cpp11BracedListStyle: false
+ForEachMacros:
+  - BITMAP_FOREACH
+  - CMSG_FOREACH
+  - _DNS_ANSWER_FOREACH
+  - DNS_ANSWER_FOREACH
+  - _DNS_ANSWER_FOREACH_FLAGS
+  - DNS_ANSWER_FOREACH_FLAGS
+  - _DNS_ANSWER_FOREACH_FULL
+  - DNS_ANSWER_FOREACH_FULL
+  - _DNS_ANSWER_FOREACH_IFINDEX
+  - DNS_ANSWER_FOREACH_IFINDEX
+  - _DNS_QUESTION_FOREACH
+  - DNS_QUESTION_FOREACH
+  - FDSET_FOREACH
+  - FOREACH_BTRFS_IOCTL_SEARCH_HEADER
+  - FOREACH_DEVICE
+  - FOREACH_DEVICE_AND_SUBSYSTEM
+  - FOREACH_DEVICE_DEVLINK
+  - FOREACH_DEVICE_PROPERTY
+  - FOREACH_DEVICE_SYSATTR
+  - FOREACH_DEVICE_TAG
+  - FOREACH_DIRENT
+  - FOREACH_DIRENT_ALL
+  - FOREACH_INOTIFY_EVENT
+  - FOREACH_STRING
+  - FOREACH_SUBSYSTEM
+  - _FOREACH_WORD
+  - FOREACH_WORD
+  - FOREACH_WORD_SEPARATOR
+  - HASHMAP_FOREACH
+  - HASHMAP_FOREACH_IDX
+  - HASHMAP_FOREACH_KEY
+  - JOURNAL_FOREACH_DATA_RETVAL
+  - JSON_VARIANT_ARRAY_FOREACH
+  - JSON_VARIANT_OBJECT_FOREACH
+  - LIST_FOREACH
+  - LIST_FOREACH_AFTER
+  - LIST_FOREACH_BEFORE
+  - LIST_FOREACH_OTHERS
+  - LIST_FOREACH_SAFE
+  - MESSAGE_FOREACH_PART
+  - NULSTR_FOREACH
+  - NULSTR_FOREACH_PAIR
+  - OBJECT_PATH_FOREACH_PREFIX
+  - ORDERED_HASHMAP_FOREACH
+  - ORDERED_HASHMAP_FOREACH_KEY
+  - ORDERED_SET_FOREACH
+  - PATH_FOREACH_PREFIX
+  - PATH_FOREACH_PREFIX_MORE
+  - SD_HWDB_FOREACH_PROPERTY
+  - SD_JOURNAL_FOREACH
+  - SD_JOURNAL_FOREACH_BACKWARDS
+  - SD_JOURNAL_FOREACH_DATA
+  - SD_JOURNAL_FOREACH_FIELD
+  - SD_JOURNAL_FOREACH_UNIQUE
+  - SECCOMP_FOREACH_LOCAL_ARCH
+  - SET_FOREACH
+  - SET_FOREACH_MOVE
+  - STRV_FOREACH
+  - STRV_FOREACH_BACKWARDS
+  - STRV_FOREACH_PAIR
+IndentPPDirectives: AfterHash
+IndentWidth:     8
+IndentWrappedFunctionNames: true
+MaxEmptyLinesToKeep: 2
+PenaltyBreakAssignment: 65
+PenaltyBreakBeforeFirstCallParameter: 16
+PenaltyBreakComment: 320
+PenaltyBreakFirstLessLess: 50
+PenaltyBreakString: 0
+PenaltyExcessCharacter: 10
+PenaltyReturnTypeOnItsOwnLine: 100
+SpaceAfterCStyleCast: true
+SpacesInAngles:  true
+TabWidth:        8
+UseCRLF:         false
index 63b1d749cb12653d63b5f735c361bddc5a289488..d24acc1f0c26f3e0ab0e46f6c2557563bf9ab719 100644 (file)
@@ -26,3 +26,7 @@ indent_size = 4
 [meson.build]
 indent_style = space
 indent_size = 8
+
+[man/*.xml]
+indent_size = 2
+indent_style = space
index 63ccea9a6866aba9e061ccf41e327dae3ec5a532..1ca8228969ddc03b78d1a0684691d67f95235df2 100644 (file)
@@ -7,7 +7,8 @@ about: A report of an error in a recent systemd version
 **systemd version the issue has been seen with**
 > ...
 
-<!-- **NOTE:** Do not submit bug reports about anything but the two most recently released systemd versions upstream! -->
+<!-- **NOTE:** Do not submit bug reports about anything but the two most recently released (non-rc) systemd versions upstream! -->
+<!-- See https://github.com/systemd/systemd/releases for the list of most recent releases. -->
 <!-- For older version please use distribution trackers (see https://systemd.io/CONTRIBUTING#filing-issues). -->
 
 **Used distribution**
diff --git a/.github/workflows/build_test.yml b/.github/workflows/build_test.yml
new file mode 100644 (file)
index 0000000..3b86689
--- /dev/null
@@ -0,0 +1,28 @@
+---
+# vi: ts=2 sw=2 et:
+#
+name: Build test
+on:
+  pull_request:
+    paths:
+      - '**/meson.build'
+      - '.github/workflows/**'
+      - 'meson_options.txt'
+      - 'src/**'
+      - 'test/fuzz/**'
+
+jobs:
+  build:
+    runs-on: ubuntu-18.04
+    strategy:
+      fail-fast: false
+      matrix:
+        env:
+          - { COMPILER: "gcc",   COMPILER_VERSION: "10" }
+          - { COMPILER: "clang", COMPILER_VERSION: "10" }
+    env: ${{ matrix.env }}
+    steps:
+      - name: Repository checkout
+        uses: actions/checkout@v1
+      - name: Build check (${{ env.COMPILER }}-${{ env.COMPILER_VERSION }})
+        run: sudo -E .github/workflows/ubuntu-build-check.sh
diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml
new file mode 100644 (file)
index 0000000..ed6db50
--- /dev/null
@@ -0,0 +1,47 @@
+---
+# vi: ts=2 sw=2 et:
+# See: https://google.github.io/oss-fuzz/getting-started/continuous-integration/
+
+name: CIFuzz
+on:
+  pull_request:
+    paths:
+      - '**/meson.build'
+      - '.github/workflows/**'
+      - 'meson_options.txt'
+      - 'src/**'
+      - 'test/fuzz/**'
+      - 'tools/oss-fuzz.sh'
+  push:
+    branches:
+      - master
+jobs:
+ Fuzzing:
+   runs-on: ubuntu-latest
+   if: github.repository == 'systemd/systemd'
+   strategy:
+     fail-fast: false
+     matrix:
+       sanitizer: [address, undefined, memory]
+   steps:
+   - name: Build Fuzzers (${{ matrix.sanitizer }})
+     id: build
+     uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
+     with:
+       oss-fuzz-project-name: 'systemd'
+       dry-run: false
+       allowed-broken-targets-percentage: 0
+       sanitizer: ${{ matrix.sanitizer }}
+   - name: Run Fuzzers (${{ matrix.sanitizer }})
+     uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
+     with:
+       oss-fuzz-project-name: 'systemd'
+       fuzz-seconds: 600
+       dry-run: false
+       sanitizer: ${{ matrix.sanitizer }}
+   - name: Upload Crash
+     uses: actions/upload-artifact@v1
+     if: failure() && steps.build.outcome == 'success'
+     with:
+       name: ${{ matrix.sanitizer }}-artifacts
+       path: ./out/artifacts
diff --git a/.github/workflows/ubuntu-build-check.sh b/.github/workflows/ubuntu-build-check.sh
new file mode 100755 (executable)
index 0000000..75fc36f
--- /dev/null
@@ -0,0 +1,117 @@
+#!/bin/bash
+
+set -ex
+
+info() { echo -e "\033[33;1m$1\033[0m"; }
+fatal() { echo >&2 -e "\033[31;1m$1\033[0m"; exit 1; }
+success() { echo >&2 -e "\033[32;1m$1\033[0m"; }
+
+ARGS=(
+    "--optimization=0"
+    "--optimization=2"
+    "--optimization=s"
+    "--optimization=3 -Db_lto=true"
+    "--optimization=3 -Db_lto=false"
+    "-Db_ndebug=true"
+)
+PACKAGES=(
+    cryptsetup-bin
+    expect
+    fdisk
+    gettext
+    iptables-dev
+    iputils-ping
+    isc-dhcp-client
+    itstool
+    kbd
+    libblkid-dev
+    libcap-dev
+    libcurl4-gnutls-dev
+    libfdisk-dev
+    libgpg-error-dev
+    liblz4-dev
+    liblzma-dev
+    libmicrohttpd-dev
+    libmount-dev
+    libp11-kit-dev
+    libpwquality-dev
+    libqrencode-dev
+    libssl-dev
+    libxkbcommon-dev
+    libzstd-dev
+    mount
+    net-tools
+    perl
+    python-lxml
+    python3-evdev
+    python3-lxml
+    python3-pip
+    python3-pyparsing
+    python3-setuptools
+    quota
+    strace
+    unifont
+    util-linux
+    zstd
+)
+COMPILER="${COMPILER:?}"
+COMPILER_VERSION="${COMPILER_VERSION:?}"
+RELEASE="$(lsb_release -cs)"
+
+bash -c "echo 'deb-src http://archive.ubuntu.com/ubuntu/ $RELEASE main restricted universe multiverse' >>/etc/apt/sources.list"
+
+# Note: As we use postfixed clang/gcc binaries, we need to override $AR
+#       as well, otherwise meson falls back to ar from binutils which
+#       doesn't work with LTO
+if [[ "$COMPILER" == clang ]]; then
+    CC="clang-$COMPILER_VERSION"
+    CXX="clang++-$COMPILER_VERSION"
+    AR="llvm-ar-$COMPILER_VERSION"
+    # Latest LLVM stack deb packages provided by https://apt.llvm.org/
+    # Following snippet was borrowed from https://apt.llvm.org/llvm.sh
+    wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
+    add-apt-repository -y "deb http://apt.llvm.org/$RELEASE/   llvm-toolchain-$RELEASE-$COMPILER_VERSION  main"
+    PACKAGES+=(clang-$COMPILER_VERSION lldb-$COMPILER_VERSION lld-$COMPILER_VERSION clangd-$COMPILER_VERSION)
+elif [[ "$COMPILER" == gcc ]]; then
+    CC="gcc-$COMPILER_VERSION"
+    CXX="g++-$COMPILER_VERSION"
+    AR="gcc-ar-$COMPILER_VERSION"
+    # Latest gcc stack deb packages provided by
+    # https://launchpad.net/~ubuntu-toolchain-r/+archive/ubuntu/test
+    sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
+    PACKAGES+=(gcc-$COMPILER_VERSION)
+else
+    fatal "Unknown compiler: $COMPILER"
+fi
+
+# PPA with some newer build dependencies (like zstd)
+add-apt-repository -y ppa:upstream-systemd-ci/systemd-ci
+apt-get -y update
+apt-get -y build-dep systemd
+apt-get -y install "${PACKAGES[@]}"
+# Install the latest meson and ninja form pip, since the distro versions don't
+# support all the features we need (like --optimization=). Since the build-dep
+# command above installs the distro versions, let's install the pip ones just
+# locally and add the local bin directory to the $PATH.
+pip3 install --user -U meson ninja
+export PATH="$HOME/.local/bin:$PATH"
+
+$CC --version
+
+for args in "${ARGS[@]}"; do
+    SECONDS=0
+
+    info "Checking build with $args"
+    if ! AR="$AR" CC="$CC" CXX="$CXX" CFLAGS="-Werror" CXXFLAGS="-Werror" meson -Dtests=unsafe -Dslow-tests=true --werror $args build; then
+        fatal "meson failed with $args"
+    fi
+
+    ninja --version
+    if ! ninja -C build; then
+        fatal "ninja failed with $args"
+    fi
+
+    git clean -dxf
+
+    success "Build with $args passed in $SECONDS seconds"
+done
index 5d1870553150b93ae8316c638b0972ba5e560ce9..0b2092d7404c2e8d3407c1717baee0f70a12c7e8 100644 (file)
@@ -12,6 +12,8 @@
 .config.args
 .gdb_history
 .deps/
+.mypy_cache/
+__pycache__/
 /*.gcda
 /*.gcno
 /*.tar.bz2
@@ -33,5 +35,5 @@
 /.mkosi-*
 /mkosi.builddir/
 /mkosi.output/
+/mkosi.default
 /tags
-__pycache__/
index 3f3af64d777c4259fb20ec21a663808b58d90bf5..662ea13a609f11c53599ded097eb23d972f7d673 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -118,6 +118,7 @@ Michael Olbrich <m.olbrich@pengutronix.de>
 Michal Soltys <soltys@ziu.info> <nozo@ziu.info>
 Michal Suchanek <msuchanek@suse.de>
 Michal Suchanek <msuchanek@suse.de> <hramrach@gmail.com>
+Michal Sekletár <msekleta@redhat.com>
 Michał Szczepański <skrzatu@hotmail.com> <skrzatu@gmail.com>
 Michel Kraus <github@demonsphere.de> <27o@users.noreply.github.com>
 Miklos Vajna <vmiklos@frugalware.org> <vmiklos@gmail.com>
@@ -206,3 +207,5 @@ Roger James <roger@beardandsandals.co.uk>
 Stephan Edel <se@se-it.eu>
 Andrey Yashkin <38919268+AndreyYashkin@users.noreply.github.com>
 Ronald Tschalär <ronald@innovation.ch>
+Jay Burger <jay.burger@fujitsu.com> <root@new-host-3.home>
+Yi Gao <ymuemc@163.com>
index 350d7cd2b8241e588dc1d803bd6ff095ea5765bc..cb1952206d34af52d0a49d3fa64524e0698ae860 100644 (file)
@@ -3,7 +3,7 @@
 # Copyright © 2016 Zeal Jagannatha
 
 # This is a settings file for OS image generation using mkosi (https://github.com/systemd/mkosi).
-# Simply invoke "mkosi" in the project directory to build an OS image.
+# Symlink this file to mkosi.default in the project root directory and invoke "mkosi" to build an OS image.
 
 [Distribution]
 Distribution=arch
@@ -52,6 +52,7 @@ BuildPackages=
         python-lxml
         qrencode
         xz
+        zstd
 
 Packages=
         libidn2
index e85612bef1e50cc13cc935e29bfdfa2afa5c76be..db9bd3550ed3d199c93d8a3dbffccd85e1ba3cb1 100644 (file)
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: LGPL-2.1+
 
 # This is a settings file for OS image generation using mkosi (https://github.com/systemd/mkosi).
-# Simply invoke "mkosi" in the project directory to build an OS image.
+# Symlink this file to mkosi.default in the project root directory and invoke "mkosi" to build an OS image.
 
 [Distribution]
 Distribution=debian
@@ -50,6 +50,7 @@ BuildPackages=
         libsmartcols-dev
         libtool
         libxkbcommon-dev
+        libzstd-dev
         m4
         meson
         pkg-config
@@ -59,6 +60,7 @@ BuildPackages=
         uuid-dev
         xsltproc
         xz-utils
+        zstd
 
 Packages=
         libqrencode4
index 01bfd2338bbcfb5ad1350786dd6df881bbe5b588..09527711fe6335523d05782887afe8fd6173af0f 100644 (file)
@@ -1,11 +1,11 @@
 # SPDX-License-Identifier: LGPL-2.1+
 
 # This is a settings file for OS image generation using mkosi (https://github.com/systemd/mkosi).
-# Simply invoke "mkosi" in the project directory to build an OS image.
+# Symlink this file to mkosi.default in the project root directory and invoke "mkosi" to build an OS image.
 
 [Distribution]
 Distribution=fedora
-Release=31
+Release=32
 
 [Output]
 Format=gpt_ext4
@@ -31,7 +31,6 @@ BuildPackages=
         gnu-efi-devel
         gnutls-devel
         gperf
-        hostname
         iptables-devel
         kmod-devel
         libacl-devel
@@ -48,6 +47,7 @@ BuildPackages=
         libselinux-devel
         libxkbcommon-devel
         libxslt
+        libzstd-devel
         lz4
         lz4-devel
         m4
@@ -63,16 +63,13 @@ BuildPackages=
         tree
         valgrind-devel
         xz-devel
+        zstd
 
 Packages=
-        coreutils
-        cryptsetup-libs
-        kmod-libs
-        e2fsprogs
-        libidn2
-        libseccomp
+        # libzstd can be dropped once the Fedora RPM gets a dependency on it
+        libzstd
+        # procps-ng provides a set of useful utilies (ps, free, etc)
         procps-ng
-        util-linux
 
 BuildDirectory=mkosi.builddir
 Cache=mkosi.cache
diff --git a/.mkosi/mkosi.opensuse b/.mkosi/mkosi.opensuse
new file mode 100644 (file)
index 0000000..53837b6
--- /dev/null
@@ -0,0 +1,78 @@
+# SPDX-License-Identifier: LGPL-2.1+
+
+# This is a settings file for OS image generation using mkosi (https://github.com/systemd/mkosi).
+# Symlink this file to mkosi.default in the project root directory and invoke "mkosi" to build an OS image.
+
+[Distribution]
+Distribution=opensuse
+Release=tumbleweed
+
+[Output]
+Format=raw_btrfs
+Bootable=yes
+
+[Partitions]
+RootSize=3G
+
+[Packages]
+# Uncomment to share system RPM cache (works only with Tumbleweed)
+#Cache=/var/cache/zypp/packages
+BuildDirectory=mkosi.builddir
+BuildPackages=
+        docbook-xsl-stylesheets
+        fdupes
+        gcc
+        gnu-efi
+        gperf
+        intltool
+        libacl-devel
+        libapparmor-devel
+        libblkid-devel
+        libbz2-devel
+        libcap-devel
+        libcryptsetup-devel
+        libcurl-devel
+        libgcrypt-devel
+        libkmod-devel
+        liblz4-devel
+        libmicrohttpd-devel
+        libmount-devel
+        libseccomp-devel
+        libselinux-devel
+        libxslt-tools
+        m4
+        meson
+        pam-devel
+        pciutils-devel
+        pcre-devel
+        python3
+        python3-lxml
+        qrencode-devel
+        system-user-nobody
+        systemd-sysvinit
+        zlib-devel
+# to satisfy tests
+        acl
+        diffutils
+        glibc-locale
+        system-group-obsolete
+        system-user-bin
+        system-user-daemon
+        system-user-root
+        timezone
+
+Packages=
+        # brought in via meson->python3
+        libp11-kit0
+        # --bootable=no
+        dbus-1
+        libapparmor1
+        libcrypt1
+        libcryptsetup12
+        libkmod2
+        liblz4-1
+        libmount1
+        libqrencode4
+        libseccomp2
+        pam
+        util-linux
index 1e4005f0707a603acac75ff5b80d529bea88fc93..8dcc67a66270a53750ed19df9c09a66d7e870c16 100644 (file)
@@ -1,11 +1,11 @@
 # SPDX-License-Identifier: LGPL-2.1+
 
 # This is a settings file for OS image generation using mkosi (https://github.com/systemd/mkosi).
-# Simply invoke "mkosi" in the project directory to build an OS image.
+# Symlink this file to mkosi.default in the project root directory and invoke "mkosi" to build an OS image.
 
 [Distribution]
 Distribution=ubuntu
-Release=bionic
+Release=focal
 Repositories=main,universe
 
 [Output]
@@ -25,7 +25,6 @@ BuildPackages=
         git
         gnu-efi
         gperf
-        iptables-dev
         libacl1-dev
         libaudit-dev
         libblkid-dev
@@ -39,6 +38,8 @@ BuildPackages=
         libgcrypt20-dev
         libgnutls28-dev
         libidn2-0-dev
+        libip4tc-dev
+        libip6tc-dev
         libkmod-dev
         liblz4-dev
         liblz4-tool
@@ -51,6 +52,8 @@ BuildPackages=
         libsmartcols-dev
         libtool
         libxkbcommon-dev
+        libxtables-dev
+        libzstd-dev
         m4
         meson
         pkg-config
@@ -61,8 +64,9 @@ BuildPackages=
         uuid-dev
         xsltproc
         xz-utils
+        zstd
 
 Packages=
-        libqrencode3
+        libqrencode4
         locales
         libidn2-0
index 82f62a860f2c7153ca0e72fa3e9504e54a8c94b8..50f8e6a230b5104870da0f098f31c4dbd2d086d3 100644 (file)
-sudo: required
-dist: xenial
+---
+# vi: ts=2 sw=2 et:
+
+language: bash
+dist: bionic
 services:
-    - docker
+  - docker
 
 env:
-    global:
-        - AUTHOR_EMAIL="$(git log -1 $TRAVIS_COMMIT --pretty=\"%aE\")"
-        - CI_MANAGERS="$TRAVIS_BUILD_DIR/travis-ci/managers"
-        - CI_TOOLS="$TRAVIS_BUILD_DIR/travis-ci/tools"
-        - REPO_ROOT="$TRAVIS_BUILD_DIR"
+  global:
+    - AUTHOR_EMAIL="$(git log -1 $TRAVIS_COMMIT --pretty=\"%aE\")"
+    - CI_MANAGERS="$TRAVIS_BUILD_DIR/travis-ci/managers"
+    - CI_TOOLS="$TRAVIS_BUILD_DIR/travis-ci/tools"
+    - REPO_ROOT="$TRAVIS_BUILD_DIR"
+  jobs:
+    - DEBIAN_RELEASE=testing PHASE="RUN_GCC"
+    - DEBIAN_RELEASE=testing PHASE="RUN_GCC_ASAN_UBSAN"
+    - DEBIAN_RELEASE=testing PHASE="RUN_CLANG"
+    - DEBIAN_RELEASE=testing PHASE="RUN_CLANG_ASAN_UBSAN"
 
 stages:
-    - name: Build & test
-      if: type != cron
-
-    - name: Fuzzit-Fuzzing
-      if: type = cron
-
-    - name: Fuzzit-Regression
-      if: type != cron
+  # 'Test' is the default stage (for matrix jobs)
+  - name: Test
+    if: type != cron
 
     # Run Coverity periodically instead of for each commit/PR
-    - name: Coverity
-      if: type = cron
-
-jobs:
-    include:
-        - stage: Build & test
-          name: Debian Testing
-          language: bash
-          env:
-              - DEBIAN_RELEASE="testing"
-              - CONT_NAME="systemd-debian-$DEBIAN_RELEASE"
-              - DOCKER_EXEC="docker exec -ti $CONT_NAME"
-          before_install:
-              - sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce
-              - docker --version
-          install:
-              - $CI_MANAGERS/debian.sh SETUP
-          script:
-              - set -e
-              # Build systemd
-              - $CI_MANAGERS/debian.sh RUN
-              - set +e
-          after_script:
-              - $CI_MANAGERS/debian.sh CLEANUP
+  - name: Coverity
+    if: type = cron
 
-        - name: Debian Testing (ASan+UBSan)
-          language: bash
-          env:
-              - DEBIAN_RELEASE="testing"
-              - CONT_NAME="systemd-debian-$DEBIAN_RELEASE"
-              - DOCKER_EXEC="docker exec -ti $CONT_NAME"
-          before_install:
-              - sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce
-              - docker --version
-          install:
-              - $CI_MANAGERS/debian.sh SETUP
-          script:
-              - set -e
-              - $CI_MANAGERS/debian.sh RUN_ASAN
-              - set +e
-          after_script:
-              - $CI_MANAGERS/debian.sh CLEANUP
+# Matrix job definition - this is run for each combination of env variables
+# from the env.jobs array above
+before_install:
+  - sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce
+  - docker --version
+install:
+  - $CI_MANAGERS/debian.sh SETUP
+script:
+  - $CI_MANAGERS/debian.sh $PHASE || travis_terminate 1
+after_script:
+  - $CI_MANAGERS/debian.sh CLEANUP
 
-        - name: Debian Testing (clang)
-          language: bash
-          env:
-              - DEBIAN_RELEASE="testing"
-              - CONT_NAME="systemd-debian-$DEBIAN_RELEASE"
-              - DOCKER_EXEC="docker exec -ti $CONT_NAME"
-          before_install:
-              - sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce
-              - docker --version
-          install:
-              - $CI_MANAGERS/debian.sh SETUP
-          script:
-              - set -e
-              - $CI_MANAGERS/debian.sh RUN_CLANG
-              - set +e
-          after_script:
-              - $CI_MANAGERS/debian.sh CLEANUP
-
-        - name: Debian Testing (clang ASan+UBSan)
-          language: bash
-          env:
-              - DEBIAN_RELEASE="testing"
-              - CONT_NAME="systemd-debian-$DEBIAN_RELEASE"
-              - DOCKER_EXEC="docker exec -ti $CONT_NAME"
-          before_install:
-              - sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce
-              - docker --version
-          install:
-              - $CI_MANAGERS/debian.sh SETUP
-          script:
-              - set -e
-              - $CI_MANAGERS/debian.sh RUN_CLANG_ASAN
-              - set +e
-          after_script:
-              - $CI_MANAGERS/debian.sh CLEANUP
-
-        - stage: Fuzzit-Regression
-          name:  Continuous Fuzzing via Fuzzit (regression)
-          language: bash
-          script:
-            - set -e
-            - $CI_MANAGERS/fuzzit.sh regression
-            - set +e
-
-        - stage: Fuzzit-Fuzzing
-          name: Continuous Fuzzing via Fuzzit (fuzzing daily)
-          language: bash
-          script:
-            - set -e
-            - $CI_MANAGERS/fuzzit.sh fuzzing
-            - set +e
-
-        - stage: Coverity
-          language: bash
-          env:
-              - FEDORA_RELEASE="latest"
-              - CONT_NAME="coverity-fedora-$FEDORA_RELEASE"
-              - DOCKER_EXEC="docker exec -ti $CONT_NAME"
-              - TOOL_BASE="/var/tmp/coverity-scan-analysis"
-              - DOCKER_RUN="docker run -v $TOOL_BASE:$TOOL_BASE:rw --env-file .cov-env"
-              # Coverity env variables
-              - PLATFORM="$(uname)"
-              - TOOL_ARCHIVE="/var/tmp/cov-analysis-$PLATFORM.tgz"
-              - SCAN_URL="https://scan.coverity.com"
-              - UPLOAD_URL="https://scan.coverity.com/builds"
-              - COVERITY_SCAN_PROJECT_NAME="$TRAVIS_REPO_SLUG"
-              - COVERITY_SCAN_NOTIFICATION_EMAIL="${AUTHOR_EMAIL}"
-              - COVERITY_SCAN_BRANCH_PATTERN="$TRAVIS_BRANCH"
-              # Encrypted COVERITY_SCAN_TOKEN env variable
-              # Generated using `travis encrypt -r systemd/systemd COVERITY_SCAN_TOKEN=xxxx`
-              - secure: "jKSz+Y1Mv8xMpQHh7g5lzW7E6HQGndFz/vKDJQ1CVShwFoyjV3Zu+MFS3UYKlh1236zL0Z4dvsYFx/b3Hq8nxZWCrWeZs2NdXgy/wh8LZhxwzcGYigp3sIA/cYdP5rDjFJO0MasNkl25/rml8+eZWz+8/xQic98UQHjSco/EOWtssoRcg0J0c4eDM7bGLfIQWE73NNY1Q1UtWjKmx1kekVrM8dPmHXJ9aERka7bmcbJAcKd6vabs6DQ5AfWccUPIn/EsRYqIJTRxJrFYU6XizANZ1a7Vwk/DWHZUEn2msxcZw5BbAMDTMx0TbfrNkKSHMHuvQUCu6KCBAq414i+LgkMfmQ2SWwKiIUsud1kxXX3ZPl9bxDv1HkvVdcniC/EM7lNEEVwm4meOnjuhI2lhOyOjmP3FTSlMHGP7xlK8DS2k9fqL58vn0BaSjwWgd+2+HuL2+nJmxcK1eLGzKqaostFxrk2Xs2vPZkUdV2nWY/asUrcWHml6YlWDn2eP83pfwxHYsMiEHY/rTKvxeVY+iirO/AphoO+eaYu7LvjKZU1Yx5Z4u/SnGWAiCH0yhMis0bWmgi7SCbw+sDd2uya+aoiLIGiB2ChW7hXHXCue/dif6/gLU7b+L8R00pQwnWdvKUPoIJCmZJYCluTeib4jpW+EmARB2+nR8wms2K9FGKM="
-          before_install:
-              - sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce
-              - docker --version
-          install:
-              # Install Coverity on the host
-              - $CI_TOOLS/get-coverity.sh
-              # Export necessary env variables for Coverity
-              - env | grep -E "TRAVIS|COV|TOOL|URL" > .cov-env
-              # Pull a Docker image and start a new container
-              - $CI_MANAGERS/fedora.sh SETUP
-          script:
-              - set -e
-              # Preconfigure with meson to prevent Coverity from capturing meson metadata
-              - $DOCKER_EXEC meson cov-build -Dman=false
-              # Run Coverity
-              - $DOCKER_EXEC tools/coverity.sh build
-              - $DOCKER_EXEC tools/coverity.sh upload
+# Inject another (single) job into the matrix for Coverity
+jobs:
+  include:
+    - stage: Coverity
+      language: bash
+      env:
+        - FEDORA_RELEASE="31"
+        - TOOL_BASE="/var/tmp/coverity-scan-analysis"
+        - CONT_NAME="coverity-fedora-$FEDORA_RELEASE"
+        - DOCKER_EXEC="docker exec -ti $CONT_NAME"
+        - DOCKER_RUN="docker run -v $TOOL_BASE:$TOOL_BASE:rw --env-file .cov-env"
+          # Coverity env variables
+        - PLATFORM="$(uname)"
+        - TOOL_ARCHIVE="/var/tmp/cov-analysis-$PLATFORM.tgz"
+        - SCAN_URL="https://scan.coverity.com"
+        - UPLOAD_URL="https://scan.coverity.com/builds"
+        - COVERITY_SCAN_PROJECT_NAME="$TRAVIS_REPO_SLUG"
+        - COVERITY_SCAN_NOTIFICATION_EMAIL="${AUTHOR_EMAIL}"
+        - COVERITY_SCAN_BRANCH_PATTERN="$TRAVIS_BRANCH"
+          # Encrypted COVERITY_SCAN_TOKEN env variable
+          # Generated using `travis encrypt -r systemd/systemd COVERITY_SCAN_TOKEN=xxxx`
+        - secure: "jKSz+Y1Mv8xMpQHh7g5lzW7E6HQGndFz/vKDJQ1CVShwFoyjV3Zu+MFS3UYKlh1236zL0Z4dvsYFx/b3Hq8nxZWCrWeZs2NdXgy/wh8LZhxwzcGYigp3sIA/cYdP5rDjFJO0MasNkl25/rml8+eZWz+8/xQic98UQHjSco/EOWtssoRcg0J0c4eDM7bGLfIQWE73NNY1Q1UtWjKmx1kekVrM8dPmHXJ9aERka7bmcbJAcKd6vabs6DQ5AfWccUPIn/EsRYqIJTRxJrFYU6XizANZ1a7Vwk/DWHZUEn2msxcZw5BbAMDTMx0TbfrNkKSHMHuvQUCu6KCBAq414i+LgkMfmQ2SWwKiIUsud1kxXX3ZPl9bxDv1HkvVdcniC/EM7lNEEVwm4meOnjuhI2lhOyOjmP3FTSlMHGP7xlK8DS2k9fqL58vn0BaSjwWgd+2+HuL2+nJmxcK1eLGzKqaostFxrk2Xs2vPZkUdV2nWY/asUrcWHml6YlWDn2eP83pfwxHYsMiEHY/rTKvxeVY+iirO/AphoO+eaYu7LvjKZU1Yx5Z4u/SnGWAiCH0yhMis0bWmgi7SCbw+sDd2uya+aoiLIGiB2ChW7hXHXCue/dif6/gLU7b+L8R00pQwnWdvKUPoIJCmZJYCluTeib4jpW+EmARB2+nR8wms2K9FGKM="
+      before_install:
+        - sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce
+        - docker --version
+      install:
+        # Install Coverity on the host
+        - $CI_TOOLS/get-coverity.sh
+          # Export necessary env variables for Coverity
+        - env | grep -E "TRAVIS|COV|TOOL|URL" > .cov-env
+          # Pull a Docker image and start a new container
+        - $CI_MANAGERS/fedora.sh SETUP
+      script:
+        - set -e
+          # Preconfigure with meson to prevent Coverity from capturing meson metadata
+        - $DOCKER_EXEC meson cov-build -Dman=false
+          # Run Coverity
+        - $DOCKER_EXEC tools/coverity.sh build
+        - $DOCKER_EXEC tools/coverity.sh upload
 
-              - set +e
-          after_script:
-              - $CI_MANAGERS/fedora.sh CLEANUP
+        - set +e
+      after_script:
+        - $CI_MANAGERS/fedora.sh CLEANUP
diff --git a/NEWS b/NEWS
index c24c865bec4c5c35e2c1ca60222f15bb18cf62f1..95685ed7f71102d6bbe42f34349f008e174031d9 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,609 @@
 systemd System and Service Manager
 
+CHANGES WITH 246:
+
+        * The service manager gained basic support for cgroup v2 freezer. Units
+          can now be suspended or resumed either using new systemctl verbs,
+          freeze and thaw respectively, or via D-Bus.
+
+        * PID 1 may now automatically load pre-compiled AppArmor policies from
+          /etc/apparmor/earlypolicy during early boot.
+
+        * The CPUAffinity= setting in service unit files now supports a new
+          special value "numa" that causes the CPU affinity masked to be set
+          based on the NUMA mask.
+
+        * systemd will now log about all left-over processes remaining in a
+          unit when the unit is stopped. It will now warn about services using
+          KillMode=none, as this is generally an unsafe thing to make use of.
+
+        * Two new unit file settings
+          ConditionPathIsEncrypted=/AssertPathIsEncrypted= have been
+          added. They may be used to check whether a specific file system path
+          resides on a block device that is encrypted on the block level
+          (i.e. using dm-crypt/LUKS).
+
+        * Another pair of new settings ConditionEnvironment=/AssertEnvironment=
+          has been added that may be used for simple environment checks. This
+          is particularly useful when passing in environment variables from a
+          container manager (or from PAM in case of the systemd --user
+          instance).
+
+        * .service unit files now accept a new setting CoredumpFilter= which
+          allows configuration of the memory sections coredumps of the
+          service's processes shall include.
+
+        * .mount units gained a new ReadWriteOnly= boolean option. If set
+          it will not be attempted to mount a file system read-only if mounting
+          in read-write mode doesn't succeed. An option x-systemd.rw-only is
+          available in /etc/fstab to control the same.
+
+        * .socket units gained a new boolean setting PassPacketInfo=. If
+          enabled, the kernel will attach additional per-packet metadata to all
+          packets read from the socket, as an ancillary message. This controls
+          the IP_PKTINFO, IPV6_RECVPKTINFO, NETLINK_PKTINFO socket options,
+          depending on socket type.
+
+        * .service units gained a new setting RootHash= which may be used to
+          specify the root hash for verity enabled disk images which are
+          specified in RootImage=. RootVerity= may be used to specify a path to
+          the Verity data matching a RootImage= file system. (The latter is
+          only useful for images that do not contain the Verity data embedded
+          into the same image that carries a GPT partition table following the
+          Discoverable Partition Specification). Similarly, systemd-nspawn
+          gained a new switch --verity-data= that takes a path to a file with
+          the verity data of the disk image supplied in --image=, if the image
+          doesn't contain the verity data itself.
+
+        * .service units gained a new setting RootHashSignature= which takes
+          either a base64 encoded PKCS#7 signature of the root hash specified
+          with RootHash=, or a path to a file to read the signature from. This
+          allows validation of the root hash against public keys available in
+          the kernel keyring, and is only supported on recent kernels
+          (>= 5.4)/libcryptsetup (>= 2.30). A similar switch has been added to
+          systemd-nspawn and systemd-dissect (--root-hash-sig=). Support for
+          this mechanism has also been added to systemd-veritysetup.
+
+        * .service unit files gained two new options
+          TimeoutStartFailureMode=/TimeoutStopFailureMode= that may be used to
+          tune behaviour if a start or stop timeout is hit, i.e. whether to
+          terminate the service with SIGTERM, SIGABRT or SIGKILL.
+
+        * Most options in systemd that accept hexadecimal values prefixed with
+          0x in additional to the usual decimal notation now also support octal
+          notation when the 0o prefix is used and binary notation if the 0b
+          prefix is used.
+
+        * Various command line parameters and configuration file settings that
+          configure key or certificate files now optionally take paths to
+          AF_UNIX sockets in the file system. If configured that way a stream
+          connection is made to the socket and the required data read from
+          it. This is a simple and natural extension to the existing regular
+          file logic, and permits other software to provide keys or
+          certificates via simple IPC services, for example when unencrypted
+          storage on disk is not desired. Specifically, systemd-networkd's
+          Wireguard and MACSEC key file settings as well as
+          systemd-journal-gatewayd's and systemd-journal-remote's PEM
+          key/certificate parameters support this now.
+
+        * Unit files, tmpfiles.d/ snippets, sysusers.d/ snippets and other
+          configuration files that support specifier expansion learnt six new
+          specifiers: %a resolves to the current architecture, %o/%w/%B/%W
+          resolve to the various ID fields from /etc/os-release, %l resolves to
+          the "short" hostname of the system, i.e. the hostname configured in
+          the kernel truncated at the first dot.
+
+        * Support for the .include syntax in unit files has been removed. The
+          concept has been obsolete for 6 years and we started warning about
+          its pending removal 2 years ago (also see NEWS file below). It's
+          finally gone now.
+
+        * StandardError= and StandardOutput= in unit files no longer support
+          the "syslog" and "syslog-console" switches. They were long removed
+          from the documentation, but will now result in warnings when used,
+          and be converted to "journal" and "journal+console" automatically.
+
+        * If the service setting User= is set to the "nobody" user, a warning
+          message is now written to the logs (but the value is nonetheless
+          accepted). Setting User=nobody is unsafe, since the primary purpose
+          of the "nobody" user is to own all files whose owner cannot be mapped
+          locally. It's in particular used by the NFS subsystem and in user
+          namespacing. By running a service under this user's UID it might get
+          read and even write access to all these otherwise unmappable files,
+          which is quite likely a major security problem.
+
+        * tmpfs mounts automatically created by systemd (/tmp, /run, /dev/shm,
+          and others) now have a size and inode limits applied (50% of RAM for
+          /tmp and /dev/shm, 10% of RAM for other mounts, etc.)
+
+        * nss-mymachines lost support for resolution of users and groups, and
+          now only does resolution of hostnames. This functionality is now
+          provided by nss-systemd. Thus, the 'mymachines' entry should be
+          removed from the 'passwd:' and 'group:' lines in /etc/nsswitch.conf
+          (and 'systemd' added if it is not already there).
+
+        * A new kernel command line option systemd.hostname= has been added
+          that allows controlling the hostname that is initialized early during
+          boot.
+
+        * A kernel command line option "udev.blockdev_read_only" has been
+          added. If specified all hardware block devices that show up are
+          immediately marked as read-only by udev. This option is useful for
+          making sure that a specific boot under no circumstances modifies data
+          on disk. Use "blockdev --setrw" to undo the effect of this, per
+          device.
+
+        * A new boolean kernel command line option systemd.swap= has been
+          added, which may be used to turn off automatic activation of swap
+          devices listed in /etc/fstab.
+
+        * New kernel command line options systemd.condition-needs-update= and
+          systemd.condition-first-boot= have been added, which override the
+          result of the ConditionNeedsUpdate= and ConditionFirstBoot=
+          conditions.
+
+        * A new kernel command line option systemd.clock-usec= has been added
+          that allows setting the system clock to the specified time in µs
+          since Jan 1st, 1970 early during boot. This is in particular useful
+          in order to make test cases more reliable.
+
+        * The fs.suid_dumpable sysctl is set to 2 / "suidsafe". This allows
+          systemd-coredump to save core files for suid processes. When saving
+          the core file, systemd-coredump will use the effective uid and gid of
+          the process that faulted.
+
+        * The /sys/module/kernel/parameters/crash_kexec_post_notifiers file is
+          now automatically set to "Y" at boot, in order to enable pstore
+          generation for collection with systemd-pstore.
+
+        * A new 'hwdb' file has been added that collects information about PCI
+          and USB devices that correctly support auto-suspend, on top of the
+          databases for this we import from the ChromiumOS project. If you have
+          a device that supports auto-suspend correctly and where it should be
+          enabled by default, please submit a patch that adds it to the
+          database (see /usr/lib/udev/hwdb.d/60-autosuspend.hwdb).
+
+        * systemd-udevd gained the new configuration option timeout_signal= as well
+          as a corresponding kernel command line option udev.timeout_signal=.
+          The option can be used to configure the UNIX signal that the main
+          daemon sends to the worker processes on timeout. Setting the signal
+          to SIGABRT is useful for debugging.
+
+        * .link files managed by systemd-udevd gained options RxFlowControl=,
+          TxFlowControl=, AutoNegotiationFlowControl= in the [Link] section, in
+          order to configure various flow control parameters. They also gained
+          RxMiniBufferSize= and RxJumboBufferSize= in order to configure jumbo
+          frame ring buffer sizes.
+
+        * networkd.conf gained a new boolean setting ManageForeignRoutes=. If
+          enabled systemd-networkd manages all routes configured by other tools.
+
+        * .network files managed by systemd-networkd gained a new section
+          [SR-IOV], in order to configure SR-IOV capable network devices.
+
+        * systemd-networkd's [IPv6Prefix] section in .network files gained a
+          new boolean setting Assign=. If enabled an address from the prefix is
+          automatically assigned to the interface.
+
+        * systemd-networkd gained a new section [DHCPv6PrefixDelegation] which
+          controls delegated prefixes assigned by DHCPv6 client. The section
+          has three settings: SubnetID=, Assign=, and Token=. The setting
+          SubnetID= allows explicit configuration of the preferred subnet that
+          systemd-networkd's Prefix Delegation logic assigns to interfaces. If
+          Assign= is enabled (which is the default) an address from any acquired
+          delegated prefix is automatically chosen and assigned to the
+          interface. The setting Token= specifies an optional address generation
+          mode for Assign=.
+
+        * systemd-networkd's [Network] section gained a new setting
+          IPv4AcceptLocal=. If enabled the interface accepts packets with local
+          source addresses.
+
+        * systemd-networkd gained support for configuring the HTB queuing
+          discipline in the [HierarchyTokenBucket] and
+          [HierarchyTokenBucketClass] sections. Similar the "pfifo" qdisc may
+          be configured in the [PFIFO] section, "GRED" in
+          [GenericRandomEarlyDetection], "SFB" in [StochasticFairBlue], "cake"
+          in [CAKE], "PIE" in [PIE], "DRR" in [DeficitRoundRobinScheduler] and
+          [DeficitRoundRobinSchedulerClass], "BFIFO" in [BFIFO],
+          "PFIFOHeadDrop" in [PFIFOHeadDrop], "PFIFOFast" in [PFIFOFast], "HHF"
+          in [HeavyHitterFilter], "ETS" in [EnhancedTransmissionSelection] and
+          "QFQ" in [QuickFairQueueing] and [QuickFairQueueingClass].
+
+        * systemd-networkd gained support for a new Termination= setting in the
+          [CAN] section for configuring the termination resistor. It also
+          gained a new ListenOnly= setting for controlling whether to only
+          listen on CAN interfaces, without interfering with traffic otherwise
+          (which is useful for debugging/monitoring CAN network
+          traffic). DataBitRate=, DataSamplePoint=, FDMode=, FDNonISO= have
+          been added to configure various CAN-FD aspects.
+
+        * systemd-networkd's [DHCPv6] section gained a new option WithoutRA=.
+          When enabled, DHCPv6 will be attempted right-away without requiring an
+          Router Advertisement packet suggesting it first (i.e. without the 'M'
+          or 'O' flags set). The [IPv6AcceptRA] section gained a boolean option
+          DHCPv6Client= that may be used to turn off the DHCPv6 client even if
+          the RA packets suggest it.
+
+        * systemd-networkd's [DHCPv4] section gained a new setting UseGateway=
+          which may be used to turn off use of the gateway information provided
+          by the DHCP lease. A new FallbackLeaseLifetimeSec= setting may be
+          used to configure how to process leases that lack a lifetime option.
+
+        * systemd-networkd's [DHCPv4] and [DHCPServer] sections gained a new
+          setting SendVendorOption= allowing configuration of additional vendor
+          options to send in the DHCP requests/responses. The [DHCPv6] section
+          gained a new SendOption= setting for sending arbitrary DHCP
+          options. RequestOptions= has been added to request arbitrary options
+          from the server. UserClass= has been added to set the DHCP user class
+          field.
+
+        * systemd-networkd's [DHCPServer] section gained a new set of options
+          EmitPOP3=/POP3=, EmitSMTP=/SMTP=, EmitLPR=/LPR= for including server
+          information about these three protocols in the DHCP lease. It also
+          gained support for including "MUD" URLs ("Manufacturer Usage
+          Description"). Support for "MUD" URLs was also added to the LLDP
+          stack, configurable in the [LLDP] section in .network files.
+
+        * The Mode= settings in [MACVLAN] and [MACVTAP] now support 'source'
+          mode. Also, the sections now support a new setting SourceMACAddress=.
+
+        * systemd-networkd's .netdev files now support a new setting
+          VLANProtocol= in the [Bridge] section that allows configuration of
+          the VLAN protocol to use.
+
+        * systemd-networkd supports a new Group= setting in the [Link] section
+          of the .network files, to control the link group.
+
+        * systemd-networkd's [Network] section gained a new
+          IPv6LinkLocalAddressGenerationMode= setting, which specifies how IPv6
+          link local address is generated.
+
+        * A new default .network file is now shipped that matches TUN/TAP
+          devices that begin with "vt-" in their name. Such interfaces will
+          have IP routing onto the host links set up automatically. This is
+          supposed to be used by VM managers to trivially acquire a network
+          interface which is fully set up for host communication, simply by
+          carefully picking an interface name to use.
+
+        * systemd-networkd's [DHCPv6] section gained a new setting RouteMetric=
+          which sets the route priority for routes specified by the DHCP server.
+
+        * systemd-networkd's [DHCPv6] section gained a new setting VendorClass=
+          which configures the vendor class information sent to DHCP server.
+
+        * The BlackList= settings in .network files' [DHCPv4] and
+          [IPv6AcceptRA] sections have been renamed DenyList=. The old names
+          are still understood to provide compatibility.
+
+        * networkctl gained the new "forcerenew" command for forcing all DHCP
+          server clients to renew their lease. The interface "status" output
+          will now show numerous additional fields of information about an
+          interface. There are new "up" and "down" commands to bring specific
+          interfaces up or down.
+
+        * systemd-resolved's DNS= configuration option now optionally accepts a
+          port number (after ":") and a host name (after "#"). When the host
+          name is specified, the DNS-over-TLS certificate is validated to match
+          the specified hostname. Additionally, in case of IPv6 addresses, an
+          interface may be specified (after "%").
+
+        * systemd-resolved may be configured to forward single-label DNS names.
+          This is not standard-conformant, but may make sense in setups where
+          public DNS servers are not used.
+
+        * systemd-resolved's DNS-over-TLS support gained SNI validation.
+
+        * systemd-nspawn's --resolv-conf= switch gained a number of new
+          supported values. Specifically, options starting with "replace-" are
+          like those prefixed "copy-" but replace any existing resolv.conf
+          file. And options ending in "-uplink" and "-stub" can now be used to
+          propagate other flavours of resolv.conf into the container (as
+          defined by systemd-resolved).
+
+        * The various programs included in systemd can now optionally output
+          their log messages on stderr prefixed with a timestamp, controlled by
+          the $SYSTEMD_LOG_TIME environment variable.
+
+        * systemctl gained a new "-P" switch that is a shortcut for "--value
+          --property=…".
+
+        * "systemctl list-units" and "systemctl list-machines" no longer hide
+          their first output column with --no-legend. To hide the first column,
+          use --plain.
+
+        * "systemctl reboot" takes the option "--reboot-argument=".
+          The optional positional argument to "systemctl reboot" is now
+          being deprecated in favor of this option.
+
+        * systemd-run gained a new switch --slice-inherit. If specified the
+          unit it generates is placed in the same slice as the systemd-run
+          process itself.
+
+        * systemd-journald gained support for zstd compression of large fields
+          in journal files. The hash tables in journal files have been hardened
+          against hash collisions. This is an incompatible change and means
+          that journal files created with new systemd versions are not readable
+          with old versions. If the $SYSTEMD_JOURNAL_KEYED_HASH boolean
+          environment variable for systemd-journald.service is set to 0 this
+          new hardening functionality may be turned off, so that generated
+          journal files remain compatible with older journalctl
+          implementations.
+
+        * journalctl will now include a clickable link in the default output for
+          each log message for which an URL with further documentation is
+          known. This is only supported on terminal emulators that support
+          clickable hyperlinks, and is turned off if a pager is used (since
+          "less" still doesn't support hyperlinks,
+          unfortunately). Documentation URLs may be included in log messages
+          either by including a DOCUMENTATION= journal field in it, or by
+          associating a journal message catalog entry with the log message's
+          MESSAGE_ID, which then carries a "Documentation:" tag.
+
+        * journald.conf gained a new boolean setting Audit= that may be used to
+          control whether systemd-journald will enable audit during
+          initialization.
+
+        * when systemd-journald's log stream is broken up into multiple lines
+          because the PID of the sender changed this is indicated in the
+          generated log records via the _LINE_BREAK=pid-change field.
+
+        * journalctl's "-o cat" output mode will now show one or more journal
+          fields specified with --output-fields= instead of unconditionally
+          MESSAGE=. This is useful to retrieve a very specific set of fields
+          without any decoration.
+
+        * The sd-journal.h API gained two new functions:
+          sd_journal_enumerate_available_unique() and
+          sd_journal_enumerate_available_data() that operate like their
+          counterparts that lack the _available_ in the name, but skip items
+          that cannot be read and processed by the local implementation
+          (i.e. are compressed in an unsupported format or such),
+
+        * coredumpctl gained a new --file= switch, matching the same one in
+          journalctl: a specific journal file may be specified to read the
+          coredump data from.
+
+        * coredumps collected by systemd-coredump may now be compressed using
+          the zstd algorithm.
+
+        * systemd-binfmt gained a new switch --unregister for unregistering all
+          registered entries at once. This is now invoked automatically at
+          shutdown, so that binary formats registered with the "F" flag will
+          not block clean file system unmounting.
+
+        * systemd-notify's --pid= switch gained new values: "parent", "self",
+          "auto" for controlling which PID to send to the service manager: the
+          systemd-notify process' PID, or the one of the process invoking it.
+
+        * systemd-logind's Session bus object learnt a new method call
+          SetType() for temporarily updating the session type of an already
+          allocated session. This is useful for upgrading tty sessions to
+          graphical ones once a compositor is invoked.
+
+        * systemd-socket-proxy gained a new switch --exit-idle-time= for
+          configuring an exit-on-idle time.
+
+        * systemd-repart's --empty= setting gained a new value "create". If
+          specified a new empty regular disk image file is created under the
+          specified name. Its size may be specified with the new --size=
+          option. The latter is also supported without the "create" mode, in
+          order to grow existing disk image files to the specified size. These
+          two new options are useful when creating or manipulating disk images
+          instead of operating on actual block devices.
+
+        * systemd-repart drop-ins now support a new UUID= setting to control
+          the UUID to assign to a newly created partition.
+
+        * systemd-repart's SizeMin= per-partition parameter now defaults to 10M
+          instead of 0.
+
+        * systemd-repart's Label= setting now support the usual, simple
+          specifier expansion.
+
+        * systemd-homed's LUKS backend gained the ability to discard empty file
+          system blocks automatically when the user logs out. This is enabled
+          by default to ensure that home directories take minimal space when
+          logged out but get full size guarantees when logged in. This may be
+          controlled with the new --luks-offline-discard= switch to homectl.
+
+        * If systemd-homed detects that /home/ is encrypted as a whole it will
+          now default to the directory or subvolume backends instead of the
+          LUKS backend, in order to avoid double encryption. The default
+          storage and file system may now be configured explicitly, too, via
+          the new /etc/systemd/homed.conf configuration file.
+
+        * systemd-homed now supports unlocking home directories with FIDO2
+          security tokens that support the 'hmac-secret' extension, in addition
+          to the existing support for PKCS#11 security token unlocking
+          support. Note that many recent hardware security tokens support both
+          interfaces. The FIDO2 support is accessible via homectl's
+          --fido2-device= option.
+
+        * homectl's --pkcs11-uri= setting now accepts two special parameters:
+          if "auto" is specified and only one suitable PKCS#11 security token
+          is plugged in, its URL is automatically determined and enrolled for
+          unlocking the home directory. If "list" is specified a brief table of
+          suitable PKCS#11 security tokens is shown. Similar, the new
+          --fido2-device= option also supports these two special values, for
+          automatically selecting and listing suitable FIDO2 devices.
+
+        * The /etc/crypttab tmp option now optionally takes an argument
+          selecting the file system to use. Moreover, the default is now
+          changed from ext2 to ext4.
+
+        * There's a new /etc/crypttab option "keyfile-erase". If specified the
+          key file listed in the same line is removed after use, regardless if
+          volume activation was successful or not. This is useful if the key
+          file is only acquired transiently at runtime and shall be erased
+          before the system continues to boot.
+
+        * There's also a new /etc/crypttab option "try-empty-password". If
+          specified, before asking the user for a password it is attempted to
+          unlock the volume with an empty password. This is useful for
+          installing encrypted images whose password shall be set on first boot
+          instead of at installation time.
+
+        * systemd-cryptsetup will now attempt to load the keys to unlock
+          volumes with automatically from files in
+          /etc/cryptsetup-keys.d/<volume>.key and
+          /run/cryptsetup-keys.d/<volume>.key, if any of these files exist.
+
+        * systemd-cryptsetup may now activate Microsoft BitLocker volumes via
+          /etc/crypttab, during boot.
+
+        * logind.conf gained a new RuntimeDirectoryInodesMax= setting to
+          control the inode limit for the per-user $XDG_RUNTIME_DIR tmpfs
+          instance.
+
+        * A new generator systemd-xdg-autostart-generator has been added. It
+          generates systemd unit files from XDG autostart .desktop files, and
+          may be used to let the systemd user instance manage services that are
+          started automatically as part of the desktop session.
+
+        * "bootctl" gained a new verb "reboot-to-firmware" that may be used
+          to query and change the firmware's 'reboot into firmware' setup flag.
+
+        * systemd-firstboot gained a new switch --kernel-command-line= that may
+          be used to initialize the /etc/kernel/cmdline file of the image. It
+          also gained a new switch --root-password-hashed= which is like
+          --root-password= but accepts a pre-hashed UNIX password as
+          argument. The new option --delete-root-password may be used to unset
+          any password for the root user (dangerous!). The --root-shell= switch
+          may be used to control the shell to use for the root account. A new
+          --force option may be used to override any already set settings with
+          the parameters specified on the command line (by default, the tool
+          will not override what has already been set before, i.e. is purely
+          incremental).
+
+        * systemd-firstboot gained support for a new --image= switch, which is
+          similar to --root= but accepts the path to a disk image file, on
+          which it then operates.
+
+        * A new sd-path.h API has been added to libsystemd. It provides a
+          simple API for retrieving various search paths and primary
+          directories for various resources.
+
+        * A new call sd_notify_barrier() has been added to the sd-daemon.h
+          API. The call will block until all previously sent sd_notify()
+          messages have been processed by the service manager. This is useful
+          to remove races caused by a process already having disappeared at the
+          time a notification message is processed by the service manager,
+          making correct attribution impossible. The systemd-notify tool will
+          now make use of this call implicitly, but this can be turned off again
+          via the new --no-block switch.
+
+        * When sending a file descriptor (fd) to the service manager to keep
+          track of, using the sd_notify() mechanism, a new parameter FDPOLL=0
+          may be specified. If passed the service manager will refrain from
+          poll()ing on the file descriptor. Traditionally (and when the
+          parameter is not specified), the service manager will poll it for
+          POLLHUP or POLLERR events, and immediately close the fds in that
+          case.
+
+        * The service manager (PID1) gained a new D-Bus method call
+          SetShowStatus() which may be used to control whether it shall show
+          boot-time status output on the console. This method has a similar
+          effect to sending SIGRTMIN+20/SIGRTMIN+21 to PID 1.
+
+        * The sd-bus API gained a number of convenience functions that take
+          va_list arguments rather than "...". For example, there's now
+          sd_bus_call_methodv() to match sd_bus_call_method(). Those calls make
+          it easier to build wrappers that accept variadic arguments and want
+          to pass a ready va_list structure to sd-bus.
+
+        * sd-bus vtable entries can have a new SD_BUS_VTABLE_ABSOLUTE_OFFSET
+          flag which alters how the userdata pointer to pass to the callbacks
+          is determined. When the flag is set, the offset field is converted
+          as-is into a pointer, without adding it to the object pointer the
+          vtable is associated with.
+
+        * sd-bus now exposes four new functions:
+          sd_bus_interface_name_is_valid() + sd_bus_service_name_is_valid() +
+          sd_bus_member_name_is_valid() + sd_bus_object_path_is_valid() will
+          validate strings to check if they qualify as various D-Bus concepts.
+
+        * The sd-bus API gained the SD_BUS_METHOD_WITH_ARGS(),
+          SD_BUS_METHOD_WITH_ARGS_OFFSET() and SD_BUS_SIGNAL_WITH_ARGS() macros
+          that simplify adding argument names to D-Bus methods and signals.
+
+        * The man pages for the sd-bus and sd-hwdb APIs have been completed.
+
+        * Various D-Bus APIs of systemd daemons now have man pages that
+          document the methods, signals and properties.
+
+        * The expectations on user/group name syntax are now documented in
+          detail; documentation on how classic home directories may be
+          converted into home directories managed by homed has been added;
+          documentation regarding integration of homed/userdb functionality in
+          desktops has been added:
+
+              https://systemd.io/USER_NAMES
+              https://systemd.io/CONVERTING_TO_HOMED
+              https://systemd.io/USERDB_AND_DESKTOPS
+
+        * Documentation for the on-disk Journal file format has been updated
+          and has now moved to:
+
+              https://systemd.io/JOURNAL_FILE_FORMAT
+
+        * The interface for containers (https://systemd.io/CONTAINER_INTERFACE)
+          has been extended by a set of environment variables that expose
+          select fields from the host's os-release file to the container
+          payload. Similarly, host's os-release files can be mounted into the
+          container underneath /run/host. Together, those mechanisms provide a
+          standardized way to expose information about the host to the
+          container payload. Both interfaces are implemented in systemd-nspawn.
+
+        * All D-Bus services shipped in systemd now implement the generic
+          LogControl1 D-Bus API which allows clients to change log level +
+          target of the service during runtime.
+
+        * Only relevant for developers: the mkosi.default symlink has been
+          dropped from version control. Please create a symlink to one of the
+          distribution-specific defaults in .mkosi/ based on your preference.
+
+        Contributions from: 24bisquitz, Adam Nielsen, Alan Perry, Alexander
+        Malafeev, Amitanand.Chikorde, Alin Popa, Alvin Šipraga, Amos Bird,
+        Andreas Rammhold, AndreRH, Andrew Doran, Anita Zhang, Ankit Jain,
+        antznin, Arnaud Ferraris, Arthur Moraes do Lago, Arusekk, Balaji
+        Punnuru, Balint Reczey, Bastien Nocera, bemarek, Benjamin Berg,
+        Benjamin Dahlhoff, Benjamin Robin, Chris Down, Chris Kerr, Christian
+        Göttsche, Christian Hesse, Christian Oder, Ciprian Hacman, Clinton Roy,
+        codicodi, Corey Hinshaw, Daan De Meyer, Dana Olson, Dan Callaghan,
+        Daniel Fullmer, Daniel Rusek, Dan Streetman, Dave Reisner, David
+        Edmundson, David Wood, Denis Pronin, Diego Escalante Urrelo, Dimitri
+        John Ledkov, dolphrundgren, duguxy, Einsler Lee, Elisei Roca, Emmanuel
+        Garette, Eric Anderson, Eric DeVolder, Evgeny Vereshchagin,
+        ExtinctFire, fangxiuning, Ferran Pallarès Roca, Filipe Brandenburger,
+        Filippo Falezza, Finn, Florian Klink, Florian Mayer, Franck Bui,
+        Frantisek Sumsal, gaurav, Georg Müller, Gergely Polonkai, Giedrius
+        Statkevičius, Gigadoc2, gogogogi, Gaurav Singh, gzjsgdsb, Hans de
+        Goede, Haochen Tong, ianhi, ignapk, Jakov Smolic, James T. Lee, Jan
+        Janssen, Jan Klötzke, Jan Palus, Jay Burger, Jeremy Cline, Jérémy
+        Rosen, Jian-Hong Pan, Jiri Slaby, Joel Shapiro, Joerg Behrmann, Jörg
+        Thalheim, Jouke Witteveen, Kai-Heng Feng, Kenny Levinsen, Kevin
+        Kuehler, Kumar Kartikeya Dwivedi, layderv, laydervus, Lénaïc Huard,
+        Lennart Poettering, Lidong Zhong, Luca Boccassi, Luca BRUNO, Lucas
+        Werkmeister, Lukas Klingsbo, Lukáš Nykrýn, Łukasz Stelmach, Maciej
+        S. Szmigiero, MadMcCrow, Marc-André Lureau, Marcel Holtmann, Marc
+        Kleine-Budde, Martin Hundebøll, Matthew Leeds, Matt Ranostay, Maxim
+        Fomin, MaxVerevkin, Michael Biebl, Michael Chapman, Michael Gubbels,
+        Michael Marley, Michał Bartoszkiewicz, Michal Koutný, Michal Sekletár,
+        Mike Gilbert, Mike Kazantsev, Mikhail Novosyolov, ml, Motiejus Jakštys,
+        nabijaczleweli, nerdopolis, Niccolò Maggioni, Niklas Hambüchen, Norbert
+        Lange, Paul Cercueil, pelzvieh, Peter Hutterer, Piero La Terza, Pieter
+        Lexis, Piotr Drąg, Rafael Fontenelle, Richard Petri, Ronan Pigott, Ross
+        Lagerwall, Rubens Figueiredo, satmandu, Sean-StarLabs, Sebastian
+        Jennen, sterlinghughes, Surhud More, Susant Sahani, szb512, Thomas
+        Haller, Tobias Hunger, Tom, Tomáš Pospíšek, Tomer Shechner, Tom Hughes,
+        Topi Miettinen, Tudor Roman, Uwe Kleine-König, Valery0xff, Vito Caputo,
+        Vladimir Panteleev, Vladyslav Tronko, Wen Yang, Yegor Vialov, Yigal
+        Korman, Yi Gao, YmrDtnJu, Yuri Chornoivan, Yu Watanabe, Zbigniew
+        Jędrzejewski-Szmek, Zhu Li, Дамјан Георгиевски, наб
+
+        – Warsaw, 2020-07-30
+
 CHANGES WITH 245:
 
         * A new tool "systemd-repart" has been added, that operates as an
@@ -171,8 +775,6 @@ CHANGES WITH 245:
         * systemd-sysusers gained support for creating users with the primary
           group named differently than the user.
 
-        * systemd-resolved's DNS-over-TLS support gained SNI validation.
-
         * systemd-growfs (i.e. the x-systemd.growfs mount option in /etc/fstab)
           gained support for growing XFS partitions. Previously it supported
           only ext4 and btrfs partitions.
@@ -270,7 +872,7 @@ CHANGES WITH 245:
           such files in version 243.
 
         * systemd-logind will now validate access to the operation of changing
-          the virtual terminal via a PolicyKit action. By default, only users
+          the virtual terminal via a polkit action. By default, only users
           with at least one session on a local VT are granted permission.
 
         * When systemd sets up PAM sessions that invoked service processes
@@ -373,7 +975,7 @@ CHANGES WITH 244:
           of the PAM session, for example for time-limited logins.
 
         * A new @pkey system call group is now defined to make it easier to
-          whitelist memory protection syscalls for containers and services
+          allow-list memory protection syscalls for containers and services
           which need to use them.
 
         * systemd-udevd: removed the 30s timeout for killing stale workers on
@@ -390,10 +992,10 @@ CHANGES WITH 244:
         * udev now provides a program (fido_id) that identifies FIDO CTAP1
           ("U2F")/CTAP2 security tokens based on the usage declared in their
           report and descriptor and outputs suitable environment variables.
-          This replaces the externally maintained whitelists of all known
+          This replaces the externally maintained allow lists of all known
           security tokens that were used previously.
 
-        * Automatically generated autosuspend udev rules for whitelisted
+        * Automatically generated autosuspend udev rules for allow-listed
           devices have been imported from the Chromium OS project. This should
           improve power saving with many more devices.
 
@@ -501,8 +1103,8 @@ CHANGES WITH 244:
           configuration time using the -Dservice-watchdog= setting. If set to
           empty, the watchdogs will be disabled.
 
-       * systemd-resolved validates IP addresses in certificates now when GnuTLS
-         is being used.
+        * systemd-resolved validates IP addresses in certificates now when GnuTLS
+          is being used.
 
         * libcryptsetup >= 2.0.1 is now required.
 
@@ -684,10 +1286,10 @@ CHANGES WITH 243:
           the IO accounting data is included in the resource log message
           generated whenever a unit stops.
 
-        * Units may now configure an explicit time-out to wait for when killed
+        * Units may now configure an explicit timeout to wait for when killed
           with SIGABRT, for example when a service watchdog is hit. Previously,
-          the regular TimeoutStopSec= time-out was applied in this case too —
-          now a separate time-out may be set using TimeoutAbortSec=.
+          the regular TimeoutStopSec= timeout was applied in this case too —
+          now a separate timeout may be set using TimeoutAbortSec=.
 
         * Services may now send a special WATCHDOG=trigger message with
           sd_notify() to trigger an immediate "watchdog missed" event, and thus
@@ -717,7 +1319,7 @@ CHANGES WITH 243:
 
         * If processes terminated during the last phase of shutdown do not exit
           quickly systemd will now show their names after a short time, to make
-          debugging easier. After a longer time-out they are forcibly killed,
+          debugging easier. After a longer timeout they are forcibly killed,
           as before.
 
         * journalctl (and the other tools that display logs) will now highlight
@@ -760,7 +1362,7 @@ CHANGES WITH 243:
 
         * systemd-networkd's DHCPv4 support now understands a new MaxAttempts=
           option for configuring the maximum number of DHCP lease requests.  It
-          also learnt a new BlackList= option for blacklisting DHCP servers (a
+          also learnt a new BlackList= option for deny-listing DHCP servers (a
           similar setting has also been added to the IPv6 RA client), as well
           as a SendRelease= option for configuring whether to send a DHCP
           RELEASE message when terminating.
@@ -988,7 +1590,7 @@ CHANGES WITH 243:
           space if there are multiple devices with the highest priority.
 
         * /etc/crypttab support has learnt a new keyfile-timeout= per-device
-          option that permits selecting the timout how long to wait for a
+          option that permits selecting the timeout how long to wait for a
           device with an encryption key before asking for the password.
 
         * IOWeight= has learnt to properly set the IO weight when using the
@@ -1992,12 +2594,12 @@ CHANGES WITH 239:
           any relevant symlinks both in /run and /etc.
 
         * Note that all long-running system services shipped with systemd will
-          now default to a system call whitelist (rather than a blacklist, as
+          now default to a system call allow list (rather than a deny list, as
           before). In particular, systemd-udevd will now enforce one too. For
           most cases this should be safe, however downstream distributions
           which disabled sandboxing of systemd-udevd (specifically the
           MountFlags= setting), might want to disable this security feature
-          too, as the default whitelisting will prohibit all mount, swap,
+          too, as the default allow-listing will prohibit all mount, swap,
           reboot and clock changing operations from udev rules.
 
         * sd-boot acquired new loader configuration settings to optionally turn
@@ -2025,7 +2627,7 @@ CHANGES WITH 239:
           lookup is likely to trigger nss-ldap which in turn might use NSS to
           ask systemd-resolved for hostname lookups. This will hence result in
           a deadlock: a user name lookup in order to start
-          systemd-resolved.service will result in a host name lookup for which
+          systemd-resolved.service will result in a hostname lookup for which
           systemd-resolved.service needs to be started already. There are
           multiple ways to work around this problem: pre-allocate the
           "systemd-resolve" user on such systems, so that nss-ldap won't be
@@ -2994,7 +3596,7 @@ CHANGES WITH 235:
           A/AAAA resource record for the "_gateway" hostname, pointing to the
           current default IP gateway. Previously it did that for the "gateway"
           name, hampering adoption, as some distributions wanted to leave that
-          host name open for local use. The old behaviour may still be
+          hostname open for local use. The old behaviour may still be
           requested at build time.
 
         * systemd-networkd's [Address] section in .network files gained a new
@@ -3025,7 +3627,7 @@ CHANGES WITH 235:
         * systemd-nspawn gained support for a new --system-call-filter= command
           line option for adding and removing entries in the default system
           call filter it applies. Moreover systemd-nspawn has been changed to
-          implement a system call whitelist instead of a blacklist.
+          implement a system call allow list instead of a deny list.
 
         * systemd-run gained support for a new --pipe command line option. If
           used the STDIN/STDOUT/STDERR file descriptors passed to systemd-run
@@ -3513,7 +4115,7 @@ CHANGES WITH 233:
           that is removed when the container dies. Specifically, if the source
           directory is specified as empty string this mechanism is selected. An
           example usage is --overlay=+/var::/var, which creates an overlay
-          mount based on the original /var contained in the image, overlayed
+          mount based on the original /var contained in the image, overlaid
           with a temporary directory in the host's /var/tmp. This way changes
           to /var are automatically flushed when the container shuts down.
 
@@ -4335,7 +4937,7 @@ CHANGES WITH 230:
           again don't consider turning this on in your stable, LTS or
           production release just yet. (Note that you have to enable
           nss-resolve in /etc/nsswitch.conf, to actually use systemd-resolved
-          and its DNSSEC mode for host name resolution from local
+          and its DNSSEC mode for hostname resolution from local
           applications.)
 
         * systemd-resolve conveniently resolves DANE records with the --tlsa
@@ -5738,11 +6340,10 @@ CHANGES WITH 220:
           fsck's progress report to an AF_UNIX socket in the file
           system.
 
-        * udev will no longer create device symlinks for all block
-          devices by default. A blacklist for excluding special block
-          devices from this logic has been turned into a whitelist
-          that requires picking block devices explicitly that require
-          device symlinks.
+        * udev will no longer create device symlinks for all block devices by
+          default. A deny list for excluding special block devices from this
+          logic has been turned into a allow list that requires picking block
+          devices explicitly that require device symlinks.
 
         * A new (currently still internal) API sd-device.h has been
           added to libsystemd. This modernized API is supposed to
@@ -6153,14 +6754,14 @@ CHANGES WITH 218:
           for a unit, as declared in the (usually vendor-supplied)
           system preset files.
 
-        * nss-myhostname will now resolve the single-label host name
+        * nss-myhostname will now resolve the single-label hostname
           "gateway" to the locally configured default IP routing
           gateways, ordered by their metrics. This assigns a stable
           name to the used gateways, regardless which ones are
           currently configured. Note that the name will only be
           resolved after all other name sources (if nss-myhostname is
           configured properly) and should hence not negatively impact
-          systems that use the single-label host name "gateway" in
+          systems that use the single-label hostname "gateway" in
           other contexts.
 
         * systemd-inhibit now allows filtering by mode when listing
@@ -6500,7 +7101,7 @@ CHANGES WITH 217:
         * Calendar time specifications in .timer units now also
           understand the strings "semi-annually", "quarterly" and
           "minutely" as shortcuts (in addition to the preexisting
-          "anually", "hourly", ...).
+          "annually", "hourly", ...).
 
         * systemd-tmpfiles will now correctly create files in /dev
           at boot which are marked for creation only at boot. It is
@@ -7588,7 +8189,7 @@ CHANGES WITH 210:
           reported by uname()'s "machine" field.
 
         * systemd-networkd now supports matching on the system
-          virtualization, architecture, kernel command line, host name
+          virtualization, architecture, kernel command line, hostname
           and machine ID.
 
         * logind is now a lot more aggressive when suspending the
@@ -7631,11 +8232,11 @@ CHANGES WITH 210:
           Wikipedia. We explicitly document which base applies for
           each configuration option.
 
-        * The DeviceAllow= setting in unit files now supports a syntax
-          to whitelist an entire group of devices node majors at once,
-          based on the /proc/devices listing. For example, with the
-          string "char-pts", it is now possible to whitelist all
-          current and future pseudo-TTYs at once.
+        * The DeviceAllow= setting in unit files now supports a syntax to
+          allow-list an entire group of devices node majors at once, based on
+          the /proc/devices listing. For example, with the string "char-pts",
+          it is now possible to allow-list all current and future pseudo-TTYs
+          at once.
 
         * sd-event learned a new "post" event source. Event sources of
           this type are triggered by the dispatching of any event
@@ -7906,12 +8507,12 @@ CHANGES WITH 209:
           example, a line that creates /run/nologin).
 
         * A new API "sd-resolve.h" has been added which provides a simple
-          asynchronous wrapper around glibc NSS host name resolution
+          asynchronous wrapper around glibc NSS hostname resolution
           calls, such as getaddrinfo(). In contrast to glibc's
           getaddrinfo_a(), it does not use signals. In contrast to most
           other asynchronous name resolution libraries, this one does
           not reimplement DNS, but reuses NSS, so that alternate
-          host name resolution systems continue to work, such as mDNS,
+          hostname resolution systems continue to work, such as mDNS,
           LDAP, etc. This API is based on libasyncns, but it has been
           cleaned up for inclusion in systemd.
 
@@ -9695,7 +10296,7 @@ CHANGES WITH 190:
           when he over-mounts a non-empty directory.
 
         * There are new specifiers that are resolved in unit files,
-          for the host name (%H), the machine ID (%m) and the boot ID
+          for the hostname (%H), the machine ID (%m) and the boot ID
           (%b).
 
         Contributions from: Allin Cottrell, Auke Kok, Brandon Philips,
@@ -9878,9 +10479,9 @@ CHANGES WITH 187:
         * journalctl gained the new "--header" switch to introspect
           header data of journal files.
 
-        * A new setting SystemCallFilters= has been added to services
-          which may be used to apply blacklists or whitelists to
-          system calls. This is based on SECCOMP Mode 2 of Linux 3.5.
+        * A new setting SystemCallFilters= has been added to services which may
+          be used to apply deny lists or allow lists to system calls. This is
+          based on SECCOMP Mode 2 of Linux 3.5.
 
         * nspawn gained a new --link-journal= switch (and quicker: -j)
           to link the container journal with the host. This makes it
diff --git a/README b/README
index b2c8d28411f623ea4cdb5fa5416add16db8eeb43..558b8d9195dbac1ca64f75ef7e0be99b4b12cbc3 100644 (file)
--- a/README
+++ b/README
@@ -35,6 +35,7 @@ LICENSE:
 REQUIREMENTS:
         Linux kernel >= 3.13
         Linux kernel >= 4.2 for unified cgroup hierarchy support
+        Linux kernel >= 5.4 for signed Verity images support
 
         Kernel Config Options:
           CONFIG_DEVTMPFS
@@ -102,6 +103,9 @@ REQUIREMENTS:
           CONFIG_EFIVAR_FS
           CONFIG_EFI_PARTITION
 
+        Required for signed Verity images support:
+          CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG
+
         We recommend to turn off Real-Time group scheduling in the
         kernel when using systemd. RT group scheduling effectively
         makes RT scheduling unavailable for most userspace, since it
@@ -144,12 +148,13 @@ REQUIREMENTS:
         libblkid >= 2.24 (from util-linux) (optional)
         libkmod >= 15 (optional)
         PAM >= 1.1.2 (optional)
-        libcryptsetup (optional)
+        libcryptsetup (optional), >= 2.3.0 required for signed Verity images support
         libaudit (optional)
         libacl (optional)
         libselinux (optional)
         liblzma (optional)
         liblz4 >= 1.3.0 / 130 (optional)
+        libzstd >= 1.4.0 (optional)
         libgcrypt (optional)
         libqrencode (optional)
         libmicrohttpd (optional)
@@ -257,19 +262,19 @@ USERS AND GROUPS:
 NSS:
         systemd ships with four glibc NSS modules:
 
-        nss-myhostname resolves the local hostname to locally
-        configured IP addresses, as well as "localhost" to
-        127.0.0.1/::1.
+        nss-myhostname resolves the local hostname to locally configured IP
+        addresses, as well as "localhost" to 127.0.0.1/::1.
 
-        nss-resolve enables DNS resolution via the systemd-resolved
-        DNS/LLMNR caching stub resolver "systemd-resolved".
+        nss-resolve enables DNS resolution via the systemd-resolved DNS/LLMNR
+        caching stub resolver "systemd-resolved".
 
         nss-mymachines enables resolution of all local containers registered
-        with machined to their respective IP addresses. It also maps UID/GIDs
-        ranges used by containers to useful names.
+        with machined to their respective IP addresses.
 
-        nss-systemd enables resolution of all dynamically allocated service
-        users. (See the DynamicUser= setting in unit files.)
+        nss-systemd enables resolution of users/group registered via the
+        User/Group Record Lookup API (https://systemd.io/USER_GROUP_API/),
+        including all dynamically allocated service users. (See the
+        DynamicUser= setting in unit files.)
 
         To make use of these NSS modules, please add them to the "hosts:",
         "passwd:" and "group:" lines in /etc/nsswitch.conf. The "resolve"
@@ -278,8 +283,8 @@ NSS:
 
         The four modules should be used in the following order:
 
-                passwd: compat mymachines systemd
-                group: compat mymachines systemd
+                passwd: compat systemd
+                group: compat systemd
                 hosts: files mymachines resolve [!UNAVAIL=return] dns myhostname
 
 SYSV INIT.D SCRIPTS:
index 0274715e8cf392c5377a195f6a56b23a9f982a8d..f836d812b18c0672a332632a88d8ab251e6723ec 100644 (file)
--- a/README.md
+++ b/README.md
@@ -6,17 +6,21 @@ System and Service Manager
 <a href="https://in.waw.pl/systemd-github-state/systemd-systemd-pull-requests.svg"><img align="right" src="https://in.waw.pl/systemd-github-state/systemd-systemd-pull-requests-small.svg" alt="Count of open pull requests over time"></a>
 [![Semaphore CI Build Status](https://semaphoreci.com/api/v1/projects/28a5a3ca-3c56-4078-8b5e-7ed6ef912e14/443470/shields_badge.svg)](https://semaphoreci.com/systemd/systemd)<br/>
 [![Coverity Scan Status](https://scan.coverity.com/projects/350/badge.svg)](https://scan.coverity.com/projects/350)<br/>
-[![Fuzzit Status](https://app.fuzzit.dev/badge?org_id=systemd&branch=master)](https://app.fuzzit.dev/orgs/systemd/dashboard)<br/>
-[![OSS-Fuzz Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/systemd.svg)](https://oss-fuzz-build-logs.storage.googleapis.com/index.html)<br/>
+[![OSS-Fuzz Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/systemd.svg)](https://oss-fuzz-build-logs.storage.googleapis.com/index.html#systemd)<br/>
+[![CIFuzz](https://github.com/systemd/systemd/workflows/CIFuzz/badge.svg)](https://github.com/systemd/systemd/actions)<br/>
 [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/1369/badge)](https://bestpractices.coreinfrastructure.org/projects/1369)<br/>
 [![Travis CI Build Status](https://travis-ci.org/systemd/systemd.svg?branch=master)](https://travis-ci.org/systemd/systemd)<br/>
 [![Language Grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/systemd/systemd.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/systemd/systemd/context:cpp)<br/>
 [![CentOS CI Build Status](https://ci.centos.org/buildStatus/icon?job=systemd-pr-build)](https://ci.centos.org/job/systemd-pr-build/)<br/>
-[![Build Status](https://dev.azure.com/evvers/systemd-systemd/_apis/build/status/systemd.systemd?branchName=master)](https://dev.azure.com/evvers/systemd-systemd/_build/latest?definitionId=1&branchName=master)
+[![Build Status](https://dev.azure.com/evvers/systemd-systemd/_apis/build/status/systemd.systemd?branchName=master)](https://dev.azure.com/evvers/systemd-systemd/_build/latest?definitionId=1&branchName=master)<br/>
+[![Fossies codespell report](https://fossies.org/linux/test/systemd-master.tar.gz/codespell.svg)](https://fossies.org/linux/test/systemd-master.tar.gz/codespell.html)</br>
+[![Packaging status](https://repology.org/badge/tiny-repos/systemd.svg)](https://repology.org/project/systemd/versions)
 
 ## Details
 
-General information about systemd can be found in the [systemd Wiki](https://www.freedesktop.org/wiki/Software/systemd).
+Most documentation is available on [systemd's web site](https://systemd.io/).
+
+Assorted, older, general information about systemd can be found in the [systemd Wiki](https://www.freedesktop.org/wiki/Software/systemd).
 
 Information about build requirements is provided in the [README file](README).
 
diff --git a/TODO b/TODO
index 9bba36fe672474d94ad589374e5c4492a78aa80e..58fdc45e1ef2dbfeffadac5cae94fede2784602d 100644 (file)
--- a/TODO
+++ b/TODO
@@ -8,8 +8,6 @@ External:
 
 * Fedora: add an rpmlint check that verifies that all unit files in the RPM are listed in %systemd_post macros.
 
-* wiki: update journal format documentation for lz4 additions
-
 Janitorial Clean-ups:
 
 * Rearrange tests so that the various test-xyz.c match a specific src/basic/xyz.c again
@@ -19,13 +17,164 @@ Janitorial Clean-ups:
 
 Features:
 
-* cryptsetup/homed: also support FIDO2 HMAC password logic for unlocking
-  devices. (see: https://github.com/mjec/fido2-hmac-secret)
+* nspawn: move "incoming mount" directory to /run/host, move "inaccessible"
+  nodes to /run/host, move notify socket (for sd_notify() between payload and
+  container manager)
+
+* cryptsetup: if keyfile specified in crypttab is AF_UNIX socket, connect to it
+  and read from it (like we do elsewhere with READ_FULL_FILE_CONNECT_SOCKET)
+
+* repart: support setting up dm-integrity with HMAC
+
+* add /etc/integritytab, to support dm-integrity setups. In particular those
+  with HMAC as hash function, so that we can have a protected /home without
+  encryption (leaving encryption to the individual dirs/homed).
+
+* complement root=, rootflags=, rootfstype= with rootsubdir= which allows
+  mounting a subdir of the root fs as actual root. This can be used as
+  fstype-agnostic version of btrfs' rootflags=subvol=foobar.
+
+* add --copy-from and --copy-to command to systemd-dissect which copies stuff
+  in and out of a disk image
+
+* Support ProtectProc= or so, using: https://patchwork.kernel.org/cover/11310197/
+
+* if /usr/bin/swapoff fails due to OOM, log a friendly explanatory message about it
+
+* build short web pages out of each catalog entry, build them along with man
+  pages, and include hyperlinks to them in the journal output
+
+* machined: add API to acquire UID range. add API to mount/dissect loopback
+  file. Both protected by PK. Then make nspawn use these APIs to run
+  unprivileged containers. i.e. push the truly privileged bits into machined,
+  so that the client side can remain entirely unprivileged, with SUID or
+  anything like that.
+
+* journald: do journal file writing out-of-process, with one writer process per
+  client UID, so that synthetic hash table collisions can slow down a specific
+  user's journal stream down but not the others.
+
+* add "throttling" to sd-event event sources: optionally, when we wake up too
+  often for one, let's turn it off entirely for a while. Use that for the
+  /proc/self/mountinfo logic.
+
+* move our systemd-user PAM snippet to /usr/, which PAM appears to support
+  these days
+
+* nspawn: support time namespaces
+
+* systemd-firstboot: make sure to always use chase_symlinks() before
+  reading/writing files
+
+* add ConditionSecurity=tpm2
+
+* Remove any support for booting without /usr pre-mounted in the initrd entirely.
+  Update INITRD_INTERFACE.md accordingly.
+
+* pid1: Move to tracking of main pid/control pid of units per pidfd
+
+* pid1: support new clone3() fork-into-cgroup feature
+
+* pid1: also remove PID files of a service when the service starts, not just
+  when it exits
+
+* make us use dynamically fewer deps for containers in general purpose distros:
+  o turn into dlopen() deps:
+    - pcre2 (always)               — irrelevant on Fedora, since dep by
+                                     libselinux, but should benefit Debian
+    - libpwquality (always)        - only relevant for homed, and maybe soon
+                                     firstboot
+    - elfutils (always)
+    - p11-kit-trust (always)
+    - kmod-libs (only when called from PID 1)
+    - cryptsetup-libs (only in RootImage= handling in PID 1, but not in systemd-cryptsetup)
+    - similar: libblkid
+    - libpam (only when called from PID 1)
+    - bzip2, xz, lz4 (always — gzip and zstd should probably stay static deps the way they are,
+      since they are so basic and our defaults)
+  o move into separate libsystemd-shared-iptables.so .so
+    - iptables-libs (only used by nspawn + networkd)
+
+* seccomp: when SystemCallArchitectures=native is set then don't install any
+  other seccomp filters for any of the other archs, in order to reduce the
+  number of seccomp filters we install needlessly.
+
+* seccomp: maybe use seccomp_merge() to merge our filters per-arch if we can.
+  Apparently kernel performance is much better with fewer larger seccomp
+  filters than with more smaller seccomp filters.
+
+* systemd-path: add ESP and XBOOTLDR path. Add "private" runtime/state/cache dir enum,
+  mapping to $RUNTIME_DIRECTORY, $STATE_DIRECTORY and such
+
+* make "systemd-dissect" an official supported tool, i.e. move to /usr/bin/ and
+  provide man page. Given that we now have a tool that can generate images like
+  this, it's useful to have one that can dump contents of them, too.
+
+* All tools that support --root= should also learn --image= so that they can
+  operate on disk images directly. Specifically: bootctl, tmpfiles, sysusers,
+  systemctl, repart, journalctl, coredumpctl. (Already done: systemd-nspawn,
+  systemd-firstboot)
+
+* seccomp: by default mask x32 ABI system wide on x86-64. it's on its way out
+
+* seccomp: don't install filters for ABIs that are masked anyway for the
+  specific service
+
+* seccomp: maybe merge all filters we install into one with that libseccomp API that allows merging.
+
+* per-service credential system. Specifically: add LoadCredential= (for loading
+  cred from file), AcquireCredential= (for asking user for cred, via
+  ask-password), PassCredential= (for passing on credential systemd itself
+  got). Then, place credentials in a per-service, immutable ramfs instance (so
+  that it cannot be swapped out), destroy after use. Also pass via keyring
+  (with graceful fallback to cover for containers). Define CredentialPath= for
+  defining subdir of /run/credentials/ where to place it. Set $CREDENTIAL_PATH
+  env var for services to the result. Also pass via fd passing (optionally).
+
+* homed: add native recovery key support. use 48 lowercase modhex characters
+  (192bit), show qr code of it, include pattern expression in user record.
+
+* homed: introduce "degraded" state for home directories that weren't cleanly
+  unmounted (use xattr we add and remove on the loop back file)
+
+* homed: during login resize fs automatically towards size goal. Specifically,
+  resize to diskSize if possible, but leave a certain amount (configured by a
+  new value diskLeaveFreeSize) of space free on the backing fs.
+
+* homed: permit multiple user record signing keys to be used locally, and pick
+  the right one for signing records automatically depending on a pre-existing
+  signature
+
+* homed: add a way to "adopt" a home directory, i.e. strip foreign signatures
+  and insert a local signature instead.
+
+* homed: as an extension to the directory+subvolume backend: if located on
+  especially marked fs, then sync down password into LUKS header of that fs,
+  and always verify passwords against it too. Bootstrapping is a problem
+  though: if no one is logged in (or no other user even exists yet), how do you
+  unlock the volume in order to create the first user and add the first pw.
+
+* homed: support new FS_IOC_ADD_ENCRYPTION_KEY ioctl for setting up fscrypt
+
+* homed: maybe pre-create ~/.cache as subvol so that it can have separate quota
+  easily?
+
+* busctl: maybe expose a verb "ping" for pinging a dbus service to see if it
+  exists and responds.
+
+* when systemd-nspawn and suchlike dissect an OS image, and there are multiple
+  root partitions, do an strverscmp() on the partition label and boot
+  first. That is inspired how sd-boot figures out which kernel to boot, and
+  thus allows defining OS images which can be A/B updated and we default to the
+  newest version automatically, both in nspawn and in sd-boot
+
+* cryptsetup: support FIDO2 tokens for deriving keys (i.e. do what homed can do
+  also in plain cryptsetup)
 
 * systemd-gpt-auto should probably set x-systemd.growfs on the mounts it
   creates
 
-* homed/userdb: distuingish passwords and recovery keys in the records, since
+* homed/userdb: distinguish passwords and recovery keys in the records, since
   we probably want to use different PBKDF algorithms/settings for them:
   passwords have low entropy but recovery keys should have good entropy key
   hence we can make them quicker to work.
@@ -35,12 +184,13 @@ Features:
   - teach it to copy in unified kernel images and maybe type #1 boot loader spec entries from host
   - make it operate on loopback files, dissecting enough to find ESP to operate on
 
+* Maybe add a separate GPT partition type to the discoverable partition spec
+  for "hibernate" partitions, that are exactly like swap partitions but only
+  activated right before hibernation and thus never used for regular swapping.
+
 * by default, in systemd --user service bump the OOMAdjust to 100, as privs
   allow so that systemd survives
 
-* honour specifiers in unit files that resolve to some very basic
-  /etc/os-release data, such as ID, VERSION_ID, BUILD_ID, VARIANT_ID.
-
 * cryptsetup: allow encoding key directly in /etc/crypttab, maybe with a
   "base64:" prefix. Useful in particular for pkcs11 mode.
 
@@ -48,9 +198,10 @@ Features:
   systemd-makefs.service instead.
 
 * socket units: allow creating a udev monitor socket with ListenDevices= or so,
-  with matches, then actviate app thorugh that passing socket oveer
+  with matches, then activate app through that passing socket over
 
-* unify on openssl:
+* unify on openssl (as soon as OpenSSL 3.0 is out, and the Debian license
+  confusion is gone)
   - port sd_id128_get_machine_app_specific() over from khash
   - port resolved over from libgcrypt (DNSSEC code)
   - port journald + fsprg over from libgcrypt
@@ -72,17 +223,10 @@ Features:
   that the device paths stay the same, regardless if crypto is used or not.
 
 * systemd-repart: by default generate minimized partition tables (i.e. tables
-  that only covere the space actually used, excluding any free space at the
+  that only cover the space actually used, excluding any free space at the
   end), in order to maximize dd'ability. Requires libfdisk work, see
   https://github.com/karelzak/util-linux/issues/907
 
-* systemd-repart: optionally, allow specifiying a path to initialize new
-  partitions from, i.e. an fs image file or a source device node. This would
-  then turn systemd-repart into a simple installer: with a few .repart files
-  you could replicate the host system on another device. a full installer would
-  then be: "systemd-repart /dev/sda && bootctl install /dev/sda &&
-  systemd-firstboot --image= …"
-
 * systemd-repart: MBR partition table support. Care needs to be taken regarding
   Type=, so that partition definitions can sanely apply to both the GPT and the
   MBR case. Idea: accept syntax "Type=gpt:home mbr:0x83" for setting the types
@@ -98,26 +242,37 @@ Features:
 
 * systemd-repart: allow managing the gpt read-only partition flag + auto-mount flag
 
+* systemd-repart: allow boolean option that ensures that if existing partition
+  doesn't exist within the configured size bounds the whole command fails. This
+  is useful to implement ESP vs. XBOOTLDR schemes in installers: have one set
+  of repart files for the case where ESP is large enough and one where it isn't
+  and XBOOTLDR is added in instead.  Then apply the former first, and if it
+  fails to apply use the latter.
+
+* systemd-repart: add per-partition option to never reuse existing partition
+  and always create anew even if matching partition already exists.
+
+* systemd-repart: add per-partition option to fail if partition already exist,
+  i.e. is not added new. Similar, add option to fail if partition does not exist yet.
+
+* systemd-repart: add --size=auto for generating/resizing images of minimal
+  size, i.e. where the image file is sized exactly as large as necessary taking
+  SizeMin= into account, but not a single byte larger.
+
 * systemd-repart: allow disabling growing of specific partitions, or making
   them (think ESP: we don't ever want to grow it, since we cannot resize vfat)
 
-* systemd-repart: add specifier expansion, add especifier that refers to root
-  device node of current system, /usr device node, and matching verity, so that
-  an installer can be made a "copy" installer of the booted OS
-
 * systemd-repart: make it a static checker during early boot for existence and
   absence of other partitions for trusted boot environments
 
-* systemd-repart: when no configuration is found, exit early do not check
-  partition table, so that it is safe to run in the initrd on any system
+* userdb: allow username prefix searches in varlink API, allow realname and
+  realname substr searches in varlink API
 
-* systemd-repart: allow config of partition uuid
-
-* userdb: allow username prefix searches in varlink API
+* userdb: allow uid/gid range checks
 
 * userdb: allow existence checks
 
-* pid: activation by journal search expression
+* pid1: activation by journal search expression
 
 * when switching root from initrd to host, set the machine_id env var so that
   if the host has no machine ID set yet we continue to use the random one the
@@ -176,14 +331,11 @@ Features:
 * systemd-firstboot: teach it dissector magic, so that you can point it to some
   disk image and it will just set everything in it all behind the scenes.
 
-* systemd-firstboot: add --force mode that replaces existing configuration.
-
 * We should probably replace /var/log/README, /etc/rc.d/README with symlinks
   that are linked to these places instead of copied. After all they are
   constant vendor data.
 
-* maybe add kernel cmdline params: 1) to force first-boot mode + 2) to force
-  random seed crediting
+* maybe add kernel cmdline params: to force random seed crediting
 
 * nspawn: on cgroupsv1 issue cgroup empty handler process based on host events,
   so that we make cgroup agent logic safe
@@ -197,11 +349,9 @@ Features:
 
 * homed:
   - when user tries to log into record signed by unrecognized key, automatically add key to our chain after polkit auth
-  - hook up machined/nspawn users with a varlink user query interface
   - rollback when resize fails mid-operation
   - GNOME's side for forget key on suspend (requires rework so that lock screen runs outside of uid)
   - resize on login?
-  - fstrim on logout?
   - shrink fs on logout?
   - update LUKS password on login if we find there's a password that unlocks the JSON record but not the LUKS device.
   - create on activate?
@@ -210,8 +360,9 @@ Features:
     beefing up logind to make pam session close hook synchronous and wait until
     systemd --user is shut down.
   - logind: maybe keep a "busy fd" as long as there's a non-released session around or the user@.service
-  - maybe make automatic, read-only, time-based reflink-copies of LUKS disk images (think: time machine)
-  - distuingish destroy / remove (i.e. currently we can unregister a user, unregister+remove their home directory, but not just remove their home directory)
+  - maybe make automatic, read-only, time-based reflink-copies of LUKS disk
+    images (and btrfs snapshots of subvolumes) (think: time machine)
+  - distinguish destroy / remove (i.e. currently we can unregister a user, unregister+remove their home directory, but not just remove their home directory)
   - in systemd's PAMName= logic: query passwords with ssh-askpassword, so that we can make "loginctl set-linger" mode work
   - fingerprint authentication, pattern authentication, …
   - make sure "classic" user records can also be managed by homed
@@ -226,6 +377,9 @@ Features:
   - make slice for users configurable (requires logind rework)
   - logind: populate auto-login list bus property from PKCS#11 token
   - when determining state of a LUKS home directory, check DM suspended sysfs file
+  - introduce API for "making room", that grows/shrinks home directory
+    according to elastic parameters, discards blocks, and removes additional snapshots. Call it
+    either from UI when disk space gets low
 
 * introduce a new per-process uuid, similar to the boot id, the machine id, the
   invocation id, that is derived from process creds, specifically a hashed
@@ -802,6 +956,10 @@ Features:
 
 * teach ConditionKernelCommandLine= globs or regexes (in order to match foobar={no,0,off})
 
+* Add ConditionDirectoryNotEmpty= handle non-absoute paths as a search path or add
+  ConditionConfigSearchPathNotEmpty= or different syntax? See the discussion starting at
+  https://github.com/systemd/systemd/pull/15109#issuecomment-607740136.
+
 * BootLoaderSpec: Clarify that the kernel has to be in $BOOT. Clarify
   that the boot loader should be installed to the ESP. Define a way
   how an installer can figure out whether a BLS compliant boot loader
@@ -857,6 +1015,7 @@ Features:
     make assumptions about their slice anymore.
   - follow PropertiesChanged state more closely, to deal with quick logouts and
     relogins
+  - (optionally?) spawn seat-manager@$SEAT.service whenever a seat shows up that as CanGraphical set
 
 * journal:
   - consider introducing implicit _TTY= + _PPID= + _EUID= + _EGID= + _FSUID= + _FSGID= fields
@@ -874,7 +1033,7 @@ Features:
   - journal: add a setgid "systemd-journal" utility to invoke from libsystemd-journal, which passes fds via STDOUT and does PK access
   - journactl: support negative filtering, i.e. FOOBAR!="waldo",
     and !FOOBAR for events without FOOBAR.
-  - journal: store timestamp of journal_file_set_offline() inhe header,
+  - journal: store timestamp of journal_file_set_offline() in the header,
     so it is possible to display when the file was last synced.
   - journal-send.c, log.c: when the log socket is clogged, and we drop, count this and write a message about this when it gets unclogged again.
   - journal: find a way to allow dropping history early, based on priority, other rules
@@ -916,6 +1075,7 @@ Features:
     them via machined, and also watch containers coming and going.
     Benefit: nspawn --ephemeral would start working nicely with the journal.
   - assign MESSAGE_ID to log messages about failed services
+  - check if loop in decompress_blob_xz() is necessary
 
 * add a test if all entries in the catalog are properly formatted.
     (Adding dashes in a catalog entry currently results in the catalog entry
@@ -1131,18 +1291,15 @@ Features:
 * networkd:
    - add more keys to [Route] and [Address] sections
    - add support for more DHCPv4 options (and, longer term, other kinds of dynamic config)
-   - add proper initrd support (in particular generate .network/.link files based on /proc/cmdline)
    - add reduced [Link] support to .network files
-   - add Scope= parsing option for [Network]
    - properly handle routerless dhcp leases
    - work with non-Ethernet devices
-   - add support for more bond options
    - dhcp: do we allow configuring dhcp routes on interfaces that are not the one we got the dhcp info from?
    - the DHCP lease data (such as NTP/DNS) is still made available when
      a carrier is lost on a link. It should be removed instantly.
    - expose in the API the following bits:
-         - option 15, domain name and/or option 119, search list
-         - option 12, host name and/or option 81, fqdn
+         - option 15, domain name
+         - option 12, hostname and/or option 81, fqdn
          - option 123, 144, geolocation
          - option 252, configure http proxy (PAC/wpad)
    - provide a way to define a per-network interface default metric value
@@ -1150,11 +1307,9 @@ Features:
    - allow Name= to be specified repeatedly in the [Match] section. Maybe also
      support Name=foo*|bar*|baz ?
    - duplicate address check for static IPs (like ARPCHECK in network-scripts)
-   - allow DUID/IAID to be customized, see issue #394.
    - whenever uplink info changes, make DHCP server send out FORCERENEW
 
-* networkd-wait-online:
-   - make operstates to wait for configurable?
+* Figure out how to do unittests of networkd's state serialization
 
 * dhcp:
    - figure out how much we can increase Maximum Message Size
@@ -1179,20 +1334,14 @@ External:
    - natively watch for dbus-*.service symlinks (PENDING)
    - teach dbus to activate all services it finds in /etc/systemd/services/org-*.service
 
-* fix alsa mixer restore to not print error when no config is stored
-
 * make cryptsetup lower --iter-time
 
-* patch kernel for xattr support in /dev, /proc/, /sys?
-
 * kernel: add device_type = "fb", "fbcon" to class "graphics"
 
 * /usr/bin/service should actually show the new command line
 
 * fedora: suggest auto-restart on failure, but not on success and not on coredump. also, ask people to think about changing the start limit logic. Also point people to RestartPreventExitStatus=, SuccessExitStatus=
 
-* fedora: F20: go timer units all the way, leave cron.daily for cron
-
 * neither pkexec nor sudo initialize environ[] from the PAM environment?
 
 * fedora: update policy to declare access mode and ownership of unit files to root:root 0644, and add an rpmlint check for it
index 3342d594223c869df58edc71189c86a609d85e65..056df00de8fb8e938de7bb674521c8f33abea625 100644 (file)
@@ -302,7 +302,8 @@ shut down.
 Subject: DNSSEC mode has been turned off, as server doesn't support it
 Defined-By: systemd
 Support: %SUPPORT_URL%
-Documentation: man:systemd-resolved.service(8) resolved.conf(5)
+Documentation: man:systemd-resolved.service(8)
+Documentation: man:resolved.conf(5)
 
 The resolver service (systemd-resolved.service) has detected that the
 configured DNS server does not support DNSSEC, and DNSSEC validation has been
@@ -417,6 +418,7 @@ Note that the memory pressure might or might not have been caused by @UNIT@.
 Subject: Accepting user/group name @USER_GROUP_NAME@, which does not match strict user/group name rules.
 Defined-By: systemd
 Support: %SUPPORT_URL%
+Documentation: https://systemd.io/USER_NAMES
 
 The user/group name @USER_GROUP_NAME@ has been specified, which is accepted
 according the relaxed user/group name rules, but does not qualify under the
@@ -432,6 +434,86 @@ characters; names not valid UTF-8; names with leading or trailing whitespace;
 the strings "." or ".."; fully numeric strings, or strings beginning in a
 hyphen and otherwise fully numeric.
 
-For further details on strict and relaxed user/group name rules, see:
+-- 1b3bb94037f04bbf81028e135a12d293
+Subject: Failed to generate valid unit name from path '@MOUNT_POINT@'.
+Defined-By: systemd
+Support: %SUPPORT_URL%
+
+The following mount point path could not be converted into a valid .mount
+unit name:
+
+    @MOUNT_POINT@
+
+Typically this means that the path to the mount point is longer than allowed
+for valid unit names.
+
+systemd dynamically synthesizes .mount units for all mount points appearing on
+the system. For that a simple escaping algorithm is applied: the absolute path
+name is used, with all "/" characters replaced by "-" (the leading one is
+removed). Moreover, any non-alphanumeric characters (as well as any of ":",
+"-", "_", ".", "\") are replaced by "\xNN" where "NN" is the hexadecimal code
+of the character. Finally, ".mount" is suffixed. The resulting string must be
+under 256 characters in length to be a valid unit name. This restriction is
+made in order for all unit names to also be suitable as file names. If a mount
+point appears that — after escaping — is longer than this limit it cannot be
+mapped to a unit. In this case systemd will refrain from synthesizing a unit
+and cannot be used to manage the mount point. It will not appear in the service
+manager's unit table and thus also not be torn down safely and automatically at
+system shutdown.
+
+It is generally recommended to avoid such overly long mount point paths, or —
+if used anyway – manage them independently of systemd, i.e. establish them as
+well as tear them down automatically at system shutdown by other software.
+
+-- b480325f9c394a7b802c231e51a2752c
+Subject: Special user @OFFENDING_USER@ configured, this is not safe!
+Defined-By: systemd
+Support: %SUPPORT_URL%
+Documentation: https://systemd.io/UIDS-GIDS
+
+The unit @UNIT@ is configured to use User=@OFFENDING_USER@.
+
+This is not safe. The @OFFENDING_USER@ user's main purpose on Linux-based
+operating systems is to be the owner of files that otherwise cannot be mapped
+to any local user. It's used by the NFS client and Linux user namespacing,
+among others. By running a unit's processes under the identity of this user
+they might possibly get read and even write access to such files that cannot
+otherwise be mapped.
+
+It is strongly recommended to avoid running services under this user identity,
+in particular on systems using NFS or running containers. Allocate a user ID
+specific to this service, either statically via systemd-sysusers or dynamically
+via the DynamicUser= service setting.
+
+-- 1c0454c1bd2241e0ac6fefb4bc631433
+Subject: systemd-udev-settle.service is deprecated.
+Defined-By: systemd
+Support: %SUPPORT_URL%
+
+Usage of the systemd service unit systemd-udev-settle.service is deprecated. It
+inserts artificial delays into the boot process without providing the
+guarantees other subsystems traditionally assumed it provides. Relying on this
+service is racy, and it is generally a bug to make use of it and depend on it.
+
+Traditionally, this service's job was to wait until all devices a system
+possesses have been fully probed and initialized, delaying boot until this
+phase is completed. However, today's systems and hardware generally don't work
+this way anymore, hardware today may show up any time and take any time to be
+probed and initialized. Thus, in the general case, it's no longer possible to
+correctly delay boot until "all devices" have been processed, as it is not
+clear what "all devices" means and when they have been found. This is in
+particular the case if USB hardware or network-attached hardware is used.
+
+Modern software that requires some specific hardware (such as a network device
+or block device) to operate should only wait for the specific devices it needs
+to show up, and otherwise operate asynchronously initializing devices as they
+appear during boot and during runtime without delaying the boot process.
+
+It is a defect of the software in question if it doesn't work this way, and
+still pulls systemd-udev-settle.service into the boot process.
+
+Please file a bug report against the following units, with a request for it to
+be updated to operate in a hotplug fashion without depending on
+systemd-udev-settle.service:
 
-https://systemd.io/USER_NAMES
+    @OFFENDING_UNITS@
index 1a6c2546e67d30b2b7e700d1ddbf229ea4b0a675..f82bab8fc34eb40b5b536c1bf0e3edccebbd0d22 100644 (file)
@@ -298,7 +298,8 @@ Maszyna wirtualna @NAME@ (PID prowadzący @LEADER@) została wyłączona.
 Subject: Wyłączono tryb DNSSEC, ponieważ serwer go nie obsługuje
 Defined-By: systemd
 Support: %SUPPORT_URL%
-Documentation: man:systemd-resolved.service(8) resolved.conf(5)
+Documentation: man:systemd-resolved.service(8)
+Documentation: man:resolved.conf(5)
 
 Usługa resolver (systemd-resolved.service) wykryła, że skonfigurowany serwer
 DNS nie obsługuje DNSSEC, w wyniku czego walidacja DNSSEC została wyłączona.
@@ -411,3 +412,115 @@ i jądro wymusiło zakończenie jego działania.
 
 Proszę zauważyć, że brak pamięci mógł nie zostać spowodowany
 przez jednostkę @UNIT@.
+
+-- b61fdac612e94b9182285b998843061f
+Subject: Przyjmowanie nazwy użytkownika/grupy @USER_GROUP_NAME@, która nie zgadza się ze ścisłymi regułami nazw użytkowników/grup.
+Defined-By: systemd
+Support: %SUPPORT_URL%
+Documentation: https://systemd.io/USER_NAMES
+
+Podano nazwę użytkownika/grupy @USER_GROUP_NAME@, co zostało przyjęte
+zgodnie z rozluźnionymi regułami nazw użytkowników/grup, ale nie spełnia
+ścisłych reguł.
+
+Ścisłe reguły nazw użytkowników/grup zapisane jako wyrażenie regularne to:
+
+^[a-zA-Z_][a-zA-Z0-9_-]{0,30}$
+
+Rozluźnione reguły nazw użytkowników/grup przyjmują wszystkie nazwy,
+oprócz pustego ciągu, nazw zawierających bajty NUL, znaki kontrolne,
+dwukropki lub ukośniki, nazw niebędących prawidłowym tekstem UTF-8,
+nazw z początkową lub końcową spacją, ciągów „.” lub „..”, ciągów
+zawierających tylko cyfry lub ciągów zaczynających się myślnikiem
+i zawierających oprócz niego tylko cyfry.
+
+-- 1b3bb94037f04bbf81028e135a12d293
+Subject: Utworzenie prawidłowej nazwy jednostki ze ścieżki „@MOUNT_POINT@” się nie powiodło.
+Defined-By: systemd
+Support: %SUPPORT_URL%
+
+Następująca ścieżka do punktu montowania nie może zostać przekonwertowana
+na prawidłową nazwę jednostki .mount:
+
+    @MOUNT_POINT@
+
+Zwykle oznacza to, że ścieżka do punktu montowania jest dłuższa niż dozwolona
+dla prawidłowych nazw jednostek.
+
+systemd dynamicznie syntetyzuje jednostki .mount dla wszystkich punktów
+montowania pojawiających się na komputerze. Stosowany do tego jest prosty
+algorytm: używana jest bezwzględna nazwa ścieżki ze wszystkimi znakami „/”
+zastąpionymi znakami „-” (początkowy jest usuwany). Co więcej, wszystkie
+niealfanumeryczne znaki (a także „:”, „-”, „_”, „.”, „\”) są zastępowane
+„\xNN”, gdzie „NN” jest szesnastkowym kodem znaki. Na końcu dołączany jest
+przyrostek „.mount”. Wynikowy ciąg musi mieć poniżej 256 znaków długości,
+aby był prawidłową nazwą jednostki. To ograniczenie obowiązuje, aby wszystkie
+nazwy jednostek mogły być używane także jako nazwy plików. Jeśli punkt
+montowania — po zastosowaniu algorytmu — jest dłuższy niż to ograniczenie,
+to nie może zostać zmapowany do jednostki. W takim przypadku systemd nie
+zsyntetyzuje jednostki i nie może być używany do zarządzania punktem
+montowania. Nie pojawi się w tabeli jednostek menedżera usług, przez co
+nie zostanie także bezpiecznie i automatycznie zdjęty podczas wyłączania
+komputera.
+
+Zasadniczo zalecane jest unikanie takich za długich ścieżek do punktów
+montowania, a jeśli są używane mimo to, zarządzanie nimi niezależnie
+od systemd, tzn. stawianie ich i automatyczne zdejmowanie podczas
+wyłączania komputera przez inne oprogramowanie.
+
+-- b480325f9c394a7b802c231e51a2752c
+Subject: Skonfigurowany jest szczególny użytkownik @OFFENDING_USER@, jest to niebezpieczne!
+Defined-By: systemd
+Support: %SUPPORT_URL%
+Documentation: https://systemd.io/UIDS-GIDS
+
+Jednostka @UNIT@ jest skonfigurowana do używania User=@OFFENDING_USER@.
+
+Nie jest to bezpieczne. Głównym zastosowaniem użytkownika @OFFENDING_USER@
+w linuksowych systemach operacyjnych jest bycie właścicielem plików, których
+nie można w inny sposób zmapować do żadnego lokalnego użytkownika. Jest
+używany między innymi przez klienta NFS i przestrzenie nazw użytkowników
+Linuksa. Wykonywanie procesów jednostki pod tożsamością tego użytkownika
+może spowodować, że będą one miały dostęp do odczytu, a może nawet do zapisu,
+plików, których nie można zmapować w inny sposób.
+
+Mocno zalecane jest unikanie wykonywania usług pod tą tożsamością użytkownika,
+zwłaszcza na komputerach używających NFS lub mających kontenery. Należy
+przydzielić identyfikator użytkownika dla tej konkretnej usługi, statycznie
+przez systemd-sysusers lub dynamicznie przez ustawienie usługi DynamicUser=.
+
+-- 1c0454c1bd2241e0ac6fefb4bc631433
+Subject: Usługa systemd-udev-settle.service jest przestarzała.
+Defined-By: systemd
+Support: %SUPPORT_URL%
+
+Użycie jednostki usługi systemd „systemd-udev-settle.service” jest
+przestarzałe. Wstawia ona sztuczne opóźnienie do procesu uruchamiania
+bez dostarczania gwarancji, które były oczekiwane przez pozostałe
+podsystemy. Korzystanie z tej usługi może prowadzić do hazardów,
+i zasadniczo jest błędem.
+
+W przeszłości zadaniem tej usługi było oczekiwanie, aż wszystkie urządzenia
+komputera zostaną w pełni wykryte i zainicjowane, opóźniając uruchamianie
+do ukończenia tego etapu. Jednakże, współczesne komputery i urządzenia
+na ogół nie działają już w ten sposób, tylko mogą pojawić się w dowolnej
+chwili i zająć dowolny czas na wykrycie i inicjację. Z tego powodu,
+w ogólnym przypadku, nie jest już możliwe poprawne opóźnienie uruchamiania
+do przetworzenia „wszystkich urządzeń”, ponieważ nie jest jasne, co znaczy
+„wszystkie urządzenia” i kiedy zostały odnalezione. Dotyczy to zwłaszcza
+urządzeń podłączonych przez USB lub sieć.
+
+Nowoczesne oprogramowanie wymagające określonego sprzętu (takiego jak
+urządzenie sieciowe lub urządzenie blokowe) do działania powinno oczekiwać
+tylko na pojawienie się danego urządzenia, a w przeciwnym razie działać
+asynchronicznie, inicjując urządzenia, kiedy te pojawiają się w trakcie
+uruchamiania i w trakcie działania systemu bez opóźniania procesu uruchamiania.
+
+Jest to wada danego oprogramowania, jeśli nie działa ono w ten sposób
+i nadal wciąga usługę systemd-udev-settle.service do procesu uruchamiania.
+
+Prosimy zgłosić błąd w następujących jednostkach z prośbą
+o ich aktualizację tak, aby działały w sposób dynamiczny
+bez zależności od usługi systemd-udev-settle.service:
+
+    @OFFENDING_UNITS@
diff --git a/coccinelle/set_ensure_put.cocci b/coccinelle/set_ensure_put.cocci
new file mode 100644 (file)
index 0000000..92d7970
--- /dev/null
@@ -0,0 +1,18 @@
+@@
+local idexpression r;
+expression p, k, x;
+@@
+- r = set_ensure_allocated(&p, k);
+- if (r < 0)
+-   return ...;
+- r = set_put(p, x);
++ r = set_ensure_put(&p, k, x);
+@@
+local idexpression r;
+expression p, k, x;
+@@
+- r = set_ensure_allocated(p, k);
+- if (r < 0)
+-   return ...;
+- r = set_put(*p, x);
++ r = set_ensure_put(p, k, x);
index 7901da3652ce7bb4c9d023e2ffaf7cb84b15ac5d..0868184c5d41edbe101356b5188ccff639fd64f0 100644 (file)
@@ -1,6 +1,18 @@
 @@
-/* Avoid running this transformation on the strempty function itself */
-position p : script:python() { p[0].current_element != "strempty" };
+/* Avoid running this transformation on the strempty function itself and
+ * on the "make_expression" macro in src/libsystemd/sd-bus/bus-convenience.c.
+ * As Coccinelle's Location object doesn't support macro "detection", use
+ * a pretty horrifying combo of specifying a file and a special "something_else"
+ * position element, which is, apparently, the default value of
+ * "current_element" before it's set (according to the source code), thus
+ * matching any "top level" position, including macros. Let's hope we never
+ * introduce a function called "something_else"...
+ */
+position p : script:python() {
+        not (p[0].current_element == "strempty" or
+                (p[0].file == "src/libsystemd/sd-bus/bus-convenience.c" and
+                        p[0].current_element == "something_else"))
+};
 expression s;
 @@
 (
index aff203b5906e215c25b858509a2376c8d60835a4..83ddf28fdd8770a87abc9b1445b46af12547b8bd 100644 (file)
@@ -101,9 +101,9 @@ Here's an example walkthrough of how this all fits together.
 
 6. If this boot also fails, on the next boot the boot loader will see the
    tag `+0-3`, i.e. the counter reached zero. At this point the entry will be
-   considered "bad", and ordered to the end of the list of entries. The next
-   newest boot entry is now tried, i.e. the system automatically reverted back
-   to an earlier version.
+   considered "bad", and ordered to the beginning of the list of entries. The
+   next newest boot entry is now tried, i.e. the system automatically reverted
+   back to an earlier version.
 
 The above describes the walkthrough when the selected boot entry continuously
 fails. Let's have a look at an alternative ending to this walkthrough. In this
index 514b8cd11afd529cd39028ebfd777eff1a004537..803ba5440faffc6a3a76120fa9222dcd5e161505 100644 (file)
@@ -47,7 +47,7 @@ functionality. Here's why we think that it is not enough for our uses:
 
 * The various EFI implementations implement the boot order/boot item logic to different levels. Some firmware implementations do not offer a boot menu at all and instead unconditionally follow the EFI boot order, booting the first item that is working.
 * If the firmware setup is used to reset all data usually all EFI boot entries are lost, making the system entirely unbootable, as the firmware setups generally do not offer a UI to define additional boot items. By placing the menu item information on disk, it is always available, regardless if the BIOS setup data is lost.
-* Harddisk images should be moveable between machines and be bootable without requiring explicit EFI variables to be set. This also requires that the list of boot options is defined on disk, and not in EFI variables alone.
+* Harddisk images should be movable between machines and be bootable without requiring explicit EFI variables to be set. This also requires that the list of boot options is defined on disk, and not in EFI variables alone.
 * EFI is not universal yet (especially on non-x86 platforms), this specification is useful both for EFI and non-EFI boot loaders.
 * Many EFI systems disable USB support during early boot to optimize boot times, thus making keyboard input unavailable in the EFI menu. It is thus useful if the OS UI has a standardized way to discover available boot options which can be booted to.
 
@@ -95,7 +95,7 @@ Note that the `$BOOT` partition is not supposed to be exclusive territory of
 this specification. This specification only defines semantics of the `/loader/`
 directory inside the file system (see below), but it doesn't intend to define
 ownership of the whole file system exclusively. Boot loaders, firmware, and
-other software implementating this specification may choose to place other
+other software implementing this specification may choose to place other
 files and directories in the same file system. For example, boot loaders that
 implement this specification might install their own boot code into the `$BOOT`
 partition. On systems where `$BOOT` is the ESP this is a particularly common
index d05503bc97786e0f196b47b8d746f5ae44834977..4011f093228149afa9f184861f5b1c731dce759b 100644 (file)
@@ -131,6 +131,8 @@ If you wonder how to detect which of these three modes is currently used, use
 you are either in legacy or hybrid mode. To distinguish these two cases, run
 `statfs()` again on `/sys/fs/cgroup/unified/`. If that succeeds and reports
 `CGROUP2_SUPER_MAGIC` you are in hybrid mode, otherwise not.
+From a shell, you can use check the `Type` in `stat -f /sys/fs/cgroup` and
+`stat -f /sys/fs/cgroup/unified`.
 
 ## systemd's Unit Types
 
index 9f261474acdba3795c9770f154c714db06c0ae40..a724d663f69b66dad3fb7b8210dc17f2338a303a 100644 (file)
@@ -71,5 +71,8 @@ available functionality:
     See [Testing systemd using sanitizers](https://systemd.io/TESTING_WITH_SANITIZERS)
     for more information.
 
+16. Fossies provides [source code misspelling reports](https://fossies.org/features.html#codespell).
+    The systemd report can be found [here](https://fossies.org/linux/test/systemd-master.tar.gz/codespell.html).
+
 Access to Coverity and oss-fuzz reports is limited. Please reach out to the
 maintainers if you need access.
index c8ca8bfefcaa6e9df775359d75daaac68f2ff717..f335a1012ebe238c0a5520fd77c865a27debd1a0 100644 (file)
@@ -424,7 +424,7 @@ layout: default
 
 ## Deadlocks
 
-- Do not issue NSS requests (that includes user name and host name lookups)
+- Do not issue NSS requests (that includes user name and hostname lookups)
   from PID 1 as this might trigger deadlocks when those lookups involve
   synchronously talking to services that we would need to start up.
 
@@ -521,7 +521,7 @@ layout: default
   hence we might want to call it "big endian" right-away.
 
 - Please never use `dup()`. Use `fcntl(fd, F_DUPFD_CLOEXEC, 3)` instead. For
-  two reason: first, you want `O_CLOEXEC` set on the new `fd` (see
+  two reasons: first, you want `O_CLOEXEC` set on the new `fd` (see
   above). Second, `dup()` will happily duplicate your `fd` as 0, 1, 2,
   i.e. stdin, stdout, stderr, should those `fd`s be closed. Given the special
   semantics of those `fd`s, it's probably a good idea to avoid
index 71f9185c585408464489a6d139076ffc9006e488..a36d2edc72a7d97a1e3899f2486beb9361677fba 100644 (file)
@@ -121,6 +121,16 @@ manager, please consider supporting the following interfaces.
    `container_ttys=pts/7 pts/8 pts/14` it will spawn three additional login
    gettys on ptys 7, 8, and 14.
 
+4. To allow applications to detect the OS version and other metadata of the host
+   running the container manager, if this is considered desirable, please parse
+   the host's `/etc/os-release` and set a `$container_host_<key>=<VALUE>`
+   environment variable for the ID fields described by the [os-release
+   interface](https://www.freedesktop.org/software/systemd/man/os-release.html), eg:
+   `$container_host_id=debian`
+   `$container_host_build_id=2020-06-15`
+   `$container_host_variant_id=server`
+   `$container_host_version_id=10`
+
 ## Advanced Integration
 
 1. Consider syncing `/etc/localtime` from the host file system into the
index 0ee71b274c04b9cf2609b6e3f74bbc188c8cc0fe..7d7bb38ebd9f8773be0bf5d7c245e17628d607bd 100644 (file)
@@ -11,7 +11,7 @@ We welcome contributions from everyone. However, please follow the following gui
 ## Filing Issues
 
 * We use [GitHub Issues](https://github.com/systemd/systemd/issues) **exclusively** for tracking **bugs** and **feature** **requests** of systemd. If you are looking for help, please contact [systemd-devel mailing list](https://lists.freedesktop.org/mailman/listinfo/systemd-devel) instead.
-* We only track bugs in the **two** **most** **recently** **released** **versions** of systemd in the GitHub Issue tracker. If you are using an older version of systemd, please contact your distribution's bug tracker instead.
+* We only track bugs in the **two** **most** **recently** **released** (non-rc) **versions** of systemd in the GitHub Issue tracker. If you are using an older version of systemd, please contact your distribution's bug tracker instead (see below). See [GitHub Release Page](https://github.com/systemd/systemd/releases) for the list of most recent releases.
 * When filing an issue, specify the **systemd** **version** you are experiencing the issue with. Also, indicate which **distribution** you are using.
 * Please include an explanation how to reproduce the issue you are pointing out.
 
@@ -20,7 +20,7 @@ Following these guidelines makes it easier for us to process your issue, and ens
 ### Older downstream versions
 For older versions that are still supported by your distribution please use respective downstream tracker:
 * **Fedora** - [bugzilla](https://bugzilla.redhat.com/enter_bug.cgi?product=Fedora&component=systemd)
-* **RHEL-7/CentOS-7** - [bugzilla](https://bugzilla.redhat.com/enter_bug.cgi?product=Red%20Hat%20Enterprise%20Linux%207&component=systemd) or [systemd-rhel github](https://github.com/lnykryn/systemd-rhel/issues)
+* **RHEL/CentOS** - [bugzilla](https://bugzilla.redhat.com/) or [systemd-rhel github](https://github.com/systemd-rhel/)
 * **Debian** - [bugs.debian.org](https://bugs.debian.org/cgi-bin/pkgreport.cgi?pkg=systemd)
 
 ## Security vulnerability reports
diff --git a/docs/CONVERTING_TO_HOMED.md b/docs/CONVERTING_TO_HOMED.md
new file mode 100644 (file)
index 0000000..78b6c61
--- /dev/null
@@ -0,0 +1,135 @@
+---
+title: Converting Existing Users to systemd-homed
+category: Users, Groups and Home Directories
+layout: default
+---
+
+# Converting Existing Users to systemd-homed managed Users
+
+Traditionally on most Linux distributions, regular (human) users are managed
+via entries in `/etc/passwd`, `/etc/shadow`, `/etc/group` and
+`/etc/gshadow`. With the advent of
+[`systemd-homed`](https://www.freedesktop.org/software/systemd/man/systemd-homed.service.html)
+it might be desirable to convert an existing, traditional user account to a
+`systemd-homed` managed one. Below is a brief guide how to do that.
+
+Before continuing, please read up on these basic concepts:
+
+* [Home Directories](https://systemd.io/HOME_DIRECTORY)
+* [JSON User Records](https://systemd.io/USER_RECORD)
+* [JSON Group Records](https://systemd.io/GROUP_RECORD)
+* [User/Group Record Lookup API via Varlink](https://systemd.io/USER_GROUP_API)
+
+## Caveat
+
+This is a manual process, and possibly a bit fragile. Hence, do this at your
+own risk, read up beforehand, and make a backup first. You know what's at
+stake: your own home directory, i.e. all your personal data.
+
+## Step-By-Step
+
+Here's the step-by-step guide:
+
+0. Preparations: make sure you run a distribution that has `systemd-homed`
+   enabled and properly set up, including the necessary PAM and NSS
+   configuration updates. Make sure you have enough disk space in `/home/` for
+   a (temporary) second copy of your home directory. Make sure to backup your
+   home directory. Make sure to log out of your user account fully. Then log in
+   as root on the console.
+
+1. Rename your existing home directory to something safe. Let's say your user
+   ID is `foobar`. Then do:
+
+    ```
+    mv /home/foobar /home/foobar.saved
+    ```
+
+2. Have a look at your existing user record, as stored in `/etc/passwd` and
+   related files. We want to use the same data for the new record, hence it's good
+   looking at the old data. Use commands such as:
+
+    ```
+    getent passwd foobar
+    getent shadow foobar
+    ```
+
+   This will tell you the `/etc/passwd` and `/etc/shadow` entries for your
+   user. For details about the fields, see the respective man pages
+   [passwd(5)](http://man7.org/linux/man-pages/man5/passwd.5.html) and
+   [shadow(5)](http://man7.org/linux/man-pages/man5/shadow.5.html).
+
+   The fourth field in the `getent passwd foobar` output tells you the GID of
+   your user's main group. Depending on your distribution it's a group private
+   to the user, or a group shared by most local, regular users. Let's say the
+   GID reported is 1000, let's then query its details:
+
+    ```
+    getent group 1000
+    ```
+
+   This will tell you the name of that group. If the name is the same as your
+   user name your distribution apparently provided you with a private group for
+   your user. If it doesn't match (and is something like `users`) it apparently
+   didn't. Note that `systemd-homed` will always manage a private group for
+   each user under the same name, hence if your distribution is one of the
+   latter kind, then there's a (minor) mismatch in structure when converting.
+
+   Save the information reported by these three commands somewhere, for later
+   reference.
+
+3. Now edit your `/etc/passwd` file and remove your existing record
+   (i.e. delete a single line, the one of your user's account, leaving all
+   other lines unmodified). Similar for `/etc/shadow`, `/etc/group` (in case
+   you have a private group for your user) and `/etc/gshadow`. Most
+   distributions provide you with a tool for that, that adds safe
+   synchronization for these changes: `vipw`, `vipw -s`, `vigr` and `vigr -s`.
+
+4. At this point the old user account vanished, while the home directory still
+   exists safely under the `/home/foobar.saved` name. Let's now create a new
+   account with `systemd-homed`, using the same username and UID as before:
+
+    ```
+    homectl create foobar --uid=$UID --real-name=$GECOS
+    ```
+
+   In this command line, replace `$UID` by the UID you previously used,
+   i.e. the third field of the `getent passwd foobar` output above. Similar,
+   replace `$GECOS` by the GECOS field of your old account, i.e the fifth field
+   of the old output. If your distribution traditionally does not assign a
+   private group to regular user groups, then consider adding `--member-of=`
+   with the group name to get a modicum of compatibility with the status quo
+   ante: this way your new user account will still not have the old primary
+   group as new primary group, but will have it as auxiliary group.
+
+   Consider reading through the
+   [homectl(1)](https://www.freedesktop.org/software/systemd/man/homectl.html)
+   manual page at this point, maybe there are a couple of other settings you
+   want to set for your new account. In particular, look at `--storage=` and
+   `--disk-size=`, in order to change how your home directory shall be stored
+   (the default `luks` storage is recommended).
+
+5. Your new user account exists now, but it has an empty home directory. Let's
+   now migrate your old home directory into it. For that let's mount the new
+   home directory temporarily and copy the data in.
+
+    ```
+    homectl with foobar -- rsync -aHAXv --remove-source-files /home/foobar.saved/ .
+    ```
+
+   This mounts the home directory of the user, and then runs the specified
+   `rsync` command which copies the contents of the old home directory into the
+   new. The new home directory is the working directory of the invoked `rsync`
+   process. We are invoking this command as root, hence the `rsync` runs as
+   root too. When the `rsync` command completes the home directory is
+   automatically unmounted again. Since we used `--remove-source-files` all files
+   copied are removed from the old home directory as the copy progresses. After
+   the command completes the old home directory should be empty. Let's remove
+   it hence:
+
+    ```
+    rmdir /home/foobar.saved
+    ```
+
+And that's it, we are done already. You can log out now and should be able to
+log in under your user account as usual, but now with `systemd-homed` managing
+your home directory.
diff --git a/docs/DESKTOP_ENVIRONMENTS.md b/docs/DESKTOP_ENVIRONMENTS.md
new file mode 100644 (file)
index 0000000..9ae1aef
--- /dev/null
@@ -0,0 +1,117 @@
+---
+title: Desktop Environment Integration
+category: Concepts
+layout: default
+---
+
+# Desktop Environments
+
+NOTE: This document is a work-in-progress.
+
+## Single Graphical Session
+
+systemd only supports running one graphical session per user at a time.
+While this might not have always been the case historically, having multiple
+sessions for one user running at the same time is problematic.
+The DBus session bus is shared between all the logins, and services that are
+started must be implicitly assigned to the user's current graphical session.
+
+In principle it is possible to run a single graphical session across multiple
+logind seats, and this could be a way to use more than one display per user.
+When a user logs in to a second seat, the seat resources could be assigned
+to the existing session, allowing the graphical environment to present it
+is a single seat.
+Currently nothing like this is supported or even planned.
+
+## Pre-defined systemd units
+
+[`systemd.special(7)`](https://www.freedesktop.org/software/systemd/man/systemd.special.html)
+defines the `graphical-session.target` and `graphical-session-pre.target` to
+allow cross-desktop integration. Furthermore, systemd defines the three base
+slices `background`, `app` and `session`.
+All units should be placed into one of these slices depending on their purposes:
+
+ * `session.slice`: Contains only processes essential to run the user's graphical session
+ * `app.slice`: Contains all normal applications that the user is running
+ * `background.slice`: Useful for low-priority background tasks
+
+The purpose of this grouping is to assign different priorities to the
+applications.
+This could e.g. mean reserving memory to session processes,
+preferentially killing background tasks in out-of-memory situations
+or assigning different memory/CPU/IO priorities to ensure that the session
+runs smoothly under load.
+
+TODO: Will there be a default to place units into e.g. `app.slice` by default
+rather than the root slice?
+
+## XDG standardization for applications
+
+To ensure cross-desktop compatibility and encourage sharing of good practices,
+desktop environments should adhere to the following conventions:
+
+ * Application units should follow the scheme `app[-<launcher>]-<ApplicationID>[@<RANDOM>].service`
+ or `app[-<launcher>]-<ApplicationID>-<RANDOM>.scope`
+   e.g:
+    - `app-gnome-org.gnome.Evince@12345.service`
+    - `app-flatpak-org.telegram.desktop@12345.service`
+    - `app-KDE-org.kde.okular@12345.service`
+    - `app-org.kde.amarok.service`
+    - `app-org.gnome.Evince-12345.scope`
+ * Using `.service` units instead of `.scope` units, i.e. allowing systemd to
+   start the process on behalf of the caller,
+   instead of the caller starting the process and letting systemd know about it,
+   is encouraged.
+ * The RANDOM should be a string of random characters to ensure that multiple instances
+ of the application can be launched.
+ It can be omitted in the case of a non-transient application services which can ensure
+ multiple instances are not spawned, such as a DBus activated application.
+ * If no application ID is available, the launcher should generate a reasonable
+   name when possible (e.g. using `basename(argv[0])`). This name must not
+   contain a `-` character.
+
+This has the following advantages:
+ * Using the `app-<launcher>-` prefix means that the unit defaults can be
+   adjusted using desktop environment specific drop-in files.
+ * The application ID can be retrieved by stripping the prefix and postfix.
+   This in turn should map to the corresponding `.desktop` file when available
+
+TODO: Define the name of slices that should be used.
+This could be `app-<launcher>-<ApplicationID>-<RANDOM>.slice`.
+
+TODO: Does it really make sense to insert the `<launcher>`? In GNOME I am
+currently using a drop-in to configure `BindTo=graphical-session.target`,
+`CollectMode=inactive-or-failed` and `TimeoutSec=5s`. I feel that such a
+policy makes sense, but it may make much more sense to just define a
+global default for all (graphical) applications.
+
+ * Should application lifetime be bound to the session?
+ * May the user have applications that do not belong to the graphical session (e.g. launched from SSH)?
+ * Could we maybe add a default `app-.service.d` drop-in configuration?
+
+## XDG autostart integration
+
+To allow XDG autostart integration, systemd ships a cross-desktop generator
+to create appropriate units for the autostart directory
+(`systemd-xdg-autostart-generator`).
+Desktop Environments can opt-in to using this by starting
+`xdg-desktop-autostart.target`. The systemd generator correctly handles
+`OnlyShowIn=` and `NotShowin=`. It also handles the KDE and GNOME specific
+`X-KDE-autostart-condition=` and `AutostartCondition=` by using desktop
+environment provided binaries in an `ExecCondition=` line.
+
+However, this generator is somewhat limited in what it supports. For example,
+all generated units will have `After=graphical-session.target` set on them,
+it may therefore not be useful to start session services.
+
+Desktop files can be marked to be explicitly excluded from the generator using the line
+`X-systemd-skip=true`. This should be set if an application provides its own
+systemd service file for startup.
+
+## Startup and shutdown best practices
+
+Question here are:
+
+ * Are there strong opinions on how the session-leader process should watch the user's session units?
+ * Should systemd/logind/… provide an integrated way to define a session in terms of a running *user* unit?
+ * Is having `gnome-session-shutdown.target` that is run with `replace-irreversibly` considered a good practice?
index fec40b1e9ca7528f796c2023137d120697e100c0..d6f5126ac2d1d35e03f4a0c3b34d37cc30eb8040 100644 (file)
@@ -145,7 +145,7 @@ systemd-udevd:
   boot loader menu through EFI a file `/run/systemd/reboot-to-boot-loader-menu`
   is created whenever this is requested. The file contains the requested boot
   loader menu timeout in µs, formatted in ASCII decimals, or zero in case no
-  time-out is requested. This file may be checked for by services run during
+  timeout is requested. This file may be checked for by services run during
   system shutdown in order to request the appropriate operation from the boot
   loader in an alternative fashion.
 
index 8f98b49c27c915fc43daf8510e4dfc14dc4d6794..2ea0b73a365173f29cd379e5b32230a42926ec0c 100644 (file)
@@ -1,6 +1,6 @@
 ---
 title: JSON Group Records
-category: Interfaces
+category: Users, Groups and Home Directories
 layout: default
 ---
 
index c0516b5c624affe14f98df38fdf378766cc1a091..990f78c9eb43856f99ec161a135ca2e4fa0634aa 100644 (file)
@@ -36,9 +36,12 @@ building clean OS images from an upstream distribution in combination with a
 fresh build of the project in the local working directory. To make use of this,
 please acquire `mkosi` from https://github.com/systemd/mkosi first, unless your
 distribution has packaged it already and you can get it from there. After the
-tool is installed it is sufficient to type `mkosi` in the systemd project
-directory to generate a disk image `image.raw` you can boot either in
-`systemd-nspawn` or in an UEFI-capable VM:
+tool is installed, symlink the settings file for your distribution of choice from
+.mkosi/ to mkosi.default in the project root directory (note that the package
+manager for this distro needs to be installed on your host system). After doing
+that, it is sufficient to type `mkosi` in the systemd project directory to
+generate a disk image `image.raw` you can boot either in `systemd-nspawn` or in
+an UEFI-capable VM:
 
 ```
 # systemd-nspawn -bi image.raw
@@ -72,22 +75,23 @@ Putting this all together, here's a series of commands for preparing a patch
 for systemd (this example is for Fedora):
 
 ```sh
-$ sudo dnf builddep systemd            # install build dependencies
-$ sudo dnf install mkosi               # install tool to quickly build images
+$ sudo dnf builddep systemd               # install build dependencies
+$ sudo dnf install mkosi                  # install tool to quickly build images
 $ git clone https://github.com/systemd/systemd.git
 $ cd systemd
-$ vim src/core/main.c                  # or wherever you'd like to make your changes
-$ meson build                          # configure the build
-$ ninja -C build                       # build it locally, see if everything compiles fine
-$ ninja -C build test                  # run some simple regression tests
-$ (umask 077; echo 123 > mkosi.rootpw) # set root password used by mkosi
-$ sudo mkosi                           # build a test image
-$ sudo systemd-nspawn -bi image.raw    # boot up the test image
-$ git add -p                           # interactively put together your patch
-$ git commit                           # commit it
+$ vim src/core/main.c                     # or wherever you'd like to make your changes
+$ meson build                             # configure the build
+$ ninja -C build                          # build it locally, see if everything compiles fine
+$ ninja -C build test                     # run some simple regression tests
+$ ln -s .mkosi/mkosi.fedora mkosi.default # Configure mkosi to build a fedora image
+$ (umask 077; echo 123 > mkosi.rootpw)    # set root password used by mkosi
+$ sudo mkosi                              # build a test image
+$ sudo systemd-nspawn -bi image.raw       # boot up the test image
+$ git add -p                              # interactively put together your patch
+$ git commit                              # commit it
 $ git push REMOTE HEAD:refs/heads/BRANCH
-                                       # where REMOTE is your "fork" on GitHub
-                                       # and BRANCH is a branch name.
+                                          # where REMOTE is your "fork" on GitHub
+                                          # and BRANCH is a branch name.
 ```
 
 And after that, head over to your repo on GitHub and click "Compare & pull request"
@@ -98,7 +102,7 @@ Happy hacking!
 ## Fuzzers
 
 systemd includes fuzzers in `src/fuzz/` that use libFuzzer and are automatically
-run by [OSS-Fuzz](https://github.com/google/oss-fuzz) and [Fuzzit](https://fuzzit.dev) with sanitizers.
+run by [OSS-Fuzz](https://github.com/google/oss-fuzz) with sanitizers.
 To add a fuzz target, create a new `src/fuzz/fuzz-foo.c` file with a `LLVMFuzzerTestOneInput`
 function and add it to the list in `src/fuzz/meson.build`.
 
@@ -118,10 +122,6 @@ python infra/helper.py build_fuzzers --sanitizer memory systemd ../systemd
 python infra/helper.py run_fuzzer systemd fuzz-foo
 ```
 
-When you add a new target you should also add the target on [Fuzzit](https://app.fuzzit.dev/admin/RxqRpGNXquIvqrmp4iJS/dashboard)
- (Please ask someone with permissions). One the target is configured on Fuzzit you need to add it to
- `travis-ci/managers/fuzzit.sh` so the new target will run sanity tests on every pull-request and periodic fuzzing jobs.
-
 If you find a bug that impacts the security of systemd, please follow the
 guidance in [CONTRIBUTING.md](CONTRIBUTING.md) on how to report a security vulnerability.
 
index 73c2359ff89012c8ebbb878ea0c5f46404a7f0f6..a3eabb7e635f87c88faa9e44cbb09340939cfa1b 100644 (file)
@@ -1,6 +1,6 @@
 ---
 title: Home Directories
-category: Concepts
+category: Users, Groups and Home Directories
 layout: default
 ---
 
@@ -125,7 +125,7 @@ medium. (Moreover it allows to embed additional partitions later on, for
 example for allowing a multi-purpose USB stick that contains both a home
 directory and a generic storage volume.)
 
-Rationale for including the encrypted user record in the the LUKS2 header:
+Rationale for including the encrypted user record in the LUKS2 header:
 Linux kernel file system implementations are generally not robust towards
 maliciously formatted file systems; there's a good chance that file system
 images can be used as attack vectors, exploiting the kernel. Thus it is
@@ -147,7 +147,7 @@ directory-based storage mechanisms (`directory`, `subvolume` and `fscrypt`)
 this is a bind mount, in case of `cifs` this is a CIFS network mount, and in
 case of the LUKS2 backend a regular block device mount of the file system
 contained in the LUKS2 image. By requiring a mount for all cases (even for
-those that already are a directory) a clear logic is defined to distuingish
+those that already are a directory) a clear logic is defined to distinguish
 active and inactive home directories, so that the directories become
 inaccessible under their regular path the instant they are
 deactivated. Moreover, the `nosuid`, `nodev` and `noexec` flags configured in
@@ -168,6 +168,10 @@ If the UID assigned to a user does not match the owner of the home directory in
 the file system, the home directory is automatically and recursively `chown()`ed
 to the correct UID.
 
-Depending on the `discard` setting of the user record either the backing
+Depending on the `luksDiscard` setting of the user record either the backing
 loopback file is `fallocate()`ed during activation, or the mounted file system
 is `FITRIM`ed after mounting, to ensure the setting is correctly enforced.
+
+When deactivating a home directory, the file system or block device is trimmed
+or extended as configured in the `luksOfflineDiscard` setting of the user
+record.
index 8985f2761c8faa8c06aba80ea14527f424a3c74e..e59bbcce15c24283855f54ede139c71fe599846e 100644 (file)
@@ -36,7 +36,7 @@ interfaces are currently used by dracut and the ArchLinux initrds.
   optionally followed (in `argv[2]`, `argv[3]`, … systemd's original command
   line options, for example `--log-level=` and similar.
 
-* Storage daemons run from the initrd should follow the the guide on [systemd
+* Storage daemons run from the initrd should follow the guide on [systemd
   and Storage Daemons for the Root File
   System](https://systemd.io/ROOT_STORAGE_DAEMONS) to survive properly from the
   boot initrd all the way to the point where systemd jumps back into the initrd
diff --git a/docs/JOURNAL_FILE_FORMAT.md b/docs/JOURNAL_FILE_FORMAT.md
new file mode 100644 (file)
index 0000000..523ed62
--- /dev/null
@@ -0,0 +1,694 @@
+---
+title: Journal File Format
+category: Interfaces
+layout: default
+---
+
+# Journal File Format
+
+_Note that this document describes the binary on-disk format of journals
+only. For interfacing with web technologies there's the [Journal JSON
+Format](http://www.freedesktop.org/wiki/Software/systemd/json). For transfer
+of journal data across the network there's the [Journal Export
+Format](http://www.freedesktop.org/wiki/Software/systemd/export)._
+
+The systemd journal stores log data in a binary format with several features:
+
+* Fully indexed by all fields
+* Can store binary data, up to 2^64-1 in size
+* Seekable
+* Primarily append-based, hence robust to corruption
+* Support for in-line compression
+* Support for in-line Forward Secure Sealing
+
+This document explains the basic structure of the file format on disk. We are
+making this available primarily to allow review and provide documentation. Note
+that the actual implementation in the [systemd
+codebase](https://github.com/systemd/systemd/blob/master/src/journal/) is the
+only ultimately authoritative description of the format, so if this document
+and the code disagree, the code is right. That said we'll of course try hard to
+keep this document up-to-date and accurate.
+
+Instead of implementing your own reader or writer for journal files we ask you
+to use the [Journal's native C
+API](http://www.freedesktop.org/software/systemd/man/sd-journal.html) to access
+these files. It provides you with full access to the files, and will not
+withhold any data. If you find a limitation, please ping us and we might add
+some additional interfaces for you.
+
+If you need access to the raw journal data in serialized stream form without C
+API our recommendation is to make use of the [Journal Export
+Format](http://www.freedesktop.org/wiki/Software/systemd/export), which you can
+get via "journalctl -o export" or via systemd-journal-gatewayd. The export
+format is much simpler to parse, but complete and accurate. Due to its
+stream-based nature it is not indexed.
+
+_Or, to put this in other words: this low-level document is probably not what
+you want to use as base of your project. You want our [C
+API](http://www.freedesktop.org/software/systemd/man/sd-journal.html) instead!
+And if you really don't want the C API, then you want the [Journal Export
+Format](http://www.freedesktop.org/wiki/Software/systemd/export) instead! This
+document is primarily for your entertainment and education. Thank you!_
+
+This document assumes you have a basic understanding of the journal concepts,
+the properties of a journal entry and so on. If not, please go and read up,
+then come back! This is a good opportunity to read about the [basic properties
+of journal
+entries](http://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html),
+in particular realize that they may include binary non-text data (though
+usually don't), and the same field might have multiple values assigned within
+the same entry.
+
+This document describes the current format of systemd 246. The documented
+format is compatible with the format used in the first versions of the journal,
+but received various compatible and incompatible additions since.
+
+If you are wondering why the journal file format has been created in the first
+place instead of adopting an existing database implementation, please have a
+look [at this
+thread](https://lists.freedesktop.org/archives/systemd-devel/2012-October/007054.html).
+
+
+## Basics
+
+* All offsets, sizes, time values, hashes (and most other numeric values) are 64bit unsigned integers in LE format.
+* Offsets are always relative to the beginning of the file.
+* The 64bit hash function siphash24 is used for newer journal files. For older files [Jenkins lookup3](https://en.wikipedia.org/wiki/Jenkins_hash_function) is used, more specifically `jenkins_hashlittle2()` with the first 32bit integer it returns as higher 32bit part of the 64bit value, and the second one uses as lower 32bit part.
+* All structures are aligned to 64bit boundaries and padded to multiples of 64bit
+* The format is designed to be read and written via memory mapping using multiple mapped windows.
+* All time values are stored in usec since the respective epoch.
+* Wall clock time values are relative to the Unix time epoch, i.e. January 1st, 1970. (`CLOCK_REALTIME`)
+* Monotonic time values are always stored jointly with the kernel boot ID value (i.e. `/proc/sys/kernel/random/boot_id`) they belong to. They tend to be relative to the start of the boot, but aren't for containers. (`CLOCK_MONOTONIC`)
+* Randomized, unique 128bit IDs are used in various locations. These are generally UUID v4 compatible, but this is not a requirement.
+
+## General Rules
+
+If any kind of corruption is noticed by a writer it should immediately rotate
+the file and start a new one. No further writes should be attempted to the
+original file, but it should be left around so that as little data as possible
+is lost.
+
+If any kind of corruption is noticed by a reader it should try hard to handle
+this gracefully, such as skipping over the corrupted data, but allowing access
+to as much data around it as possible.
+
+A reader should verify all offsets and other data as it reads it. This includes
+checking for alignment and range of offsets in the file, especially before
+trying to read it via a memory map.
+
+A reader must interleave rotated and corrupted files as good as possible and
+present them as single stream to the user.
+
+All fields marked as "reserved" must be initialized with 0 when writing and be
+ignored on reading. They are currently not used but might be used later on.
+
+
+## Structure
+
+The file format's data structures are declared in
+[journal-def.h](https://github.com/systemd/systemd/blob/master/src/journal/journal-def.h).
+
+The file format begins with a header structure. After the header structure
+object structures follow. Objects are appended to the end as time
+progresses. Most data stored in these objects is not altered anymore after
+having been written once, with the exception of records necessary for
+indexing. When new data is appended to a file the writer first writes all new
+objects to the end of the file, and then links them up at front after that's
+done. Currently, seven different object types are known:
+
+```c
+enum {
+        OBJECT_UNUSED,
+        OBJECT_DATA,
+        OBJECT_FIELD,
+        OBJECT_ENTRY,
+        OBJECT_DATA_HASH_TABLE,
+        OBJECT_FIELD_HASH_TABLE,
+        OBJECT_ENTRY_ARRAY,
+        OBJECT_TAG,
+        _OBJECT_TYPE_MAX
+};
+```
+
+* A **DATA** object, which encapsulates the contents of one field of an entry, i.e. a string such as `_SYSTEMD_UNIT=avahi-daemon.service`, or `MESSAGE=Foobar made a booboo.` but possibly including large or binary data, and always prefixed by the field name and "=".
+* A **FIELD** object, which encapsulates a field name, i.e. a string such as `_SYSTEMD_UNIT` or `MESSAGE`, without any `=` or even value.
+* An **ENTRY** object, which binds several **DATA** objects together into a log entry.
+* A **DATA_HASH_TABLE** object, which encapsulates a hash table for finding existing **DATA** objects.
+* A **FIELD_HASH_TABLE** object, which encapsulates a hash table for finding existing **FIELD** objects.
+* An **ENTRY_ARRAY** object, which encapsulates a sorted array of offsets to entries, used for seeking by binary search.
+* A **TAG** object, consisting of an FSS sealing tag for all data from the beginning of the file or the last tag written (whichever is later).
+
+## Header
+
+The Header struct defines, well, you guessed it, the file header:
+
+```c
+_packed_ struct Header {
+        uint8_t signature[8]; /* "LPKSHHRH" */
+        le32_t compatible_flags;
+        le32_t incompatible_flags;
+        uint8_t state;
+        uint8_t reserved[7];
+        sd_id128_t file_id;
+        sd_id128_t machine_id;
+        sd_id128_t boot_id;    /* last writer */
+        sd_id128_t seqnum_id;
+        le64_t header_size;
+        le64_t arena_size;
+        le64_t data_hash_table_offset;
+        le64_t data_hash_table_size;
+        le64_t field_hash_table_offset;
+        le64_t field_hash_table_size;
+        le64_t tail_object_offset;
+        le64_t n_objects;
+        le64_t n_entries;
+        le64_t tail_entry_seqnum;
+        le64_t head_entry_seqnum;
+        le64_t entry_array_offset;
+        le64_t head_entry_realtime;
+        le64_t tail_entry_realtime;
+        le64_t tail_entry_monotonic;
+        /* Added in 187 */
+        le64_t n_data;
+        le64_t n_fields;
+        /* Added in 189 */
+        le64_t n_tags;
+        le64_t n_entry_arrays;
+        /* Added in 246 */
+        le64_t data_hash_chain_depth;
+        le64_t field_hash_chain_depth;
+};
+```
+
+The first 8 bytes of Journal files must contain the ASCII characters `LPKSHHRH`.
+
+If a writer finds that the **machine_id** of a file to write to does not match
+the machine it is running on it should immediately rotate the file and start a
+new one.
+
+When journal file is first created the **file_id** is randomly and uniquely
+initialized.
+
+When a writer opens a file it shall initialize the **boot_id** to the current
+boot id of the system.
+
+The currently used part of the file is the **header_size** plus the
+**arena_size** field of the header. If a writer needs to write to a file where
+the actual file size on disk is smaller than the reported value it shall
+immediately rotate the file and start a new one. If a writer is asked to write
+to a file with a header that is shorter than his own definition of the struct
+Header, he shall immediately rotate the file and start a new one.
+
+The **n_objects** field contains a counter for objects currently available in
+this file. As objects are appended to the end of the file this counter is
+increased.
+
+The first object in the file starts immediately after the header. The last
+object in the file is at the offset **tail_object_offset**, which may be 0 if
+no object is in the file yet.
+
+The **n_entries**, **n_data**, **n_fields**, **n_tags**, **n_entry_arrays** are
+counters of the objects of the specific types.
+
+**tail_entry_seqnum** and **head_entry_seqnum** contain the sequential number
+(see below) of the last or first entry in the file, respectively, or 0 if no
+entry has been written yet.
+
+**tail_entry_realtime** and **head_entry_realtime** contain the wallclock
+timestamp of the last or first entry in the file, respectively, or 0 if no
+entry has been written yet.
+
+**tail_entry_monotonic** is the monotonic timestamp of the last entry in the
+file, referring to monotonic time of the boot identified by **boot_id**.
+
+**data_hash_chain_depth** is a counter of the deepest chain in the data hash
+table, minus one. This is updated whenever a chain is found that is longer than
+the previous deepest chain found. Note that the counter is updated during hash
+table lookups, as the chains are traversed. This counter is used to determine
+when it is a good time to rotate the journal file, because hash collisions
+became too frequent.
+
+Similar, **field_hash_chain_depth** is a counter of the deepest chain in the
+field hash table, minus one.
+
+
+## Extensibility
+
+The format is supposed to be extensible in order to enable future additions of
+features. Readers should simply skip objects of unknown types as they read
+them. If a compatible feature extension is made a new bit is registered in the
+header's **compatible_flags** field. If a feature extension is used that makes
+the format incompatible a new bit is registered in the header's
+**incompatible_flags** field. Readers should check these two bit fields, if
+they find a flag they don't understand in compatible_flags they should continue
+to read the file, but if they find one in **incompatible_flags** they should
+fail, asking for an update of the software. Writers should refuse writing if
+there's an unknown bit flag in either of these fields.
+
+The file header may be extended as new features are added. The size of the file
+header is stored in the header. All header fields up to **n_data** are known to
+unconditionally exist in all revisions of the file format, all fields starting
+with **n_data** needs to be explicitly checked for via a size check, since they
+were additions after the initial release.
+
+Currently only five extensions flagged in the flags fields are known:
+
+```c
+enum {
+        HEADER_INCOMPATIBLE_COMPRESSED_XZ   = 1 << 0,
+        HEADER_INCOMPATIBLE_COMPRESSED_LZ4  = 1 << 1,
+        HEADER_INCOMPATIBLE_KEYED_HASH      = 1 << 2,
+        HEADER_INCOMPATIBLE_COMPRESSED_ZSTD = 1 << 3,
+};
+
+enum {
+        HEADER_COMPATIBLE_SEALED = 1 << 0,
+};
+```
+
+HEADER_INCOMPATIBLE_COMPRESSED_XZ indicates that the file includes DATA objects
+that are compressed using XZ. Similarly, HEADER_INCOMPATIBLE_COMPRESSED_LZ4
+indicates that the file includes DATA objects that are compressed with the LZ4
+algorithm. And HEADER_INCOMPATIBLE_COMPRESSED_ZSTD indicates that there are
+objects compressed with ZSTD.
+
+HEADER_INCOMPATIBLE_KEYED_HASH indicates that instead of the unkeyed Jenkins
+hash function the keyed siphash24 hash function is used for the two hash
+tables, see below.
+
+HEADER_COMPATIBLE_SEALED indicates that the file includes TAG objects required
+for Forward Secure Sealing.
+
+
+## Dirty Detection
+
+```c
+enum {
+        STATE_OFFLINE = 0,
+        STATE_ONLINE = 1,
+        STATE_ARCHIVED = 2,
+        _STATE_MAX
+};
+```
+
+If a file is opened for writing the **state** field should be set to
+STATE_ONLINE. If a file is closed after writing the **state** field should be
+set to STATE_OFFLINE. After a file has been rotated it should be set to
+STATE_ARCHIVED. If a writer is asked to write to a file that is not in
+STATE_OFFLINE it should immediately rotate the file and start a new one,
+without changing the file.
+
+After and before the state field is changed `fdatasync()` should be executed on
+the file to ensure the dirty state hits disk.
+
+
+## Sequence Numbers
+
+All entries carry sequence numbers that are monotonically counted up for each
+entry (starting at 1) and are unique among all files which carry the same
+**seqnum_id** field. This field is randomly generated when the journal daemon
+creates its first file. All files generated by the same journal daemon instance
+should hence carry the same seqnum_id. This should guarantee a monotonic stream
+of sequential numbers for easy interleaving even if entries are distributed
+among several files, such as the system journal and many per-user journals.
+
+
+## Concurrency
+
+The file format is designed to be usable in a simultaneous
+single-writer/multiple-reader scenario. The synchronization model is very weak
+in order to facilitate storage on the most basic of file systems (well, the
+most basic ones that provide us with `mmap()` that is), and allow good
+performance. No file locking is used. The only time where disk synchronization
+via `fdatasync()` should be enforced is after and before changing the **state**
+field in the file header (see below). It is recommended to execute a memory
+barrier after appending and initializing new objects at the end of the file,
+and before linking them up in the earlier objects.
+
+This weak synchronization model means that it is crucial that readers verify
+the structural integrity of the file as they read it and handle invalid
+structure gracefully. (Checking what you read is a pretty good idea out of
+security considerations anyway.) This specifically includes checking offset
+values, and that they point to valid objects, with valid sizes and of the type
+and hash value expected. All code must be written with the fact in mind that a
+file with inconsistent structure might just be inconsistent temporarily, and
+might become consistent later on. Payload OTOH requires less scrutiny, as it
+should only be linked up (and hence visible to readers) after it was
+successfully written to memory (though not necessarily to disk). On non-local
+file systems it is a good idea to verify the payload hashes when reading, in
+order to avoid annoyances with `mmap()` inconsistencies.
+
+Clients intending to show a live view of the journal should use `inotify()` for
+this to watch for files changes. Since file writes done via `mmap()` do not
+result in `inotify()` writers shall truncate the file to its current size after
+writing one or more entries, which results in inotify events being
+generated. Note that this is not used as a transaction scheme (it doesn't
+protect anything), but merely for triggering wakeups.
+
+Note that inotify will not work on network file systems if reader and writer
+reside on different hosts. Readers which detect they are run on journal files
+on a non-local file system should hence not rely on inotify for live views but
+fall back to simple time based polling of the files (maybe recheck every 2s).
+
+
+## Objects
+
+All objects carry a common header:
+
+```c
+enum {
+        OBJECT_COMPRESSED_XZ   = 1 << 0,
+        OBJECT_COMPRESSED_LZ4  = 1 << 1,
+        OBJECT_COMPRESSED_ZSTD = 1 << 2,
+};
+
+_packed_ struct ObjectHeader {
+        uint8_t type;
+        uint8_t flags;
+        uint8_t reserved[6];
+        le64_t size;
+        uint8_t payload[];
+};
+```
+
+The **type** field is one of the object types listed above. The **flags** field
+currently knows three flags: OBJECT_COMPRESSED_XZ, OBJECT_COMPRESSED_LZ4 and
+OBJECT_COMPRESSED_ZSTD. It is only valid for DATA objects and indicates that
+the data payload is compressed with XZ/LZ4/ZSTD. If one of the
+OBJECT_COMPRESSED_* flags is set for an object then the matching
+HEADER_INCOMPATIBLE_COMPRESSED_XZ/HEADER_INCOMPATIBLE_COMPRESSED_LZ4/HEADER_INCOMPATIBLE_COMPRESSED_ZSTD
+flag must be set for the file as well. At most one of these three bits may be
+set. The **size** field encodes the size of the object including all its
+headers and payload.
+
+
+## Data Objects
+
+```c
+_packed_ struct DataObject {
+        ObjectHeader object;
+        le64_t hash;
+        le64_t next_hash_offset;
+        le64_t next_field_offset;
+        le64_t entry_offset; /* the first array entry we store inline */
+        le64_t entry_array_offset;
+        le64_t n_entries;
+        uint8_t payload[];
+};
+```
+
+Data objects carry actual field data in the **payload[]** array, including a
+field name, a `=` and the field data. Example:
+`_SYSTEMD_UNIT=foobar.service`. The **hash** field is a hash value of the
+payload. If the `HEADER_INCOMPATIBLE_KEYED_HASH` flag is set in the file header
+this is the siphash24 hash value of the payload, keyed by the file ID as stored
+in the **file_id** field of the file header. If the flag is not set it is the
+non-keyed Jenkins hash of the payload instead. The keyed hash is preferred as
+it makes the format more robust against attackers that want to trigger hash
+collisions in the hash table.
+
+**next_hash_offset** is used to link up DATA objects in the DATA_HASH_TABLE if
+a hash collision happens (in a singly linked list, with an offset of 0
+indicating the end). **next_field_offset** is used to link up data objects with
+the same field name from the FIELD object of the field used.
+
+**entry_offset** is an offset to the first ENTRY object referring to this DATA
+object. **entry_array_offset** is an offset to an ENTRY_ARRAY object with
+offsets to other entries referencing this DATA object. Storing the offset to
+the first ENTRY object in-line is an optimization given that many DATA objects
+will be referenced from a single entry only (for example, `MESSAGE=` frequently
+includes a practically unique string). **n_entries** is a counter of the total
+number of ENTRY objects that reference this object, i.e. the sum of all
+ENTRY_ARRAYS chained up from this object, plus 1.
+
+The **payload[]** field contains the field name and date unencoded, unless
+OBJECT_COMPRESSED_XZ/OBJECT_COMPRESSED_LZ4/OBJECT_COMPRESSED_ZSTD is set in the
+`ObjectHeader`, in which case the payload is compressed with the indicated
+compression algorithm.
+
+
+## Field Objects
+
+```c
+_packed_ struct FieldObject {
+        ObjectHeader object;
+        le64_t hash;
+        le64_t next_hash_offset;
+        le64_t head_data_offset;
+        uint8_t payload[];
+};
+```
+
+Field objects are used to enumerate all possible values a certain field name
+can take in the entire journal file.
+
+The **payload[]** array contains the actual field name, without '=' or any
+field value. Example: `_SYSTEMD_UNIT`. The **hash** field is a hash value of
+the payload. As for the DATA objects, this too is either the `.file_id` keyed
+siphash24 hash of the payload, or the non-keyed Jenkins hash.
+
+**next_hash_offset** is used to link up FIELD objects in the FIELD_HASH_TABLE
+if a hash collision happens (in singly linked list, offset 0 indicating the
+end). **head_data_offset** points to the first DATA object that shares this
+field name. It is the head of a singly linked list using DATA's
+**next_field_offset** offset.
+
+
+## Entry Objects
+
+```
+_packed_ struct EntryItem {
+        le64_t object_offset;
+        le64_t hash;
+};
+
+_packed_ struct EntryObject {
+        ObjectHeader object;
+        le64_t seqnum;
+        le64_t realtime;
+        le64_t monotonic;
+        sd_id128_t boot_id;
+        le64_t xor_hash;
+        EntryItem items[];
+};
+```
+
+An ENTRY object binds several DATA objects together into one log entry, and
+includes other metadata such as various timestamps.
+
+The **seqnum** field contains the sequence number of the entry, **realtime**
+the realtime timestamp, and **monotonic** the monotonic timestamp for the boot
+identified by **boot_id**.
+
+The **xor_hash** field contains a binary XOR of the hashes of the payload of
+all DATA objects referenced by this ENTRY. This value is usable to check the
+contents of the entry, being independent of the order of the DATA objects in
+the array. Note that even for files that have the
+`HEADER_INCOMPATIBLE_KEYED_HASH` flag set (and thus siphash24 the otherwise
+used hash function) the hash function used for this field, as singular
+exception, is the Jenkins lookup3 hash function. The XOR hash value is used to
+quickly compare the contents of two entries, and to define a well-defined order
+between two entries that otherwise have the same sequence numbers and
+timestamps.
+
+The **items[]** array contains references to all DATA objects of this entry,
+plus their respective hashes (which are calculated the same way as in the DATA
+objects, i.e. keyed by the file ID).
+
+In the file ENTRY objects are written ordered monotonically by sequence
+number. For continuous parts of the file written during the same boot
+(i.e. with the same boot_id) the monotonic timestamp is monotonic too. Modulo
+wallclock time jumps (due to incorrect clocks being corrected) the realtime
+timestamps are monotonic too.
+
+
+## Hash Table Objects
+
+```c
+_packed_ struct HashItem {
+        le64_t head_hash_offset;
+        le64_t tail_hash_offset;
+};
+
+_packed_ struct HashTableObject {
+        ObjectHeader object;
+        HashItem items[];
+};
+```
+
+The structure of both DATA_HASH_TABLE and FIELD_HASH_TABLE objects are
+identical. They implement a simple hash table, which each cell containing
+offsets to the head and tail of the singly linked list of the DATA and FIELD
+objects, respectively. DATA's and FIELD's next_hash_offset field are used to
+chain up the objects. Empty cells have both offsets set to 0.
+
+Each file contains exactly one DATA_HASH_TABLE and one FIELD_HASH_TABLE
+objects. Their payload is directly referred to by the file header in the
+**data_hash_table_offset**, **data_hash_table_size**,
+**field_hash_table_offset**, **field_hash_table_size** fields. These offsets do
+_not_ point to the object headers but directly to the payloads. When a new
+journal file is created the two hash table objects need to be created right
+away as first two objects in the stream.
+
+If the hash table fill level is increasing over a certain fill level (Learning
+from Java's Hashtable for example: > 75%), the writer should rotate the file
+and create a new one.
+
+The DATA_HASH_TABLE should be sized taking into account to the maximum size the
+file is expected to grow, as configured by the administrator or disk space
+considerations. The FIELD_HASH_TABLE should be sized to a fixed size; the
+number of fields should be pretty static as it depends only on developers'
+creativity rather than runtime parameters.
+
+
+## Entry Array Objects
+
+
+```c
+_packed_ struct EntryArrayObject {
+        ObjectHeader object;
+        le64_t next_entry_array_offset;
+        le64_t items[];
+};
+```
+
+Entry Arrays are used to store a sorted array of offsets to entries. Entry
+arrays are strictly sorted by offsets on disk, and hence by their timestamps
+and sequence numbers (with some restrictions, see above).
+
+Entry Arrays are chained up. If one entry array is full another one is
+allocated and the **next_entry_array_offset** field of the old one pointed to
+it. An Entry Array with **next_entry_array_offset** set to 0 is the last in the
+list. To optimize allocation and seeking, as entry arrays are appended to a
+chain of entry arrays they should increase in size (double).
+
+Due to being monotonically ordered entry arrays may be searched with a binary
+search (bisection).
+
+One chain of entry arrays links up all entries written to the journal. The
+first entry array is referenced in the **entry_array_offset** field of the
+header.
+
+Each DATA object also references an entry array chain listing all entries
+referencing a specific DATA object. Since many DATA objects are only referenced
+by a single ENTRY the first offset of the list is stored inside the DATA object
+itself, an ENTRY_ARRAY object is only needed if it is referenced by more than
+one ENTRY.
+
+
+## Tag Object
+
+```c
+#define TAG_LENGTH (256/8)
+
+_packed_ struct TagObject {
+        ObjectHeader object;
+        le64_t seqnum;
+        le64_t epoch;
+        uint8_t tag[TAG_LENGTH]; /* SHA-256 HMAC */
+};
+```
+
+Tag objects are used to seal off the journal for alteration. In regular
+intervals a tag object is appended to the file. The tag object consists of a
+SHA-256 HMAC tag that is calculated from the objects stored in the file since
+the last tag was written, or from the beginning if no tag was written yet. The
+key for the HMAC is calculated via the externally maintained FSPRG logic for
+the epoch that is written into **epoch**. The sequence number **seqnum** is
+increased with each tag. When calculating the HMAC of objects header fields
+that are volatile are excluded (skipped). More specifically all fields that
+might validly be altered to maintain a consistent file structure (such as
+offsets to objects added later for the purpose of linked lists and suchlike)
+after an object has been written are not protected by the tag. This means a
+verifier has to independently check these fields for consistency of
+structure. For the fields excluded from the HMAC please consult the source code
+directly. A verifier should read the file from the beginning to the end, always
+calculating the HMAC for the objects it reads. Each time a tag object is
+encountered the HMAC should be verified and restarted. The tag object sequence
+numbers need to increase strictly monotonically. Tag objects themselves are
+partially protected by the HMAC (i.e. seqnum and epoch is included, the tag
+itself not).
+
+
+## Algorithms
+
+### Reading
+
+Given an offset to an entry all data fields are easily found by following the
+offsets in the data item array of the entry.
+
+Listing entries without filter is done by traversing the list of entry arrays
+starting with the headers' **entry_array_offset** field.
+
+Seeking to an entry by timestamp or sequence number (without any matches) is
+done via binary search in the entry arrays starting with the header's
+**entry_array_offset** field. Since these arrays double in size as more are
+added the time cost of seeking is O(log(n)*log(n)) if n is the number of
+entries in the file.
+
+When seeking or listing with one field match applied the DATA object of the
+match is first identified, and then its data entry array chain traversed. The
+time cost is the same as for seeks/listings with no match.
+
+If multiple matches are applied, multiple chains of entry arrays should be
+traversed in parallel. Since they all are strictly monotonically ordered by
+offset of the entries, advancing in one can be directly applied to the others,
+until an entry matching all matches is found. In the worst case seeking like
+this is O(n) where n is the number of matching entries of the "loosest" match,
+but in the common case should be much more efficient at least for the
+well-known fields, where the set of possible field values tend to be closely
+related. Checking whether an entry matches a number of matches is efficient
+since the item array of the entry contains hashes of all data fields
+referenced, and the number of data fields of an entry is generally small (<
+30).
+
+When interleaving multiple journal files seeking tends to be a frequently used
+operation, but in this case can be effectively suppressed by caching results
+from previous entries.
+
+When listing all possible values a certain field can take it is sufficient to
+look up the FIELD object and follow the chain of links to all DATA it includes.
+
+### Writing
+
+When an entry is appended to the journal for each of its data fields the data
+hash table should be checked. If the data field does not yet exist in the file
+it should be appended and added to the data hash table. When a field data
+object is added the field hash table should be checked for the field name of
+the data field, and a field object be added if necessary. After all data fields
+(and recursively all field names) of the new entry are appended and linked up
+in the hashtables the entry object should be appended and linked up too.
+
+In regular intervals a tag object should be written if sealing is enabled (see
+above). Before the file is closed a tag should be written too, to seal it off.
+
+Before writing an object, time and disk space limits should be checked and
+rotation triggered if necessary.
+
+
+## Optimizing Disk IO
+
+_A few general ideas to keep in mind:_
+
+The hash tables for looking up fields and data should be quickly in the memory
+cache and not hurt performance. All entries and entry arrays are ordered
+strictly by time on disk, and hence should expose an OK access pattern on
+rotating media, when read sequentially (which should be the most common case,
+given the nature of log data).
+
+The disk access patterns of the binary search for entries needed for seeking
+are problematic on rotating disks. This should not be a major issue though,
+since seeking should not be a frequent operation.
+
+When reading, collecting data fields for presenting entries to the user is
+problematic on rotating disks. In order to optimize these patterns the item
+array of entry objects should be sorted by disk offset before
+writing. Effectively, frequently used data objects should be in the memory
+cache quickly. Non-frequently used data objects are likely to be located
+between the previous and current entry when reading and hence should expose an
+OK access pattern. Problematic are data objects that are neither frequently nor
+infrequently referenced, which will cost seek time.
+
+And that's all there is to it.
+
+Thanks for your interest!
diff --git a/docs/PASSWORD_AGENTS.md b/docs/PASSWORD_AGENTS.md
new file mode 100644 (file)
index 0000000..75b10da
--- /dev/null
@@ -0,0 +1,40 @@
+---
+title: Password Agents
+category: Interfaces
+layout: default
+---
+
+# Password Agents
+
+systemd 12 and newer support lightweight password agents which can be used to query the user for system-level passwords or passphrases. These are passphrases that are not related to a specific user, but to some kind of hardware or service. Right now this is used exclusively for encrypted hard-disk passphrases but later on this is likely to be used to query passphrases of SSL certificates at Apache startup time as well. The basic idea is that a system component requesting a password entry can simply drop a simple .ini-style file into `/run/systemd/ask-password` which multiple different agents may watch via `inotify()`, and query the user as necessary. The answer is then sent back to the querier via an `AF_UNIX`/`SOCK_DGRAM` socket. Multiple agents might be running at the same time in which case they all should query the user and the agent which answers first wins. Right now systemd ships with the following passphrase agents:
+
+* A Plymouth agent used for querying passwords during boot-up
+* A console agent used in similar situations if Plymouth is not available
+* A GNOME agent which can be run as part of the normal user session which pops up a notification message and icon which when clicked receives the passphrase from the user. This is useful and necessary in case an encrypted system hard-disk is plugged in when the machine is already up.
+* A [`wall(1)`](http://man7.org/linux/man-pages/man1/wall.1.html) agent which sends wall messages as soon as a password shall be entered.
+* A simple tty agent which is built into "`systemctl start`" (and similar commands) and asks passwords to the user during manual startup of a service
+* A simple tty agent which can be run manually to respond to all queued passwords
+
+It is easy to write additional agents. The basic algorithm to follow looks like this:
+
+* Create an inotify watch on /run/systemd/ask-password, watch for `IN_CLOSE_WRITE|IN_MOVED_TO`
+* Ignore all events on files in that directory that do not start with "`ask.`"
+* As soon as a file named "`ask.xxxx`" shows up, read it. It's a simple `.ini` file that may be parsed with the usual parsers. The `xxxx` suffix is randomized.
+* Make sure to ignore unknown `.ini` file keys in those files, so that we can easily extend the format later on.
+* You'll find the question to ask the user in the `Message=` field in the `[Ask]` section. It is a single-line string in UTF-8, which might be internationalized (by the party that originally asks the question, not by the agent).
+* You'll find an icon name (following the XDG icon naming spec) to show next to the message in the `Icon=` field in the `[Ask]` section
+* You'll find the PID of the client asking the question in the `PID=` field in the `[Ask]` section (Before asking your question use `kill(PID, 0)` and ignore the file if this returns `ESRCH`; there's no need to show the data of this field but if you want to you may)
+* `Echo=` specifies whether the input should be obscured. If this field is missing or is `Echo=0`, the input should not be shown.
+* The socket to send the response to is configured via `Socket=` in the `[Ask]` section. It is a `AF_UNIX`/`SOCK_DGRAM` socket in the file system.
+* Ignore files where the time specified in the `NotAfter=` field in the `[Ask]` section is in the past. The time is specified in usecs, and refers to the `CLOCK_MONOTONIC` clock. If `NotAfter=` is `0`, no such check should take place.
+* Make sure to hide a password query dialog as soon as a) the `ask.xxxx` file is deleted, watch this with inotify. b) the `NotAfter=` time elapses, if it is set `!= 0`.
+* Access to the socket is restricted to privileged users. To acquire the necessary privileges to send the answer back, consider using PolicyKit. In fact, the GNOME agent we ship does that, and you may simply piggyback on that, by executing "`/usr/bin/pkexec /lib/systemd/systemd-reply-password 1 /path/to/socket`" or "`/usr/bin/pkexec /lib/systemd/systemd-reply-password 0 /path/to/socket`" and writing the password to its standard input. Use '`1`' as argument if a password was entered by the user, or '`0`' if the user canceled the request.
+* If you do not want to use PK ensure to acquire the necessary privileges in some other way and send a single datagram to the socket consisting of the password string either prefixed with "`+`" or with "`-`" depending on whether the password entry was successful or not. You may but don't have to include a final `NUL` byte in your message.
+
+Again, it is essential that you stop showing the password box/notification/status icon if the `ask.xxx` file is removed or when `NotAfter=` elapses (if it is set `!= 0`)!
+
+It may happen that multiple password entries are pending at the same time. Your agent needs to be able to deal with that. Depending on your environment you may either choose to show all outstanding passwords at the same time or instead only one and as soon as the user replied to that one go on to the next one.
+
+You may test this all with manually invoking the "`systemd-ask-password`" tool on the command line. Pass `--no-tty` to ensure the password is asked via the agent system. Note that only privileged users may use this tool (after all this is intended purely for system-level passwords).
+
+If you write a system level agent a smart way to activate it is using systemd `.path` units. This will ensure that systemd will watch the `/run/systemd/ask-password` directory and spawn the agent as soon as that directory becomes non-empty. In fact, the console, wall and Plymouth agents are started like this. If systemd is used to maintain user sessions as well you can use a similar scheme to automatically spawn your user password agent as well. (As of this moment we have not switched any DE over to use systemd for session management, however.)
index 95bfcb98d38b291e5d7828ad7c4c2185546418ec..4b138b593c1442b4d670b0a54192e6c31ceca60c 100644 (file)
@@ -87,7 +87,7 @@ And now, here's the list of (hopefully) all APIs that we have introduced with sy
 | [Boot Loader interface](https://systemd.io/BOOT_LOADER_INTERFACE) | EFI variables | yes | yes | gummiboot | yes | - | no |
 | [Service bus API](https://www.freedesktop.org/wiki/Software/systemd/dbus) | D-Bus | yes | yes | system-config-services | no | - | no |
 | [logind](https://www.freedesktop.org/wiki/Software/systemd/logind) | D-Bus | yes | yes | GNOME | no | - | no |
-| [sd-login.h API](https://www.freedesktop.org/software/systemd/man/sd-login.html) | C Library | yes | yes | GNOME, PolicyKit, ... | no | - | no |
+| [sd-login.h API](https://www.freedesktop.org/software/systemd/man/sd-login.html) | C Library | yes | yes | GNOME, polkit, ... | no | - | no |
 | [sd-daemon.h API](https://www.freedesktop.org/software/systemd/man/sd-daemon.html) | C Library or Drop-in | yes | yes | numerous | yes | - | yes |
 | [sd-id128.h API](https://www.freedesktop.org/software/systemd/man/sd-id128.html) | C Library | yes | yes | - | yes | - | no |
 | [sd-journal.h API](https://www.freedesktop.org/software/systemd/man/sd-journal.html) | C Library | yes | yes | - | maybe | - | no |
@@ -98,7 +98,7 @@ And now, here's the list of (hopefully) all APIs that we have introduced with sy
 | [Unit file format](https://www.freedesktop.org/software/systemd/man/systemd.unit.html) | File format | yes | yes | numerous | no | - | no |
 | [Network](https://www.freedesktop.org/software/systemd/man/systemd.network.html) & [Netdev file format](https://www.freedesktop.org/software/systemd/man/systemd.netdev.html) | File format | yes | yes | no | no | - | no |
 | [Link file format](https://www.freedesktop.org/software/systemd/man/systemd.link.html) | File format | yes | yes | no | no | - | no |
-| [Journal File Format](https://www.freedesktop.org/wiki/Software/systemd/journal-files) | File format | yes | yes | - | maybe | - | no |
+| [Journal File Format](https://systemd.io/JOURNAL_FILE_FORMAT) | File format | yes | yes | - | maybe | - | no |
 | [Journal Export Format](https://www.freedesktop.org/wiki/Software/systemd/export) | File format | yes | yes | - | yes | - | no |
 | [Cooperation in cgroup tree](https://www.freedesktop.org/wiki/Software/systemd/PaxControlGroups) | Treaty | yes | yes | libvirt | yes | libvirt | no |
 | [Password Agents](https://www.freedesktop.org/wiki/Software/systemd/PasswordAgents) | Socket+Files | yes | yes | - | yes | - | no |
index d3b68e967aa09d85c42633eca23cf8770610f10e..da3fe40baad1418683e64cab1c245d087738e31b 100644 (file)
@@ -118,7 +118,7 @@ requires random numbers as well, including for the following uses:
 
 * systemd maintains various hash tables internally. In order to harden them
   against [collision
-  attacks](https://rt.perl.org/Public/Bug/Display.html?CSRF_Token=165691af9ddaa95f653402f1b68de728)
+  attacks](https://www.cs.auckland.ac.nz/~mcw/Teaching/refs/misc/denial-of-service.pdf)
   they are seeded with random numbers.
 
 * At various places systemd needs random bytes for temporary file name
@@ -212,10 +212,10 @@ boot, in order to ensure the entropy pool is filled up quickly.
    random-seed`](https://www.freedesktop.org/software/systemd/man/bootctl.html#random-seed))
    a seed file with an initial seed is placed in a file `/loader/random-seed`
    in the ESP. In addition, an identically sized randomized EFI variable called
-   the the 'system token' is set, which is written to the machine's firmware
-   NVRAM. During boot, when `systemd-boot` finds both the random seed file and
-   the system token they are combined and hashed with SHA256 (in counter mode,
-   to generate sufficient data), to generate a new random seed file to store in
+   the 'system token' is set, which is written to the machine's firmware NVRAM.
+   During boot, when `systemd-boot` finds both the random seed file and the
+   system token they are combined and hashed with SHA256 (in counter mode, to
+   generate sufficient data), to generate a new random seed file to store in
    the ESP as well as a random seed to pass to the OS kernel. The new random
    seed file for the ESP is then written to the ESP, ensuring this is completed
    before the OS is invoked. Very early during initialization PID 1 will read
@@ -257,7 +257,16 @@ boot, in order to ensure the entropy pool is filled up quickly.
    file. If done, `systemd-boot` will use the random seed file even if no
    system token is found in EFI variables.
 
-With the three mechanisms described above it should be possible to provide
+4. A kernel command line option `systemd.random_seed=` may be used to pass in a
+   base64 encoded seed to initialize the kernel's entropy pool from during
+   early service manager initialization. This option is only safe in testing
+   environments, as the random seed passed this way is accessible to
+   unprivileged programs via `/proc/cmdline`. Using this option outside of
+   testing environments is a security problem since cryptographic key material
+   derived from the entropy pool initialized with a seed accessible to
+   unprivileged programs should not be considered secret.
+
+With the four mechanisms described above it should be possible to provide
 early-boot entropy in most cases. Specifically:
 
 1. On EFI systems, `systemd-boot`'s random seed logic should make sure good
@@ -267,7 +276,8 @@ early-boot entropy in most cases. Specifically:
 2. On virtualized systems, the early `virtio-rng` hookup should ensure entropy
    is available early on — as long as the VM environment provides virtualized
    RNG devices, which they really should all do in 2019. Complain to your
-   hosting provider if they don't.
+   hosting provider if they don't. For VMs used in testing environments,
+   `systemd.random_seed=` may be used as an alternative to a virtualized RNG.
 
 3. On Intel/AMD systems systemd's own reliance on the kernel entropy pool is
    minimal (as RDRAND is used on those for UUID generation). This only works if
@@ -286,8 +296,9 @@ This primarily leaves two kind of systems in the cold:
    boot. Alternatively, consider implementing a solution similar to
    systemd-boot's random seed concept in your platform's boot loader.
 
-2. Virtualized environments that lack both virtio-rng and RDRAND. Tough
-   luck. Talk to your hosting provider, and ask them to fix this.
+2. Virtualized environments that lack both virtio-rng and RDRAND, outside of
+   test environments. Tough luck. Talk to your hosting provider, and ask them
+   to fix this.
 
 3. Also note: if you deploy an image without any random seed and/or without
    installing any 'system token' in an EFI variable, as described above, this
@@ -410,6 +421,10 @@ This primarily leaves two kind of systems in the cold:
     information to possibly gain too much information about the current state
     of the kernel's entropy pool.
 
+    That said, we actually do implement this with the `systemd.random_seed=`
+    kernel command line option. Don't use this outside of testing environments,
+    however, for the aforementioned reasons.
+
 12. *Why doesn't `systemd-boot` rewrite the 'system token' too each time
     when updating the random seed file stored in the ESP?*
 
index 0b34e5325fc87e6de8f10ceeb07e81a0e0f1b6ce..bd2915bab6095f569ed42d1ff71a447ff00d1a1c 100644 (file)
@@ -6,4 +6,8 @@ layout: default
 
 # Reporting of Security Vulnerabilities
 
-If you discover a security vulnerability, we'd appreciate a non-public disclosure. The [issue tracker](https://github.com/systemd/systemd/issues) and [systemd-devel mailing list](https://lists.freedesktop.org/mailman/listinfo/systemd-devel) are fully public. If you need to reach systemd developers in a non-public way, report the issue to the [systemd-security@redhat.com](mailto:systemd-security@redhat.com) mailing list. The disclosure will be coordinated with distributions.
+If you discover a security vulnerability, we'd appreciate a non-public disclosure. systemd developers can be contacted privately on the **[systemd-security@redhat.com](mailto:systemd-security@redhat.com) mailing list**. The disclosure will be coordinated with distributions.
+
+(The [issue tracker](https://github.com/systemd/systemd/issues) and [systemd-devel mailing list](https://lists.freedesktop.org/mailman/listinfo/systemd-devel) are fully public.)
+
+Subscription to the systemd-security mailing list is open to **regular systemd contributors and people working in the security teams of various distributions**. Those conditions should be backed by publicly accessible information (ideally, a track of posts and commits from the mail address in question). If you fall into one of those categories and wish to be subscribed, submit a **[subscription request](https://www.redhat.com/mailman/listinfo/systemd-security)**.
index 271d8ab1e3d6d23670bac8a736f987e912b17c42..19944d08b804104c5ab69d13c1463fc9c01bd53a 100644 (file)
@@ -114,6 +114,7 @@ All execution-related settings are available for transient units.
 ✓ SupplementaryGroups=
 ✓ Nice=
 ✓ OOMScoreAdjust=
+✓ CoredumpFilter=
 ✓ IOSchedulingClass=
 ✓ IOSchedulingPriority=
 ✓ CPUSchedulingPolicy=
@@ -158,6 +159,9 @@ All execution-related settings are available for transient units.
 ✓ RestrictRealtime=
 ✓ RestrictSUIDSGID=
 ✓ RestrictAddressFamilies=
+✓ RootHash=
+✓ RootHashSignature=
+✓ RootVerity=
 ✓ LockPersonality=
 ✓ LimitCPU=
 ✓ LimitFSIZE=
@@ -284,37 +288,39 @@ All process killing settings are available for transient units:
 Most service unit settings are available for transient units.
 
 ```
-✓ PIDFile=
+✓ BusName=
 ✓ ExecCondition=
-✓ ExecStartPre=
+✓ ExecReload=
 ✓ ExecStart=
 ✓ ExecStartPost=
-✓ ExecReload=
+✓ ExecStartPre=
 ✓ ExecStop=
 ✓ ExecStopPost=
-✓ RestartSec=
-✓ TimeoutStartSec=
-✓ TimeoutStopSec=
-✓ TimeoutAbortSec=
-✓ TimeoutSec=
-✓ RuntimeMaxSec=
-✓ WatchdogSec=
-✓ Type=
-✓ Restart=
-✓ RootDirectoryStartOnly=
-✓ RemainAfterExit=
+✓ FileDescriptorStoreMax=
 ✓ GuessMainPID=
-✓ RestartPreventExitStatus=
-✓ RestartForceExitStatus=
-✓ SuccessExitStatus=
 ✓ NonBlocking=
-✓ BusName=
-✓ FileDescriptorStoreMax=
 ✓ NotifyAccess=
+✓ OOMPolicy=
+✓ PIDFile=
+✓ RemainAfterExit=
+✓ Restart=
+✓ RestartForceExitStatus=
+✓ RestartPreventExitStatus=
+✓ RestartSec=
+✓ RootDirectoryStartOnly=
+✓ RuntimeMaxSec=
   Sockets=
+✓ SuccessExitStatus=
+✓ TimeoutAbortSec=
+✓ TimeoutSec=
+✓ TimeoutStartFailureMode=
+✓ TimeoutStartSec=
+✓ TimeoutStopFailureMode=
+✓ TimeoutStopSec=
+✓ Type=
 ✓ USBFunctionDescriptors=
 ✓ USBFunctionStrings=
-✓ OOMPolicy=
+✓ WatchdogSec=
 ```
 
 ## Mount Unit Settings
@@ -331,6 +337,7 @@ All mount unit settings are available to transient units:
 ✓ SloppyOptions=
 ✓ LazyUnmount=
 ✓ ForceUnmount=
+✓ ReadWriteOnly=
 ```
 
 ## Automount Unit Settings
@@ -427,6 +434,7 @@ Most socket unit settings are available to transient units.
 ✓ Broadcast=
 ✓ PassCredentials=
 ✓ PassSecurity=
+✓ PassPacketInfo=
 ✓ TCPCongestion=
 ✓ ReusePort=
 ✓ MessageQueueMaxMessages=
index 255cc713226968cbf0c23a3227c3c674cdc81a1e..67e6d083ff74aa1423aa330003addf766244803c 100644 (file)
@@ -1,6 +1,6 @@
 ---
 title: Users, Groups, UIDs and GIDs on systemd Systems
-category: Concepts
+category: Users, Groups and Home Directories
 layout: default
 ---
 
@@ -132,7 +132,7 @@ but downstreams are strongly advised against doing that.)
    range is above the 16bit boundary. Moreover it's below the 31bit boundary,
    as some broken code (specifically: the kernel's `devpts` file system)
    erroneously considers UIDs signed integers, and hence can't deal with values
-   above 2^31. The `nss-mymachines` glibc NSS module will synthesize user
+   above 2^31. The `systemd-machined.service` service will synthesize user
    database records for all UIDs assigned to a running container from this
    range.
 
@@ -240,14 +240,14 @@ the artifacts the container manager persistently leaves in the system.
 |                     5 | `tty` group           | `systemd`     | `/etc/passwd`                 |
 |                 6…999 | System users          | Distributions | `/etc/passwd`                 |
 |            1000…60000 | Regular users         | Distributions | `/etc/passwd` + LDAP/NIS/…    |
-|           60001…60513 | Human Users (homed)   | `systemd`     | `nss-systemd`
+|           60001…60513 | Human Users (homed)   | `systemd`     | `nss-systemd`                 |
 |           60514…61183 | Unused                |               |                               |
 |           61184…65519 | Dynamic service users | `systemd`     | `nss-systemd`                 |
 |           65520…65533 | Unused                |               |                               |
 |                 65534 | `nobody` user         | Linux         | `/etc/passwd` + `nss-systemd` |
 |                 65535 | 16bit `(uid_t) -1`    | Linux         |                               |
 |          65536…524287 | Unused                |               |                               |
-|     524288…1879048191 | Container UID ranges  | `systemd`     | `nss-mymachines`              |
+|     524288…1879048191 | Container UID ranges  | `systemd`     | `nss-systemd`                 |
 | 1879048192…2147483647 | Unused                |               |                               |
 | 2147483648…4294967294 | HIC SVNT LEONES       |               |                               |
 |            4294967295 | 32bit `(uid_t) -1`    | Linux         |                               |
diff --git a/docs/USERDB_AND_DESKTOPS.md b/docs/USERDB_AND_DESKTOPS.md
new file mode 100644 (file)
index 0000000..a19f746
--- /dev/null
@@ -0,0 +1,169 @@
+---
+title: systemd-homed and JSON User/Group Record Support in Desktop Environments
+category: Users, Groups and Home Directories
+layout: default
+---
+
+# `systemd-homed` and JSON User/Group Record Support in Desktop Environments
+
+Starting with version 245, systemd supports a new subsystem
+[`systemd-homed.service`](https://www.freedesktop.org/software/systemd/man/systemd-homed.service.html)
+for managing regular ("human") users and their home directories. Along with it
+a new concept `userdb` got merged that brings rich, extensible JSON user/group
+records, extending the classic UNIX/glibc NSS `struct passwd`/`struct group`
+structures. Both additions are added in a fully backwards compatible way,
+accessible through `getpwnam()`/`getgrnam()`/… (i.e. libc NSS) and PAM as
+usual, meaning that for basic support no changes in the upper layers of the
+stack (in particular desktop environments, such as GNOME or KDE) have to be
+made. However, for better support a number of changes to desktop environments
+are recommended. A few areas where that applies are discussed below.
+
+Before reading on, please read up on the basic concepts, specifically:
+
+* [Home Directories](https://systemd.io/HOME_DIRECTORY)
+* [JSON User Records](https://systemd.io/USER_RECORD)
+* [JSON Group Records](https://systemd.io/GROUP_RECORD)
+* [User/Group Record Lookup API via Varlink](https://systemd.io/USER_GROUP_API)
+
+## Support for Suspending Home Directory Access during System Suspend
+
+One key feature of `systemd-homed` managed encrypted home directories is the
+ability that access to them can be suspended automatically during system sleep,
+removing any cryptographic key material from memory while doing so. This is
+important in a world where most laptop users seldom shut down their computers
+but most of the time just suspend them instead. Previously, the encryption keys
+for the home directories remained in memory during system suspend, so that
+sufficiently equipped attackers could read them from there and gain full access
+to the device. By removing the key material from memory before suspend, and
+re-requesting it on resume this attack vector can be closed down effectively.
+
+Supporting this mechanism requires support in the desktop environment, since
+the encryption keys (i.e. the user's login password) need to be reacquired on
+system resume, from a lock screen or similar. This lock screen must run in
+system context, and cannot run in the user's own context, since otherwise it
+might end up accessing the home directory of the user even though access to it
+is temporarily suspended and thus will hang if attempted.
+
+It is suggested that desktop environments that implement lock screens run them
+from system context, for example by switching back to the display manager, and
+only revert back to the session after re-authentication via this system lock
+screen (re-authentication in this case refers to passing the user's login
+credentials to the usual PAM authentication hooks). Or in other words, when
+going into system suspend it is recommended that GNOME Shell switches back to
+the GNOME Display Manager login screen which now should double as screen lock,
+and only switches back to the shell's UI after the user re-authenticated there.
+
+Note that this change in behavior is a good idea in any case, and does not
+create any dependencies on `systemd-homed` or systemd-specific APIs. It's
+simply a change of behavior regarding use of existing APIs, not a suggested
+hook-up to a any new API.
+
+A display manager which supports this kind of out-of-context screen lock
+operation needs to inform systemd-homed about this so that systemd-homed knows
+that it is safe to suspend the user's home directory on suspend. This is done
+via the `suspend=` argument to the
+[`pam_systemd_home`](https://www.freedesktop.org/software/systemd/man/pam_systemd_home.html)
+PAM module. A display manager should hence change its PAM stack configuration
+to set this parameter to on. `systemd-homed` will not suspend home directories
+if there's at least one active session of the user that does not support
+suspending, as communicated via this parameter.
+
+## User Management UIs
+
+The rich user/group records `userdb` and `systemd-homed` support carry various
+fields of relevance to UIs that manage the local user database or parts
+thereof. In particular, most of the metadata `accounts-daemon` (also see below)
+supports is directly available in these JSON records. Hence it makes sense for
+any user management UI to expose them directly.
+
+`systemd-homed` exposes APIs to add, remove and make changes to local users via
+D-Bus, with full [polkit](https://www.freedesktop.org/software/polkit/docs/latest/)
+hook-up. On the command line this is exposed via the
+`homectl` command. A graphical UI that exposes similar functionality would be
+very useful, exposing the various new account settings, and in particular
+providing a stream-lined UI for enrolling new-style authentication tokens such
+as PKCS#11/YubiKey-style devices. (Ideally, if the user plugs in an
+uninitialized YubiKey during operation it might be nice if the Desktop would
+automatically ask if a key pair shall be written to it and the local account be
+bound to it, `systemd-homed` provides enough YubiKey/PKCS#11 support to make
+this a reality today; except that it will not take care of token
+initialization).
+
+A strong point of `systemd-homed` is per-user resource management. In
+particular disk space assignments are something that most likely should be
+exposed in a user management UI. Various metadata fields are supplied allowing
+exposure of disk space assignment "slider" UI. Note however that the file system
+back-ends of `systemd-homed.service` have different feature sets. Specifically,
+only btrfs has online file system shrinking support, ext4 only offline file
+system shrinking support, and xfs no shrinking support at all (all three file
+systems support online file system growing however). This means if the LUKS
+back-end is used, disk space assignment cannot be instant for logged in users,
+unless btrfs is used.
+
+Note that only `systemd-homed` provides an API for modifying/creating/deleting
+users. The generic `userdb` subsystem (which might have other back-ends, besides
+`systemd-homed`, for example LDAP or Windows) exclusively provides a read-only
+interface. (This is unlikely to change, as the other back-ends might have very
+different concepts of adding or modifying users, i.e. might not even have any
+local concept for that at all). This means any user management UI that intends
+to change (and not just view) user accounts should talk directly to
+`systemd-homed` to make use of its features; there's no abstraction available
+to support other back-ends under the same API.
+
+Unfortunately there's currently no documentation for the `systemd-homed` D-Bus
+API. Consider using the `homectl` sources as guidelines for implementing a user
+management UI. The JSON user/records are well documented however, see above,
+and the D-Bus API provides limited introspection.
+
+## Relationship to `accounts-daemon`
+
+For a long time `accounts-daemon` has been included in Linux distributions
+providing richer user accounts. The functionality of this daemon overlaps in
+many areas with the functionality of `systemd-homed` or `userdb`, but there are
+systematic differences, which means that `systemd-homed` cannot replace
+`accounts-daemon` fully. Most importantly: `accounts-daemon` provides
+"side-car" metadata for *any* type of user account, while `systemd-homed` only
+provides additional metadata for the users it defines itself.  In other words:
+`accounts-daemon` will augment foreign accounts; `systemd-homed` cannot be used
+to augment users defined elsewhere, for example in LDAP or as classic
+`/etc/passwd` records.
+
+This probably means that for the time being, a user management UI (or other UI)
+that wants to support rich user records with compatibility with the status quo
+ante should probably talk to both `systemd-homed` and `accounts-daemon` at the
+same time, and ignore `accounts-daemon`'s records if `systemd-homed` defines
+them. While I (Lennart) personally believe in the long run `systemd-homed` is
+the way to go for rich user records, any UI that wants to manage and support
+rich records for classic records has to support `accounts-daemon` in parallel
+for the time being.
+
+In the short term, it might make sense to also expose the `userdb` provided
+records via `accounts-daemon`, so that clients of the latter can consume them
+without changes. However, I think in the long run `accounts-daemon` should
+probably be removed from the general stack, hence this sounds like a temporary
+solution only.
+
+In case you wonder, there's no automatic mechanism for converting existing
+users registered in `/etc/passwd` or LDAP to users managed by
+`systemd-homed`. There's documentation for doing this manually though, see
+[Converting Existing Users to systemd-homed managed
+Users](https://systemd.io/CONVERTING_TO_HOMED).
+
+## Future Additions
+
+JSON user/group records are extensible, hence we can easily add any additional
+fields desktop environments require. For example pattern-based authentication
+is likely very useful on touch-based devices, and the user records should hence
+learn them natively. Fields for other authentication mechanisms, such as
+fingerprint authentication should be provided as well, eventually.
+
+It is planned to extend the `userdb` Varlink API to support look-ups by partial
+user name and real name (GECOS) data, so that log-in screens can optionally
+implement simple complete-as-you-type login screens.
+
+It is planned to extend the `systemd-homed` D-Bus API to instantly inform clients
+about hardware associated with a specific user being plugged in, to which login
+screens can listen in order to initiate authentication. Specifically, any
+YubiKey-like security token plugged in that is associated with a local user
+record should initiate authentication for that user, making typing in of the
+username unnecessary.
index 21d498f5fd11298309756b56f1d7bb77ed5cb3dd..ca88b3a16d9ce1732360d9f8b0b3feda4bc4ac86 100644 (file)
@@ -1,6 +1,6 @@
 ---
 title: User/Group Record Lookup API via Varlink
-category: Interfaces
+category: Users, Groups and Home Directories
 layout: default
 ---
 
@@ -95,7 +95,7 @@ services are listening there, that have special relevance:
 2. `io.systemd.Multiplexer` → This service multiplexes client queries to all
    other running services. It's supposed to simplify client development: in
    order to look up or enumerate user/group records it's sufficient to talk to
-   one service instead of all of them in parallel. Note that it is not availabe
+   one service instead of all of them in parallel. Note that it is not available
    during earliest boot and final shutdown phases, hence for programs running
    in that context it is preferable to implement the parallel lookup
    themselves.
@@ -108,7 +108,7 @@ example, introspection is not available, and the resolver logic is not used.
 
 ## Other Services
 
-The `systemd` project provides two other services implementing this
+The `systemd` project provides three other services implementing this
 interface. Specifically:
 
 1. `io.systemd.DynamicUser` → This service is implemented by the service
@@ -119,6 +119,10 @@ interface. Specifically:
    and provides records for the users and groups defined by the home
    directories it manages.
 
+3. `io.systemd.Machine` → This service is implemented by
+   `systemd-machined.service` and provides records for the users and groups used
+   by local containers that use user namespacing.
+
 Other projects are invited to implement these services too. For example it
 would make sense for LDAP/ActiveDirectory projects to implement these
 interfaces, which would provide them a way to do per-user resource management
@@ -160,7 +164,7 @@ method GetUserRecord(
         service : string
 ) -> (
         record : object,
-        incomplete : boolean
+        incomplete : bool
 )
 
 method GetGroupRecord(
@@ -169,7 +173,7 @@ method GetGroupRecord(
         service : string
 ) -> (
         record : object,
-        incomplete : boolean
+        incomplete : bool
 )
 
 method GetMemberships(
@@ -185,6 +189,7 @@ error NoRecordFound()
 error BadService()
 error ServiceNotAvailable()
 error ConflictingRecordFound()
+error EnumerationNotSupported()
 ```
 
 The `GetUserRecord` method looks up or enumerates a user record. If the `uid`
@@ -264,4 +269,11 @@ services. Result of this is that it can be one service that defines a user A,
 and another service that defines a group B, and a third service that declares
 that A is a member of B.
 
+Looking up explicit users/groups by their name or UID/GID, or querying
+user/group memberships must be supported by all services implementing these
+interfaces. However, supporting enumeration (i.e. user/group lookups that may
+result in more than one reply, because neither UID/GID nor name is specified)
+is optional. Services which are asked for enumeration may return the
+`EnumerationNotSupported` error in this case.
+
 And that's really all there is to it.
diff --git a/docs/USER_NAMES.md b/docs/USER_NAMES.md
new file mode 100644 (file)
index 0000000..ec07b19
--- /dev/null
@@ -0,0 +1,169 @@
+---
+title: User/Group Name Syntax
+category: Users, Groups and Home Directories
+layout: default
+---
+
+# User/Group Name Syntax
+
+The precise set of allowed user and group names on Linux systems is weakly
+defined. Depending on the distribution a different set of requirements and
+restrictions on the syntax of user/group names are enforced — on some
+distributions the accepted syntax is even configurable by the administrator. In
+the interest of interoperability systemd enforces different rules when
+processing users/group defined by other subsystems and when defining users/groups
+itself, following the principle of "Be conservative in what you send, be
+liberal in what you accept". Also in the interest of interoperability systemd
+will enforce the same rules everywhere and not make them configurable or
+distribution dependent. The precise rules are described below.
+
+Generally, the same rules apply for user as for group names.
+
+## Other Systems
+
+* On POSIX the set of [valid user
+  names](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_437)
+  is defined as [lower and upper case ASCII letters, digits, period,
+  underscore, and
+  hyphen](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_282),
+  with the restriction that hyphen is not allowed as first character of the
+  user name. Interestingly no size limit is declared, i.e. in neither
+  direction, meaning that strictly speaking according to POSIX both the empty
+  string is a valid user name as well as a string of gigabytes in length.
+
+* Debian/Ubuntu based systems enforce the regular expression
+  `^[a-z][-a-z0-9]*$`, i.e. only lower case ASCII letters, digits and
+  hyphens. As first character only lowercase ASCII letters are allowed. This
+  regular expression is configurable by the administrator at runtime
+  though. This rule enforces a minimum length of one character but no maximum
+  length.
+
+* Upstream shadow-utils enforces the regular expression
+  `^[a-z_][a-z0-9_-]*[$]$`, i.e. is similar to the Debian/Ubuntu rule, but
+  allows underscores and hyphens, but the latter not as first character. Also,
+  an optional trailing dollar character is permitted.
+
+* Fedora/Red Hat based systems enforce the regular expression of
+  `^[a-zA-Z0-9_.][a-zA-Z0-9_.-]{0,30}[a-zA-Z0-9_.$-]?$`, i.e. a size limit of
+  32 characters, with upper and lower case letters, digits, underscores,
+  hyphens and periods. No hyphen as first character though, and the last
+  character may be a dollar character. On top of that, `.` and `..` are not
+  allowed as user/group names.
+
+* sssd is known to generate user names with embedded `@` and white-space
+  characters, as well as non-ASCII (i.e. UTF-8) user/group names.
+
+* winbindd is known to generate user/group names with embedded `\` and
+  white-space characters, as well as non-ASCII (i.e. UTF-8) user/group names.
+
+Other operating systems enforce different rules; in this documentation we'll
+focus on Linux systems only however, hence those are out of scope. That said,
+software like Samba is frequently deployed on Linux for providing compatibility
+with Windows systems; on such systems it might be wise to stick to user/group
+names also valid according to Windows rules.
+
+## Rules systemd enforces
+
+Distilled from the above, below are the rules systemd enforces on user/group
+names. An additional, common rule between both modes listed below is that empty
+strings are not valid user/group names.
+
+Philosophically, the strict mode described below enforces an allow list of
+what's allowed and prohibits everything else, while the relaxed mode described
+below implements a deny list of what's not allowed and permits everything else.
+
+### Strict mode
+
+Strict user/group name syntax is enforced whenever a systemd component is used
+to register a user or group in the system, for example a system user/group
+using
+[`systemd-sysusers.service`](https://www.freedesktop.org/software/systemd/man/systemd-sysusers.html)
+or a regular user with
+[`systemd-homed.service`](https://www.freedesktop.org/software/systemd/man/systemd-homed.html).
+
+In strict mode, only uppercase and lowercase characters are allowed, as well as
+digits, underscores and hyphens. The first character may not be a digit or
+hyphen. A size limit is enforced: the minimum of `sysconf(_SC_LOGIN_NAME_MAX)`
+(typically 256 on Linux; rationale: this is how POSIX suggests to detect the
+limit), `UT_NAMESIZE-1` (typically 31 on Linux; rationale: names longer than
+this cannot correctly appear in `utmp`/`wtmp` and create ambiguity with login
+accounting) and `FILENAME_MAX` (4096 on Linux; rationale: user names typically
+appear in directory names, i.e. the home directory), thus MIN(256, 31, 4096) =
+31.
+
+Note that these rules are both more strict and more relaxed than all of the
+rules enforced by other systems listed above. A user/group name conforming to
+systemd's strict rules will not necessarily pass a test by the rules enforced
+by these other subsystems.
+
+Written as regular expression the above is: `^[a-zA-Z_][a-zA-Z0-9_-]{0,30}$`
+
+### Relaxed mode
+
+Relaxed user/group name syntax is enforced whenever a systemd component accepts
+and makes use of user/group names registered by other (non-systemd)
+components of the system, for example in
+[`systemd-logind.service`](https://www.freedesktop.org/software/systemd/man/systemd-logind.html).
+
+Relaxed syntax is also enforced by the `User=` setting in service unit files,
+i.e. for system services used for running services. Since these users may be
+registered by a variety of tools relaxed mode is used, but since the primary
+purpose of these users is to run a system service and thus a job for systemd a
+warning is shown if the specified user name does not qualify by the strict
+rules above.
+
+* No embedded NUL bytes (rationale: handling in C must be possible and
+  straight-forward)
+
+* No names consisting fully of digits (rationale: avoid confusion with numeric
+  UID/GID specifications)
+
+* Similar, no names consisting of an initial hyphen and otherwise entirely made
+  up of digits (rationale: avoid confusion with negative, numeric UID/GID
+  specifications, e.g. `-1`)
+
+* No strings that do not qualify as valid UTF-8 (rationale: we want to be able
+  to embed these strings in JSON, with permits only valid UTF-8 in its strings;
+  user names using other character sets, such as JIS/Shift-JIS will cause
+  validation errors)
+
+* No control characters (i.e. characters in ASCII range 1…31; rationale: they
+  tend to have special meaning when output on a terminal in other contexts,
+  moreover the newline character — as a specific control character — is used as
+  record separator in `/etc/passwd`, and hence it's crucial to avoid
+  ambiguities here)
+
+* No colon characters (rationale: it is used as field separator in `/etc/passwd`)
+
+* The two strings `.` and `..` are not permitted, as these have special meaning
+  in file system paths, and user names are frequently included in file system
+  paths, in particular for the purpose of home directories.
+
+* Similar, no slashes, as these have special meaning in file system paths
+
+* No leading or trailing white-space is permitted; and hence no user/group names
+  consisting of white-space only either (rationale: this typically indicates
+  parsing errors, and creates confusion since not visible on screen)
+
+Note that these relaxed rules are implied by the strict rules above, i.e. all
+user/group names accepted by the strict rules are also accepted by the relaxed
+rules, but not vice versa.
+
+Note that this relaxed mode does not refuse a couple of very questionable
+syntaxes. For example it permits a leading or embedded period. A leading period
+is problematic because the matching home directory would typically be hidden
+from the user's/administrator's view. An embedded period is problematic since
+it creates ambiguity in traditional `chown` syntax (which is still accepted
+today) that uses it to separate user and group names in the command's
+parameter: without consulting the user/group databases it is not possible to
+determine if a `chown` invocation would change just the owning user or both the
+owning user and group. It also allows embedding `@` (which is confusing to
+MTAs).
+
+## Common Core
+
+Combining all rules listed above, user/group names that shall be considered
+valid in all systemd contexts and on all Linux systems should match the
+following regular expression (at least according to our understanding):
+
+`^[a-z][a-z0-9-]{0,30}$`
index 9828cd50bc36e4aab4def940b71bb2e101c8689a..f6d22c217b0026dfd6f3a4cf83b05800b9f3322e 100644 (file)
@@ -1,6 +1,6 @@
 ---
 title: JSON User Records
-category: Interfaces
+category: Users, Groups and Home Directories
 layout: default
 ---
 
@@ -14,7 +14,7 @@ pairs, encoded as JSON. Specifically:
 1. [`systemd-homed.service`](https://www.freedesktop.org/software/systemd/man/systemd-homed.service.html)
    manages `human` user home directories and embeds these JSON records
    directly in the home directory images (see [Home
-   Directories](https://systemd.io/HOME_DIRECTORY)) for details.
+   Directories](https://systemd.io/HOME_DIRECTORY) for details).
 
 2. [`pam_systemd`](https://www.freedesktop.org/software/systemd/man/pam_systemd.html)
    processes these JSON records for users that log in, and applies various
@@ -180,7 +180,7 @@ strictly local context and without signatures doesn't have to deal with the
 `perMachine` or `binding` sections and can include its data exclusively in the
 regular section. A service that uses a separate, private channel for
 authenticating users (or that doesn't have a concept of authentication at all)
-does not need to to be concerned with the `secret` section of user records, as
+does not need to be concerned with the `secret` section of user records, as
 the fields included therein are only useful when executing authentication
 operations natively against JSON user records.
 
@@ -455,6 +455,10 @@ storage. If false and `luks` storage is used turns this behavior off. In
 addition, depending on this setting an `FITRIM` or `fallocate()` operation is
 executed to make sure the image matches the selected option.
 
+`luksOfflineDiscard` → A boolean. Similar to `luksDiscard`, it controls whether
+to trim/allocate the file system/backing file when deactivating the home
+directory.
+
 `luksCipher` → A string, indicating the cipher to use for the LUKS storage mechanism.
 
 `luksCipherMode` → A string, selecting the cipher mode to use for the LUKS storage mechanism.
@@ -542,7 +546,12 @@ below). It's undefined how precise the URI is: during log-in it is tested
 against all plugged in security tokens and if there's exactly one matching
 private key found with it it is used.
 
-`privileged` → An object, which contains the fields of he `privileged` section
+`fido2HmacCredential` → An array of strings, each with a Base64-encoded FIDO2
+credential ID that shell be used for authentication with FIDO2 devices that
+implement the `hmac-secret` extension. The salt to pass to the FIDO2 device is
+found in `fido2HmacSalt`.
+
+`privileged` → An object, which contains the fields of the `privileged` section
 of the user record, see below.
 
 `perMachine` → An array of objects, which contain the `perMachine` section of
@@ -578,7 +587,7 @@ restrictive access semantics. The following fields are currently defined:
 be a string like "What's the name of your first pet?", but is entirely for the
 user to choose.
 
-`hashPassword` → An array of strings, each containing a hashed UNIX password
+`hashedPassword` → An array of strings, each containing a hashed UNIX password
 string, in the format
 [`crypt(3)`](http://man7.org/linux/man-pages/man3/crypt.3.html) generates. This
 corresponds with `sp_pwdp` field of `struct spwd` (and in a way the `pw_passwd`
@@ -590,7 +599,7 @@ as the lines in the traditional `~/.ssh/authorized_key` file.
 
 `pkcs11EncryptedKey` → An array of objects. Each element of the array should be
 an object consisting of three string fields: `uri` shall contain a PKCS#11
-security token URI, `data` shall contain a Base64 encoded encrypted key and
+security token URI, `data` shall contain a Base64-encoded encrypted key and
 `hashedPassword` shall contain a UNIX password hash to test the key
 against. Authenticating with a security token against this account shall work
 as follows: the encrypted secret key is converted from its Base64
@@ -598,13 +607,29 @@ representation into binary, then decrypted with the PKCS#11 `C_Decrypt()`
 function of the PKCS#11 module referenced by the specified URI, using the
 private key found on the same token. The resulting decrypted key is then
 Base64-encoded and tested against the specified UNIX hashed password. The
-Base64-enceded decrypted key may also be used to unlock further resources
+Base64-encoded decrypted key may also be used to unlock further resources
 during log-in, for example the LUKS or `fscrypt` storage backend. It is
 generally recommended that for each entry in `pkcs11EncryptedKey` there's also
 a matching one in `pkcs11TokenUri` and vice versa, with the same URI, appearing
 in the same order, but this should not be required by applications processing
 user records.
 
+`fido2HmacSalt` → An array of objects, implementing authentication support with
+FIDO2 devices that implement the `hmac-secret` extension. Each element of the
+array should be an object consisting of three string fields: `credential`,
+`salt`, `hashedPassword`. The first two shall contain Base64-encoded binary
+data: the FIDO2 credential ID and the salt value to pass to the FIDO2
+device. During authentication this salt along with the credential ID is sent to
+the FIDO2 token, which will HMAC hash the salt with its internal secret key and
+return the result. This resulting binary key should then be Base64-encoded and
+used as string password for the further layers of the stack. The
+`hashedPassword` field of the `fido2HmacSalt` field shall be a UNIX password
+hash to test this derived secret key against for authentication. It is
+generally recommended that for each entry in `fido2HmacSalt` there's also a
+matching one in `fido2HmacCredential`, and vice versa, with the same credential
+ID, appearing in the same order, but this should not be required by
+applications processing user records.
+
 ## Fields in the `perMachine` section
 
 As mentioned, the `perMachine` section contains settings that shall apply to
@@ -648,12 +673,13 @@ that may be used in this section are identical to the equally named ones in the
 `mountNoDevices`, `mountNoSuid`, `mountNoExecute`, `cifsDomain`,
 `cifsUserName`, `cifsService`, `imagePath`, `uid`, `gid`, `memberOf`,
 `fileSystemType`, `partitionUuid`, `luksUuid`, `fileSystemUuid`, `luksDiscard`,
-`luksCipher`, `luksCipherMode`, `luksVolumeKeySize`, `luksPbkdfHashAlgorithm`,
-`luksPbkdfType`, `luksPbkdfTimeCostUSec`, `luksPbkdfMemoryCost`,
-`luksPbkdfParallelThreads`, `rateLimitIntervalUSec`, `rateLimitBurst`,
-`enforcePasswordPolicy`, `autoLogin`, `stopDelayUSec`, `killProcesses`,
-`passwordChangeMinUSec`, `passwordChangeMaxUSec`, `passwordChangeWarnUSec`,
-`passwordChangeInactiveUSec`, `passwordChangeNow`, `pkcs11TokenUri`.
+`luksOfflineDiscard`, `luksCipher`, `luksCipherMode`, `luksVolumeKeySize`,
+`luksPbkdfHashAlgorithm`, `luksPbkdfType`, `luksPbkdfTimeCostUSec`,
+`luksPbkdfMemoryCost`, `luksPbkdfParallelThreads`, `rateLimitIntervalUSec`,
+`rateLimitBurst`, `enforcePasswordPolicy`, `autoLogin`, `stopDelayUSec`,
+`killProcesses`, `passwordChangeMinUSec`, `passwordChangeMaxUSec`,
+`passwordChangeWarnUSec`, `passwordChangeInactiveUSec`, `passwordChangeNow`,
+`pkcs11TokenUri`, `fido2HmacCredential`.
 
 ## Fields in the `binding` section
 
@@ -805,7 +831,7 @@ public key.
 The `signature` field in the top-level user record object is an array of
 objects. Each object encapsulates one signature and has two fields: `data` and
 `key` (both are strings). The `data` field contains the actual signature,
-encoded in base64, the `key` field contains a copy of the public key whose
+encoded in Base64, the `key` field contains a copy of the public key whose
 private key was used to make the signature, in PEM format. Currently only
 signatures with Ed25519 keys are defined.
 
@@ -859,13 +885,20 @@ The `secret` field of the top-level user record contains the following fields:
 
 `password` → an array of strings, each containing a plain text password.
 
-`pkcs11Pin` → an array of strings, each containing a plain text PIN, suitable
-for unlocking PKCS#11 security tokens that require that.
+`tokenPin` → an array of strings, each containing a plain text PIN, suitable
+for unlocking security tokens that require that. (The field `pkcs11Pin` should
+be considered a compatibility alias for this field, and merged with `tokenPin`
+in case both are set.)
 
 `pkcs11ProtectedAuthenticationPathPermitted` → a boolean. If set to true allows
 the receiver to use the PKCS#11 "protected authentication path" (i.e. a
 physical button/touch element on the security token) for authenticating the
-user. If false or unset authentication this way shall not be attempted.
+user. If false or unset, authentication this way shall not be attempted.
+
+`fido2UserPresencePermitted` → a boolean. If set to true allows the receiver to
+use the FIDO2 "user presence" flag. This is similar to the concept of
+`pkcs11ProtectedAuthenticationPathPermitted`, but exposes the FIDO2 concept
+behind it. If false or unset authentication this way shall not be attempted.
 
 ## Mapping to `struct passwd` and `struct spwd`
 
index e7365cd1426505ee330a1f4a492b56a0660a1ad6..da74b19d9095f54bea3de1e4dffe97faf2c9101e 100644 (file)
@@ -1,7 +1,7 @@
 # This file is part of systemd.
 
-passwd:         compat mymachines systemd
-group:          compat [SUCCESS=merge] mymachines [SUCCESS=merge] systemd
+passwd:         compat systemd
+group:          compat [SUCCESS=merge] systemd
 shadow:         compat
 
 hosts:          files mymachines resolve [!UNAVAIL=return] dns myhostname
diff --git a/fuzzbuzz.yaml b/fuzzbuzz.yaml
deleted file mode 100644 (file)
index 2cd1763..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-base: ubuntu:16.04
-language: c
-setup:
-- sudo bash -c "echo 'deb-src http://archive.ubuntu.com/ubuntu/ xenial main restricted universe multiverse' >>/etc/apt/sources.list"
-- sudo apt-get update -y
-- sudo apt-get build-dep -y systemd
-- sudo apt-get install -y python3-pip
-- sudo apt-get install -y libfdisk-dev libp11-kit-dev libssl-dev libpwquality-dev
-- pip3 install meson ninja
-- export PATH="$HOME/.local/bin/:$PATH"
-- CC=$FUZZ_CC CXX=$FUZZ_CXX meson -Dfuzzbuzz=true -Dfuzzbuzz-engine-dir=$(dirname "$FUZZ_ENGINE") -Dfuzzbuzz-engine=$(cut -d. -f1 <(basename "$FUZZ_ENGINE")) -Db_lundef=false ./build
-- ninja -v -C ./build fuzzers
-environment:
-targets:
-- name: fuzz-compress
-  harness:
-    binary: ./build/fuzz-compress
-- name: fuzz-unit-file
-  harness:
-    binary: ./build/fuzz-unit-file
-  corpus: ./test/fuzz/fuzz-unit-file
-- name: fuzz-journald-syslog
-  harness:
-    binary: ./build/fuzz-journald-syslog
-  corpus: ./test/fuzz/fuzz-journald-syslog
-- name: fuzz-netdev-parser
-  harness:
-    binary: ./build/fuzz-netdev-parser
-  corpus: ./test/fuzz/fuzz-netdev-parser
-- name: fuzz-network-parser
-  harness:
-    binary: ./build/fuzz-network-parser
-  corpus: ./test/fuzz/fuzz-network-parser
diff --git a/hwdb.d/60-autosuspend.hwdb b/hwdb.d/60-autosuspend.hwdb
new file mode 100644 (file)
index 0000000..e742a33
--- /dev/null
@@ -0,0 +1,54 @@
+# This file is part of systemd.
+#
+# The lookup keys are $MODALIAS strings, see udev's hwdb builtin.
+#
+# Match string formats:
+# <subsystem>:<modalias>
+#
+# pci:v<vendor>d<device>
+# usb:v<vendor>p<product>
+#
+# To add local entries, create a new file
+#   /etc/udev/hwdb.d/61-autosuspend-local.hwdb
+# and add your rules there. To load the new rules execute (as root):
+#   systemd-hwdb update
+#   udevadm trigger /dev/…
+#
+# If your changes are generally applicable, preferably send them as a pull
+# request to
+#   https://github.com/systemd/systemd
+# or create a bug report on https://github.com/systemd/systemd/issues and
+# include your new rules, a description of the device, and the output of
+#   udevadm info
+# the device.
+#
+# Allowed properties are:
+#    ID_AUTOSUSPEND=1
+
+#
+# Sort by brand, model
+
+#########################################
+# Alcor
+#########################################
+
+# AU9540 Smartcard Reader
+usb:v058Fp9540*
+ ID_AUTOSUSPEND=1
+
+#########################################
+# QEMU
+#########################################
+
+# Emulated USB HID devices
+usb:v0627p0001:*QEMU USB Keyboard*
+usb:v0627p0001:*QEMU USB Mouse*
+usb:v0627p0001:*QEMU USB Tablet*
+ ID_AUTOSUSPEND=1
+
+#########################################
+# Wacom
+#########################################
+
+usb:v056Ap51A0*
+ ID_AUTOSUSPEND=1
index 7b08e2643e9f54cc5d5b3e1aac93a66b37b6151f..84d997ebdc27c468286134717c38b063064bfa24 100644 (file)
@@ -598,8 +598,9 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHDX9494NR:pvr*
 
 # HP EliteBook 725 G2
 evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHPLicrice:pvr*
-# HP EliteBook 840 G1
-evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHPEliteBook840G1:pvr*
+# HP EliteBook
+evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHPEliteBook*:pvr*
+evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP*:pnHPEliteBook*:pvr*
 # HP ProBook 440 G2
 evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHP440G2:pvr*
 # several HP ProBooks 4xx
@@ -608,8 +609,6 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP*:pnHP*ProBook*4*:pvr*
 # HP ZBook
 evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHPZBook*:pvr*
 evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP*:pnHPZBook*:pvr*
-# Elitebook x360 1040 G6
-evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP:pn*EliteBook*x3601040G6:pvr*
  KEYBOARD_KEY_81=f20                                    # Fn+F8; Microphone mute button, should be micmute
 
 # HP ZBook 15 G2
@@ -628,7 +627,6 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP:pnHPZBookStudioG4:pvr*
 
 # HP Folio 1040g2
 evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHPEliteBookFolio1040G2:pvr*
- KEYBOARD_KEY_81=f20                                    # Fn+F8; Microphone mute button, should be micmute
  KEYBOARD_KEY_d8=!f23                                   # touchpad off
  KEYBOARD_KEY_d9=!f22                                   # touchpad on
 
index 576b314d3cd82e761a11879492723ecc98c0a35e..092d356b64195972be065d7925695e1918410c42 100644 (file)
@@ -680,6 +680,10 @@ sensor:modalias:acpi:KIOX000A*:dmi:*:svnTREKSTOR:pnPrimetabT13B:*
 sensor:modalias:acpi:BOSC0200*:dmi:*:svnTrekStor*:pnSurfTabtwin11.6:*
  ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
 
+# alternative version of Trekstor's SurfTab Twin 11.6
+sensor:modalias:acpi:BOSC0200*:dmi:*:bvrTP15-VT5.2.1.3:*:svnTrekStor*:pnSurfTabtwin11.6:*
+ ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, -1
+
 sensor:modalias:acpi:KIOX010A*:dmi:*:svnTREKSTOR:pnPrimebookC11B:*
 sensor:modalias:acpi:KIOX010A*:dmi:*:svnTREKSTOR:pnPRIMEBOOKC11B:*
  ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, -1, 0; 0, 0, -1
index 4df6dabf89f6ef94f11b214d08d20487c26f59fb..5c77387a26130454dfe775e5ee7c5edb62827058 100644 (file)
@@ -1,6 +1,9 @@
 # SPDX-License-Identifier: LGPL-2.1+
 
-hwdb_files = files('''
+# Those files right now are not supported by the grammar. Also,
+# they are very long but quite repetitive and the parser is not very fast.
+# So we don't "test" them.
+hwdb_files_notest = files('''
         20-pci-vendor-model.hwdb
         20-pci-classes.hwdb
         20-usb-vendor-model.hwdb
@@ -12,6 +15,10 @@ hwdb_files = files('''
         20-OUI.hwdb
         20-net-ifname.hwdb
         20-vmbus-class.hwdb
+'''.split())
+
+hwdb_files_test = files('''
+        60-autosuspend.hwdb
         60-evdev.hwdb
         60-input-id.hwdb
         60-keyboard.hwdb
@@ -23,7 +30,16 @@ hwdb_files = files('''
 '''.split())
 
 if conf.get('ENABLE_HWDB') == 1
-        install_data(hwdb_files,
+        auto_suspend_rules = custom_target(
+                '60-autosuspend-chromiumos.hwdb',
+                output : '60-autosuspend-chromiumos.hwdb',
+                command : make_autosuspend_rules_py,
+                capture : true,
+                install : true,
+                install_dir: udevhwdbdir)
+
+        install_data(hwdb_files_notest,
+                     hwdb_files_test,
                      install_dir : udevhwdbdir)
 
         meson.add_install_script('sh', '-c',
@@ -32,15 +48,15 @@ if conf.get('ENABLE_HWDB') == 1
         meson.add_install_script('sh', '-c',
                                  'test -n "$DESTDIR" || @0@/systemd-hwdb update'
                                  .format(rootbindir))
-endif
-
-############################################################
 
-parse_hwdb_py = find_program('parse_hwdb.py')
-if want_tests != 'false'
-        test('parse-hwdb',
-             parse_hwdb_py,
-             timeout : 90)
+        if want_tests != 'false'
+                parse_hwdb_py = find_program('parse_hwdb.py')
+                test('parse-hwdb',
+                     parse_hwdb_py,
+                     args : [hwdb_files_test,
+                             auto_suspend_rules],
+                     timeout : 90)
+        endif
 endif
 
 ############################################################
index 579c45fda0218281080d94aef71493e1d171eeb3..025133416f6e23c23ce64f1dfad9bc96f0d84a76 100755 (executable)
@@ -59,6 +59,7 @@ REAL = Combine((INTEGER + Optional('.' + Optional(INTEGER))) ^ ('.' + INTEGER))
 SIGNED_REAL = Combine(Optional(Word('-+')) + REAL)
 UDEV_TAG = Word(string.ascii_uppercase, alphanums + '_')
 
+# Those patterns are used in type-specific matches
 TYPES = {'mouse':    ('usb', 'bluetooth', 'ps2', '*'),
          'evdev':    ('name', 'atkbd', 'input'),
          'id-input': ('modalias'),
@@ -68,13 +69,30 @@ TYPES = {'mouse':    ('usb', 'bluetooth', 'ps2', '*'),
          'sensor':   ('modalias', ),
         }
 
+# Patterns that are used to set general properties on a device
+GENERAL_MATCHES = {'acpi',
+                   'bluetooth',
+                   'usb',
+                   'pci',
+                   'sdio',
+                   'vmbus',
+                   'OUI',
+                   }
+
+def upperhex_word(length):
+    return Word(nums + 'ABCDEF', exact=length)
+
 @lru_cache()
 def hwdb_grammar():
     ParserElement.setDefaultWhitespaceChars('')
 
     prefix = Or(category + ':' + Or(conn) + ':'
                 for category, conn in TYPES.items())
-    matchline = Combine(prefix + Word(printables + ' ' + '®')) + EOL
+
+    matchline_typed = Combine(prefix + Word(printables + ' ' + '®'))
+    matchline_general = Combine(Or(GENERAL_MATCHES) + ':' + Word(printables + ' ' + '®'))
+    matchline = (matchline_typed | matchline_general) + EOL
+
     propertyline = (White(' ', exact=1).suppress() +
                     Combine(UDEV_TAG - '=' - Word(alphanums + '_=:@*.!-;, "') - Optional(pythonStyleComment)) +
                     EOL)
@@ -102,6 +120,7 @@ def property_grammar():
              ('MOUSE_WHEEL_CLICK_ANGLE_HORIZONTAL', INTEGER),
              ('MOUSE_WHEEL_CLICK_COUNT', INTEGER),
              ('MOUSE_WHEEL_CLICK_COUNT_HORIZONTAL', INTEGER),
+             ('ID_AUTOSUSPEND', Literal('1')),
              ('ID_INPUT', Literal('1')),
              ('ID_INPUT_ACCELEROMETER', Literal('1')),
              ('ID_INPUT_JOYSTICK', Literal('1')),
@@ -115,8 +134,6 @@ def property_grammar():
              ('ID_INPUT_TOUCHPAD', Literal('1')),
              ('ID_INPUT_TOUCHSCREEN', Literal('1')),
              ('ID_INPUT_TRACKBALL', Literal('1')),
-             ('MOUSE_WHEEL_TILT_HORIZONTAL', Literal('1')),
-             ('MOUSE_WHEEL_TILT_VERTICAL', Literal('1')),
              ('POINTINGSTICK_SENSITIVITY', INTEGER),
              ('POINTINGSTICK_CONST_ACCEL', REAL),
              ('ID_INPUT_JOYSTICK_INTEGRATION', Or(('internal', 'external'))),
@@ -166,8 +183,27 @@ def parse(fname):
         return []
     return [convert_properties(g) for g in parsed.GROUPS]
 
-def check_match_uniqueness(groups):
+def check_matches(groups):
     matches = sum((group[0] for group in groups), [])
+
+    # This is a partial check. The other cases could be also done, but those
+    # two are most commonly wrong.
+    grammars = { 'usb' : 'v' + upperhex_word(4) + Optional('p' + upperhex_word(4)),
+                 'pci' : 'v' + upperhex_word(8) + Optional('d' + upperhex_word(8)),
+    }
+
+    for match in matches:
+        prefix, rest = match.split(':', maxsplit=1)
+        gr = grammars.get(prefix)
+        if gr:
+            try:
+                gr.parseString(rest)
+            except ParseBaseException as e:
+                error('Pattern {!r} is invalid: {}', rest, e)
+                continue
+            if rest[-1] not in '*:':
+                error('pattern {} does not end with "*" or ":"', match)
+
     matches.sort()
     prev = None
     for match in matches:
@@ -242,7 +278,7 @@ if __name__ == '__main__':
     for fname in args:
         groups = parse(fname)
         print_summary(fname, groups)
-        check_match_uniqueness(groups)
+        check_matches(groups)
         check_properties(groups)
 
     sys.exit(ERROR)
index c038c4686db696785c315677b8e60d046331c8f8..6db048b63be3035a10081922308a541a5313052a 100644 (file)
       </varlistentry>
 
       <varlistentry>
-        <term><option>systemd-efi-options</option> <optional><replaceable>VALUE</replaceable></optional></term>
+        <term><option>systemd-efi-options</option> <optional><replaceable>STRING</replaceable></optional></term>
 
         <listitem><para>When called without the optional argument, prints the current value of the
         <literal>SystemdOptions</literal> EFI variable. When called with an argument, sets the
         for the meaning of that variable.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>reboot-to-firmware</option> <optional><replaceable>BOOL</replaceable></optional></term>
+
+        <listitem><para>Query or set the "Reboot-Into-Firmware-Setup" flag of the EFI firmware. Takes a
+        boolean argument which controls whether to show the firmware setup on next system reboot. If the
+        argument is omitted shows the current status of the flag, or whether the flag is supported. This
+        controls the same flag as <command>systemctl reboot --firmware-setup</command>, but is more
+        low-level and allows setting the flag independently from actually requesting a
+        reboot.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>list</option></term>
 
index 5a0a3c287102805b454395bf17f673978151b8aa..26f762bf816e220c3042fc37c6f22208853e628f 100644 (file)
@@ -32,7 +32,7 @@
     of firmware, this firmware may also load the kernel directly.</para>
 
     <para>The kernel (optionally) mounts an in-memory file system, often generated by
-    <citerefentry project='die-net'><refentrytitle>dracut</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+    <citerefentry project='man-pages'><refentrytitle>dracut</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
     which looks for the root file system. Nowadays this is usually implemented as an initramfs — a compressed
     archive which is extracted when the kernel boots up into a lightweight in-memory file system based on
     tmpfs, but in the past normal file systems using an in-memory block device (ramdisk) were used, and the
@@ -170,7 +170,7 @@ emergency.service    |              |              |
     user units. For non-graphical sessions, <filename>default.target</filename> is used. Whenever the user
     logs into a graphical session, the login manager will start the
     <filename>graphical-session.target</filename> target that is used to pull in units required for the
-    grahpical session. A number of targets (shown on the right side) are started when specific hardware is
+    graphical session. A number of targets (shown on the right side) are started when specific hardware is
     available to the user.</para>
 
 <programlisting>
@@ -192,7 +192,7 @@ emergency.service    |              |              |
              v            graphical-session-pre.target
  (various user services)          |                       (printers)
              |                    v                           |
-             |        (services for the graphical sesion)     v
+             |       (services for the graphical session)     v
              |                    |                       printer.target
              v                    v
       <emphasis>default.target</emphasis>      graphical-session.target</programlisting>
@@ -349,7 +349,7 @@ systemd-reboot.service   systemd-poweroff.service   systemd-halt.service   syste
       <citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd.target</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd-halt.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
-      <citerefentry project='die-net'><refentrytitle>dracut</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+      <citerefentry project='man-pages'><refentrytitle>dracut</refentrytitle><manvolnum>8</manvolnum></citerefentry>
     </para>
   </refsect1>
 
index 81cdc33898f7c31a0ac25378e96ec93ac6058139..46da7741c97e5cf7abcb63cf460f1d31199f31ee 100644 (file)
@@ -49,7 +49,7 @@
     <title>Options</title>
 
     <para>All options are configured in the
-    <literal>[Coredump]</literal> section:</para>
+    [Coredump] section:</para>
 
     <variablelist class='config-directives'>
 
index ab5dffdfb37411dd09da8be8386deb5949af0637..150d410c24cc5d4ad681627aeadfc1cf3cd4f78d 100644 (file)
@@ -52,8 +52,8 @@
         matching specified characteristics. If no command is
         specified, this is the implied default.</para>
 
-        <para>The output is designed to be human readable and contains list contains
-        a table with the following columns:</para>
+        <para>The output is designed to be human readable and contains a table with the following
+        columns:</para>
         <variablelist>
           <varlistentry>
             <term>TIME</term>
 
         <listitem><para>Invoke a debugger on the last core dump
         matching specified characteristics. By default,
-        <citerefentry><refentrytitle>gdb</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+        <citerefentry project='man-pages'><refentrytitle>gdb</refentrytitle><manvolnum>1</manvolnum></citerefentry>
         will be used. This may be changed using the <option>--debugger=</option>
         option or the <varname>$SYSTEMD_DEBUGGER</varname> environment
         variable.</para></listitem>
 
         <listitem><para>Use the given debugger for the <command>debug</command>
         command. If not given and <varname>$SYSTEMD_DEBUGGER</varname> is unset, then
-        <citerefentry><refentrytitle>gdb</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+        <citerefentry project='man-pages'><refentrytitle>gdb</refentrytitle><manvolnum>1</manvolnum></citerefentry>
         will be used. </para></listitem>
       </varlistentry>
 
index 9b6fffd154ef9ad9f1779d6ce119432eb569439d..ee54499bfe7a13c86869b3c7c4e355a0e7d511a5 100644 (file)
@@ -41,7 +41,7 @@
     character are ignored. Each of the remaining lines describes one
     encrypted block device. Fields are delimited by white space.</para>
 
-    <para>Each line is in the form<programlisting><replaceable>name</replaceable> <replaceable>encrypted-device</replaceable> <replaceable>password</replaceable> <replaceable>options</replaceable></programlisting>
+    <para>Each line is in the form<programlisting><replaceable>volume-name</replaceable> <replaceable>encrypted-device</replaceable> <replaceable>key-file</replaceable> <replaceable>options</replaceable></programlisting>
     The first two fields are mandatory, the remaining two are
     optional.</para>
 
     it is opened as a LUKS device; otherwise, it is assumed to be in
     raw dm-crypt (plain mode) format.</para>
 
-    <para>The first field contains the name of the resulting encrypted
-    block device; the device is set up within
-    <filename>/dev/mapper/</filename>.</para>
+    <para>The first field contains the name of the resulting encrypted volume; its block device is set up
+    below <filename>/dev/mapper/</filename>.</para>
 
     <para>The second field contains a path to the underlying block
     device or file, or a specification of a block device via
     <literal>UUID=</literal> followed by the UUID.</para>
 
-    <para>The third field specifies the encryption password. If the
-    field is not present or the password is set to
-    <literal>none</literal> or <literal>-</literal>, the password has
-    to be manually entered during system boot. Otherwise, the field is
-    interpreted as an absolute path to a file containing the encryption
-    password. For swap encryption, <filename>/dev/urandom</filename>
-    or the hardware device <filename>/dev/hw_random</filename> can be
-    used as the password file; using <filename>/dev/random</filename>
-    may prevent boot completion if the system does not have enough
-    entropy to generate a truly random encryption key.</para>
+    <para>The third field specifies an absolute path to a file to read the encryption key from. Optionally,
+    the path may be followed by <literal>:</literal> and an fstab device specification (e.g. starting with
+    <literal>LABEL=</literal> or similar); in which case, the path is relative to the device file system
+    root. If the field is not present or set to <literal>none</literal> or <literal>-</literal>, a key file
+    named after the volume to unlock (i.e. the first column of the line), suffixed with
+    <filename>.key</filename> is automatically loaded from the <filename>/etc/cryptsetup-keys.d/</filename>
+    and <filename>/run/cryptsetup-keys.d/</filename> directories, if present. Otherwise, the password has to
+    be manually entered during system boot. For swap encryption, <filename>/dev/urandom</filename> may be
+    used as key file.</para>
 
     <para>The fourth field, if present, is a comma-delimited list of
     options. The following options are recognized:</para>
         size is then given by the key size.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>keyfile-erase</option></term>
+
+        <listitem><para>If enabled, the specified key file is erased after the volume is activated or when
+        activation fails. This is in particular useful when the key file is only acquired transiently before
+        activation (e.g. via a file in <filename>/run/</filename>, generated by a service running before
+        activation), and shall be removed after use. Defaults to off.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>key-slot=</option></term>
 
         <option>size=</option>.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>bitlk</option></term>
+
+        <listitem><para>Decrypt Bitlocker drive. Encryption parameters
+        are deduced by cryptsetup from Bitlocker header.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>_netdev</option></term>
 
 
         <listitem><para>Perform encryption using the same cpu that IO was submitted on. The default is to use
         an unbound workqueue so that encryption work is automatically balanced between available CPUs.</para>
+
         <para>This requires kernel 4.0 or newer.</para>
         </listitem>
       </varlistentry>
         <term><option>submit-from-crypt-cpus</option></term>
 
         <listitem><para>Disable offloading writes to a separate thread after encryption. There are some
-        situations where offloading write bios from the encryption threads to a single thread degrades
-        performance significantly. The default is to offload write bios to the same thread because it benefits
-        CFQ to have writes submitted using the same context.</para>
+        situations where offloading write requests from the encryption threads to a dedicated thread degrades
+        performance significantly. The default is to offload write requests to a dedicated thread because it
+        benefits the CFQ scheduler to have writes submitted using the same context.</para>
+
         <para>This requires kernel 4.0 or newer.</para>
         </listitem>
       </varlistentry>
       </varlistentry>
 
       <varlistentry>
-        <term><option>tmp</option></term>
+        <term><option>tmp=</option></term>
 
-        <listitem><para>The encrypted block device will be prepared
-        for using it as <filename>/tmp</filename>; it will be
-        formatted using
-        <citerefentry project='man-pages'><refentrytitle>mke2fs</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
-        This option implies <option>plain</option>.</para>
+        <listitem><para>The encrypted block device will be prepared for using it as
+        <filename>/tmp/</filename>; it will be formatted using <citerefentry
+        project='man-pages'><refentrytitle>mkfs</refentrytitle><manvolnum>8</manvolnum></citerefentry>. Takes
+        a file system type as argument, such as <literal>ext4</literal>, <literal>xfs</literal> or
+        <literal>btrfs</literal>. If no argument is specified defaults to <literal>ext4</literal>. This
+        option implies <option>plain</option>.</para>
 
-        <para>WARNING: Using the <option>tmp</option> option will
-        destroy the contents of the named partition during every boot,
-        so make sure the underlying block device is specified
-        correctly.</para></listitem>
+        <para>WARNING: Using the <option>tmp</option> option will destroy the contents of the named partition
+        during every boot, so make sure the underlying block device is specified correctly.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         before it is used to unlock the LUKS volume.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>try-empty-password=</option></term>
+
+        <listitem><para>Takes a boolean argument. If enabled, right before asking the user for a password it
+        is first attempted to unlock the volume with an empty password. This is useful for systems that are
+        initialized with an encrypted volume with only an empty password set, which shall be replaced with a
+        suitable password during first boot, but after activation.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>x-systemd.device-timeout=</option></term>
 
@@ -490,7 +514,8 @@ external   /dev/sda3       keyfile:LABEL=keydev keyfile-timeout=10s</programlist
 
       <para>The PKCS#11 logic allows hooking up any compatible security token that is capable of storing RSA
       decryption keys. Here's an example how to set up a Yubikey security token for this purpose, using
-      <command>ykman</command> from the yubikey-manager project:</para>
+      <citerefentry project='debian'><refentrytitle>ykmap</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+      from the yubikey-manager project:</para>
 
 <programlisting><xi:include href="yubikey-crypttab.sh" parse="text" /></programlisting>
 
index 85805777a0d57727973802d0a26d6b7d3333ccb2..03fe05f1bca66123420e0b738b81e9c4f1491522 100644 (file)
@@ -1,11 +1,12 @@
 <?xml version="1.0" encoding="utf-8" ?>
 <!ENTITY MOUNT_PATH @MOUNT_PATH@>
 <!ENTITY UMOUNT_PATH @UMOUNT_PATH@>
-<!ENTITY systemgeneratordir @SYSTEM_GENERATOR_PATH@>
-<!ENTITY usergeneratordir @USER_GENERATOR_PATH@>
-<!ENTITY systemenvgeneratordir @SYSTEM_ENV_GENERATOR_PATH@>
-<!ENTITY userenvgeneratordir @USER_ENV_GENERATOR_PATH@>
+<!ENTITY systemgeneratordir @SYSTEM_GENERATOR_DIR@>
+<!ENTITY usergeneratordir @USER_GENERATOR_DIR@>
+<!ENTITY systemenvgeneratordir @SYSTEM_ENV_GENERATOR_DIR@>
+<!ENTITY userenvgeneratordir @USER_ENV_GENERATOR_DIR@>
 <!ENTITY CERTIFICATE_ROOT @CERTIFICATE_ROOT@>
+<!ENTITY FALLBACK_HOSTNAME @FALLBACK_HOSTNAME@>
 <!ENTITY MEMORY_ACCOUNTING_DEFAULT @MEMORY_ACCOUNTING_DEFAULT_YES_NO@>
 <!ENTITY KILL_USER_PROCESSES @KILL_USER_PROCESSES_YES_NO@>
 <!ENTITY DEBUGTTY @DEBUGTTY@>
index 52b3883607cf9c8ea49339512b24ba8d0a515bd6..072529eeecbce9422e0a95c907c3dcf094020b57 100644 (file)
       special target unit <filename>sockets.target</filename>. It is
       recommended to place a
       <varname>WantedBy=sockets.target</varname> directive in the
-      <literal>[Install]</literal> section to automatically add such a
+      [Install] section to automatically add such a
       dependency on installation of a socket unit. Unless
       <varname>DefaultDependencies=no</varname> is set, the necessary
       ordering dependencies are implicitly created for all socket
         operating system-independent.</para></listitem>
 
         <listitem><para>Make sure to include an
-        <literal>[Install]</literal> section including installation
+        [Install] section including installation
         information for the unit file. See
         <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
         for details. To activate your service on boot, make sure to
diff --git a/man/directives-template.xml b/man/directives-template.xml
new file mode 100644 (file)
index 0000000..e0063e0
--- /dev/null
@@ -0,0 +1,199 @@
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="systemd.directives">
+  <refentryinfo>
+    <title>systemd.directives</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>systemd.directives</refentrytitle>
+    <manvolnum>7</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>systemd.directives</refname>
+    <refpurpose>Index of configuration directives</refpurpose>
+  </refnamediv>
+
+  <refsect1>
+    <title>Unit directives</title>
+
+    <para>Directives for configuring units, used in unit files.</para>
+
+    <variablelist id='unit-directives' />
+  </refsect1>
+
+  <refsect1>
+    <title>Options on the kernel command line</title>
+
+    <para>Kernel boot options for configuring the behaviour of the systemd process.</para>
+
+    <variablelist id='kernel-commandline-options' />
+  </refsect1>
+
+  <refsect1>
+    <title>Environment variables</title>
+
+    <para>Environment variables understood by the systemd manager and other programs and environment
+    variable-compatible settings.</para>
+
+    <variablelist id='environment-variables' />
+  </refsect1>
+
+  <refsect1>
+    <title>EFI variables</title>
+
+    <para>EFI variables understood by
+    <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+    and other programs.</para>
+
+    <variablelist id='efi-variables' />
+  </refsect1>
+
+  <refsect1>
+    <title>Home Area/User Account directives</title>
+
+    <para>Directives for configuring home areas and user accounts via
+    <citerefentry><refentrytitle>systemd-homed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
+
+    <variablelist id='home-directives' />
+  </refsect1>
+
+  <refsect1>
+    <title>UDEV directives</title>
+
+    <para>Directives for configuring systemd units through the udev database.</para>
+
+    <variablelist id='udev-directives' />
+  </refsect1>
+
+  <refsect1>
+    <title>Network directives</title>
+
+    <para>Directives for configuring network links through the net-setup-link udev builtin and networks
+    through systemd-networkd.</para>
+
+    <variablelist id='network-directives' />
+  </refsect1>
+
+  <refsect1>
+    <title>Journal fields</title>
+
+    <para>Fields in the journal events with a well known meaning.</para>
+
+    <variablelist id='journal-directives' />
+  </refsect1>
+
+  <refsect1>
+    <title>PAM configuration directives</title>
+
+    <para>Directives for configuring PAM behaviour.</para>
+
+    <variablelist id='pam-directives' />
+  </refsect1>
+
+  <refsect1>
+    <title><filename>/etc/crypttab</filename> and
+    <filename>/etc/fstab</filename> options</title>
+
+    <para>Options which influence mounted filesystems and encrypted volumes.</para>
+
+    <variablelist id='fstab-options' />
+  </refsect1>
+
+  <refsect1>
+    <title><citerefentry><refentrytitle>systemd.nspawn</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    directives</title>
+
+    <para>Directives for configuring systemd-nspawn containers.</para>
+
+    <variablelist id='nspawn-directives' />
+  </refsect1>
+
+  <refsect1>
+    <title>Program configuration options</title>
+
+    <para>Directives for configuring the behaviour of the systemd process and other tools through
+    configuration files.</para>
+
+    <variablelist id='config-directives' />
+  </refsect1>
+
+  <refsect1>
+    <title>Command line options</title>
+
+    <para>Command-line options accepted by programs in the systemd suite.</para>
+
+    <variablelist id='options' />
+  </refsect1>
+
+  <refsect1>
+    <title>Constants</title>
+
+    <para>Various constant used and/or defined by systemd.</para>
+
+    <variablelist id='constants' />
+  </refsect1>
+
+  <refsect1>
+    <title>Miscellaneous options and directives</title>
+
+    <para>Other configuration elements which don't fit in any of the above groups.</para>
+
+    <variablelist id='miscellaneous' />
+  </refsect1>
+
+  <refsect1>
+    <title>Specifiers</title>
+
+    <para>Short strings which are substituted in configuration directives.</para>
+
+    <variablelist id='specifiers' />
+  </refsect1>
+
+  <refsect1>
+    <title>Files and directories</title>
+
+    <para>Paths and file names referred to in the documentation.</para>
+
+    <variablelist id='filenames' />
+  </refsect1>
+
+  <refsect1>
+    <title>D-Bus interfaces</title>
+
+    <para>Interfaces exposed over D-Bus.</para>
+
+    <variablelist id='dbus-interface' />
+  </refsect1>
+
+  <refsect1>
+    <title>D-Bus methods</title>
+
+    <para>Methods exposed in the D-Bus interface.</para>
+
+    <variablelist id='dbus-method' />
+  </refsect1>
+
+  <refsect1>
+    <title>D-Bus properties</title>
+
+    <para>Properties exposed in the D-Bus interface.</para>
+
+    <variablelist id='dbus-property' />
+  </refsect1>
+
+  <refsect1>
+    <title>D-Bus signals</title>
+
+    <para>Signals emitted in the D-Bus interface.</para>
+
+    <variablelist id='dbus-signal' />
+  </refsect1>
+
+  <refsect1>
+    <title>Colophon</title>
+    <para id='colophon' />
+  </refsect1>
+</refentry>
index 17c2c505edf0cf0e61f38aa96f3348c579fe0cbf..0f53b0fef106780c6b58de53089c3cd155f5af69 100644 (file)
@@ -22,7 +22,7 @@
 
   <refnamediv>
     <refname>environment.d</refname>
-    <refpurpose>Definition of user session environment</refpurpose>
+    <refpurpose>Definition of user service environment</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
@@ -36,8 +36,8 @@
   <refsect1>
     <title>Description</title>
 
-    <para>The <filename>environment.d</filename> directories contain a list of environment variable
-    assignments for services started by the systemd user instance.
+    <para>Configuration files in the <filename>environment.d/</filename> directories contain lists of
+    environment variable assignments for services started by the systemd user instance.
     <citerefentry><refentrytitle>systemd-environment-d-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
     parses them and updates the environment exported by the systemd user instance. See below for an
     discussion of which processes inherit those variables.</para>
@@ -58,7 +58,6 @@
     variable assignments, separated by newlines. The right hand side of these assignments may
     reference previously defined environment variables, using the <literal>${OTHER_KEY}</literal>
     and <literal>$OTHER_KEY</literal> format. It is also possible to use
-
     <literal>${<replaceable>FOO</replaceable>:-<replaceable>DEFAULT_VALUE</replaceable>}</literal>
     to expand in the same way as <literal>${<replaceable>FOO</replaceable>}</literal> unless the
     expansion would be empty, in which case it expands to <replaceable>DEFAULT_VALUE</replaceable>,
@@ -95,7 +94,7 @@
     <para>Environment variables exported by the user manager (<command>systemd --user</command> instance
     started in the <filename>user@<replaceable>uid</replaceable>.service</filename> system service) apply to
     any services started by that manager. In particular, this may include services which run user shells. For
-    example in the Gnome environment, the graphical terminal emulator runs as the
+    example in the GNOME environment, the graphical terminal emulator runs as the
     <filename>gnome-terminal-server.service</filename> user unit, which in turn runs the user shell, so that
     shell will inherit environment variables exported by the user manager. For other instances of the shell,
     not launched by the user manager, the environment they inherit is defined by the program that starts
index 497cb580c73165e61048365fca7b76201429664f..d5899dc362183d90402d6b5c8354e8aedadaecbe 100644 (file)
     <filename>/usr/share/</filename> hierarchy to the locations
     defined by the various relevant specifications.</para>
 
-    <para>During runtime, and for local configuration and state,
+    <para>During runtime, and for local configuration and runtime state,
     additional directories are defined:</para>
 
     <table>
index 23348f00ba5da08ed9e158ab219c01e942f20028..0724749c07a43857d71b6cf26c1da908c3ab8f32 100644 (file)
@@ -50,7 +50,7 @@
       <listitem><para>An individual LUKS2 encrypted loopback file for a user, stored in
       <filename>/home/*.home</filename>. At login the file system contained in this files is mounted, after
       the LUKS2 encrypted volume has been attached. The user's password is identical to the encryption
-      passphrase of the LUKS2 volume. Access to data without preceeding user authentication is thus not
+      passphrase of the LUKS2 volume. Access to data without preceding user authentication is thus not
       possible, even for the system administrator. This storage mechanism provides the strongest data
       security and is thus recommended.</para></listitem>
 
         <term><option>--identity=</option><replaceable>FILE</replaceable></term>
 
         <listitem><para>Read the user's JSON record from the specified file. If passed as
-        <literal>-</literal> reads the user record from standard input. The supplied JSON object must follow
-        the structure documented on <ulink url="https://systemd.io/USER_RECORDS">JSON User
-        Records</ulink>. This option may be used in conjunction with the <command>create</command> and
+        <literal>-</literal> read the user record from standard input. The supplied JSON object must follow
+        the structure documented on <ulink url="https://systemd.io/USER_RECORD">JSON User Records</ulink>.
+        This option may be used in conjunction with the <command>create</command> and
         <command>update</command> commands (see below), where it allows configuring the user record in JSON
         as-is, instead of setting the individual user record properties (see below).</para></listitem>
       </varlistentry>
         different system and the configured UID is taken by another user there, then
         <command>systemd-homed</command> may assign the user a different UID on that system. The specified
         UID must be outside of the system user range. It is recommended to use the 60001…60513 UID range for
-        this purpose. If not specified the UID is automatically picked. When logging in and the home
-        directory is found to be owned by a UID not matching the user's assigned one the home directory and
-        all files and directories inside it will have their ownership changed automatically before login
-        completes.</para>
+        this purpose. If not specified, the UID is automatically picked. If the home directory is found to be
+        owned by a different UID when logging in, the home directory and everything underneath it will have
+        its ownership changed automatically before login completes.</para>
 
         <para>Note that users managed by <command>systemd-homed</command> always have a matching group
         associated with the same name as well as a GID matching the UID of the user. Thus, configuring the
         privileges. Note that <command>systemd-homed</command> does not manage any groups besides a group
         matching the user in name and numeric UID/GID. Thus any groups listed here must be registered
         independently, for example with <citerefentry
-        project='man-pages'><refentrytitle>groupadd</refentrytitle><manvolnum>8</manvolnum></citerefentry>. If
-        non-existant groups that are listed there are ignored. This option may be used more than once, in
-        which case all specified group lists are combined.</para></listitem>
+        project='man-pages'><refentrytitle>groupadd</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+        Any non-existent groups are ignored. This option may be used more than once, in which case all
+        specified group lists are combined. If the user is currently a member of a group which is not listed,
+        the user will be removed from the group.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><option>--skel=</option><replaceable>PATH</replaceable></term>
 
         <listitem><para>Takes a file system path to a directory. Specifies the skeleton directory to
-        initialize the home directory with. All files and directories in the specified are copied into any
-        newly create home directory. If not specified defaults to
-        <filename>/etc/skel/</filename>.</para></listitem>
+        initialize the home directory with. All files and directories in the specified path are copied into
+        any newly create home directory. If not specified defaults to <filename>/etc/skel/</filename>.
+        </para></listitem>
       </varlistentry>
 
       <varlistentry>
         <listitem><para>Takes a specifier indicating the preferred language of the user. The
         <varname>$LANG</varname> environment variable is initialized from this value on login, and thus a
         value suitable for this environment variable is accepted here, for example
-        <option>--language=de_DE.UTF8</option></para></listitem>
+        <option>--language=de_DE.UTF8</option>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         security token with exactly one pair of X.509 certificate and private key. A random secret key is
         then generated, encrypted with the public key of the X.509 certificate, and stored as part of the
         user record. At login time it is decrypted with the PKCS#11 module and then used to unlock the
-        account and associated resources. See below for an example how to set up authentication with security
-        token.</para></listitem>
+        account and associated resources. See below for an example how to set up authentication with a
+        security token.</para>
+
+        <para>Instead of a valid PKCS#11 URI, the special strings <literal>list</literal> and
+        <literal>auto</literal> may be specified. If <literal>list</literal> is passed, a brief table of
+        suitable, currently plugged in PKCS#11 hardware tokens is shown, along with their URIs. If
+        <literal>auto</literal> is passed, a suitable PKCS#11 hardware token is automatically selected (this
+        operation will fail if there isn't exactly one suitable token discovered). The latter is a useful
+        shortcut for the most common case where a single PKCS#11 hardware token is plugged in.</para>
+
+        <para>Note that many hardware security tokens implement both PKCS#11/PIV and FIDO2 with the
+        <literal>hmac-secret</literal> extension (for example: the YubiKey 5 series), as supported with the
+        <option>--fido2-device=</option> option below. Both mechanisms are similarly powerful, though FIDO2
+        is the more modern technology. PKCS#11/PIV tokens have the benefit of being recognizable before
+        authentication and hence can be used for implying the user identity to use for logging in, which
+        FIDO2 does not allow. PKCS#11/PIV devices generally require initialization (i.e. storing a
+        private/public key pair on them, see example below) before they can be used; FIDO2 security tokens
+        generally do not required that, and work out of the box.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--fido2-device=</option><replaceable>PATH</replaceable></term>
+
+        <listitem><para>Takes a path to a Linux <literal>hidraw</literal> device
+        (e.g. <filename>/dev/hidraw1</filename>), referring to a FIDO2 security token implementing the
+        <literal>hmac-secret</literal> extension, that shall be able to unlock the user account. If used, a
+        random salt value is generated on the host, which is passed to the FIDO2 device, which calculates a
+        HMAC hash of it, keyed by its internal secret key. The result is then used as key for unlocking the
+        user account. The random salt is included in the user record, so that whenever authentication is
+        needed it can be passed again to the FIDO2 token, to retrieve the actual key.</para>
+
+        <para>Instead of a valid path to a FIDO2 <literal>hidraw</literal> device the special strings
+        <literal>list</literal> and <literal>auto</literal> may be specified. If <literal>list</literal> is
+        passed, a brief table of suitable discovered FIDO2 devices is shown. If <literal>auto</literal> is
+        passed, a suitable FIDO2 token is automatically selected, if exactly one is discovered. The latter is
+        a useful shortcut for the most common case where a single FIDO2 hardware token is plugged in.</para>
+
+        <para>Note that FIDO2 devices suitable for this option must implement the
+        <literal>hmac-secret</literal> extension. Most current devices (such as the YubiKey 5 series) do. If
+        the extension is not implemented the device cannot be used for unlocking home directories.</para>
+
+        <para>Note that many hardware security tokens implement both FIDO2 and PKCS#11/PIV (and thus may be
+        used with either <option>--fido2-device=</option> or <option>--pkcs11-token-uri=</option>), for a
+        discussion see above.</para></listitem>
       </varlistentry>
 
       <varlistentry>
 
         <listitem><para>Each of these options takes a time span specification as argument (in the syntax
         documented in
-        <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>5</manvolnum></citerefentry>) and
-        configure various aspects of the user's password expiration policy. Specifically,
+        <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>) and
+        configures various aspects of the user's password expiration policy. Specifically,
         <option>--password-change-min=</option> configures how much time has to pass after changing the
         password of the user until the password may be changed again. If the user tries to change their
         password before this time passes the attempt is refused. <option>--password-change-max=</option>
-        configures how much time has to pass after the the password is changed until the password expires and
-        needs to be changed again. After this time passes any attempts to log in may only proceed after the
-        password is changed. <option>--password-change-warn=</option> specifies how much earlier than then
-        the time configured with <option>--password-change-max=</option> the user is warned at login to
-        change their password as it will expire soon. Finally <option>--password-change-inactive=</option>
-        configures the time which has to pass after the password as expired until the user is not permitted
-        to log in or change the password anymore. Note that these options only apply to password
-        authentication, and do not apply to other forms of authentication, for example PKCS#11-based security
-        token authentication.</para></listitem>
+        configures how soon after it has been changed the password expires and needs to be changed again.
+        After this time passes logging in may only proceed after the password is changed.
+        <option>--password-change-warn=</option> specifies how much earlier than then the time configured
+        with <option>--password-change-max=</option> the user is warned at login to change their password as
+        it will expire soon. Finally <option>--password-change-inactive=</option> configures the time which
+        has to pass after the password as expired until the user is not permitted to log in or change the
+        password anymore. Note that these options only apply to password authentication, and do not apply to
+        other forms of authentication, for example PKCS#11-based security token
+        authentication.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <citerefentry project='man-pages'><refentrytitle>su</refentrytitle><manvolnum>1</manvolnum></citerefentry>
         or a similar tool. Use <option>--rlimit=LIMIT_NPROC=</option> to place a limit on the tasks actually
         running under the UID of the user, thus excluding any child processes that might have changed user
-        identity. This controls the <varname>TasksMax=</varname> settting of the per-user systemd slice unit
+        identity. This controls the <varname>TasksMax=</varname> setting of the per-user systemd slice unit
         <filename>user-$UID.slice</filename>. See
         <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>
         for further details.</para></listitem>
         <term><option>--cpu-weight=</option><replaceable>WEIGHT</replaceable></term>
         <term><option>--io-weight=</option><replaceable>WEIGHT</replaceable></term>
 
-        <listitem><para>Set CPU and IO scheduling weights of the processes of the user, including those of
+        <listitem><para>Set CPU and IO scheduling weights of the processes of the user, including those of
         processes forked off by the user that changed user credentials. Takes a numeric value in the range
         1…10000. This controls the <varname>CPUWeight=</varname> and <varname>IOWeight=</varname> settings of
         the per-user systemd slice unit <filename>user-$UID.slice</filename>. See
         <listitem><para>Selects the storage mechanism to use for this home directory. Takes one of
         <literal>luks</literal>, <literal>fscrypt</literal>, <literal>directory</literal>,
         <literal>subvolume</literal>, <literal>cifs</literal>. For details about these mechanisms, see
-        above. If a new home directory is created and the storage type is not specifically specified defaults
-        to <literal>luks</literal> if supported, <literal>subvolume</literal> as first fallback if supported,
-        and <literal>directory</literal> if not.</para></listitem>
+        above. If a new home directory is created and the storage type is not specifically specified,
+        <citerefentry><refentrytitle>homed.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+        defines which default storage to use.</para></listitem>
       </varlistentry>
 
       <varlistentry>
 
         <listitem><para>When LUKS2 storage is used configures the file system type to use inside the home
         directory LUKS2 container. One of <literal>ext4</literal>, <literal>xfs</literal>,
-        <literal>btrfs</literal>. If not specified defaults to <literal>ext4</literal>. Note that
-        <literal>xfs</literal> is not recommended as its support for file system resizing is too
-        limited.</para></listitem>
+        <literal>btrfs</literal>. If not specified
+        <citerefentry><refentrytitle>homed.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+        defines which default file system type to use. Note that <literal>xfs</literal> is not recommended as
+        its support for file system resizing is too limited.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         loopback file) the discard logic defaults to on.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--luks-offline-discard=</option><replaceable>BOOL</replaceable></term>
+
+        <listitem><para>Similar to <option>--luks-discard=</option>, controls the trimming of the file
+        system. However, while <option>--luks-discard=</option> controls what happens when the home directory
+        is active, <option>--luks-offline-discard=</option> controls what happens when it becomes inactive,
+        i.e. whether to trim/allocate the storage when deactivating the home directory. This option defaults
+        to on, to ensure disk space is minimized while a user is not logged in.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>--luks-cipher=</option><replaceable>CIPHER</replaceable></term>
         <term><option>--luks-cipher-mode=</option><replaceable>MODE</replaceable></term>
         <para>Activation of a home directory involves various operations that depend on the selected storage
         mechanism. If the LUKS2 mechanism is used, this generally involves: inquiring the user for a
         password, setting up a loopback device, validating and activating the LUKS2 volume, checking the file
-        system, mounting the file system, and potentiatlly changing the ownership of all included files to
-        the correct UID/GID.</para></listitem>
+        system, mounting the file system, and potentially changing the ownership of all included files to the
+        correct UID/GID.</para></listitem>
       </varlistentry>
 
       <varlistentry>
       <varlistentry>
         <term><command>passwd</command> <replaceable>USER</replaceable></term>
 
-        <listitem><para>Change the password of the specified home direcory/user account.</para></listitem>
+        <listitem><para>Change the password of the specified home directory/user account.</para></listitem>
       </varlistentry>
 
       <varlistentry>
     </example>
 
     <example>
-      <title>Set up authentication with a YubiKey security token:</title>
+      <title>Set up authentication with a YubiKey security token using PKCS#11/PIV:</title>
 
       <programlisting># Clear the Yubikey from any old keys (careful!)
 ykman piv reset
@@ -809,16 +862,18 @@ ykman piv generate-key -a RSA2048 9d pubkey.pem
 # Create a self-signed certificate from this public key, and store it on the device.
 ykman piv generate-certificate --subject "Knobelei" 9d pubkey.pem
 
-# We don't need the publibc key on disk anymore
+# We don't need the public key on disk anymore
 rm pubkey.pem
 
-# Check if the newly create key on the Yubikey shows up as token in PKCS#11. Have a look at the output, and
-# copy the resulting token URI to the clipboard.
-p11tool --list-tokens
+# Allow the security token to unlock the account of user 'lafcadio'.
+homectl update lafcadio --pkcs11-token-uri=auto</programlisting>
+    </example>
+
+    <example>
+      <title>Set up authentication with a FIDO2 security token:</title>
 
-# Allow the security token referenced by the determined PKCS#11 URI to unlock the account of user
-# 'lafcadio'. (Replace the '…' by the URI from the clipboard.)
-homectl update lafcadio --pkcs11-token-uri=…</programlisting>
+      <programlisting># Allow a FIDO2 security token to unlock the account of user 'nihilbaxter'.
+homectl update nihilbaxter --fido2-device=auto</programlisting>
     </example>
   </refsect1>
 
@@ -827,6 +882,7 @@ homectl update lafcadio --pkcs11-token-uri=…</programlisting>
     <para>
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd-homed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>homed.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>userdbctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry project='man-pages'><refentrytitle>useradd</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
       <citerefentry project='man-pages'><refentrytitle>cryptsetup</refentrytitle><manvolnum>8</manvolnum></citerefentry>
diff --git a/man/homed.conf.xml b/man/homed.conf.xml
new file mode 100644 (file)
index 0000000..2c5f335
--- /dev/null
@@ -0,0 +1,84 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="homed.conf" conditional='ENABLE_HOMED'
+    xmlns:xi="http://www.w3.org/2001/XInclude">
+  <refentryinfo>
+    <title>homed.conf</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>homed.conf</refentrytitle>
+    <manvolnum>5</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>homed.conf</refname>
+    <refname>homed.conf.d</refname>
+    <refpurpose>Home area/user account manager configuration files</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <para><filename>/etc/systemd/homed.conf</filename></para>
+    <para><filename>/etc/systemd/homed.conf.d/*.conf</filename></para>
+    <para><filename>/run/systemd/homed.conf.d/*.conf</filename></para>
+    <para><filename>/usr/lib/systemd/homed.conf.d/*.conf</filename></para>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>These configuration files control default parameters for home areas/user accounts created and
+    managed by
+    <citerefentry><refentrytitle>systemd-homed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
+
+  </refsect1>
+
+  <xi:include href="standard-conf.xml" xpointer="main-conf" />
+
+  <refsect1>
+    <title>Options</title>
+
+    <para>The following options are available in the [Home] section:</para>
+
+    <variablelist class='home-directives'>
+
+      <varlistentry>
+        <term><varname>DefaultStorage=</varname></term>
+        <listitem><para>The default storage to use for home areas. Takes one of <literal>luks</literal>,
+        <literal>fscrypt</literal>, <literal>directory</literal>, <literal>subvolume</literal>,
+        <literal>cifs</literal>. For details about these options, see
+        <citerefentry><refentrytitle>homectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>. If not
+        configured or assigned the empty string, the default storage is automatically determined: if not
+        running in a container environment and <filename>/home/</filename> is not itself encrypted, defaults
+        to <literal>luks</literal>. Otherwise defaults to <literal>subvolume</literal> if
+        <filename>/home/</filename> is on a btrfs file system, and <literal>directory</literal>
+        otherwise. Note that the storage selected on the <command>homectl</command> command line always takes
+        precedence.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>DefaultFileSystemType=</varname></term>
+        <listitem><para>When using <literal>luks</literal> as storage (see above), selects the default file
+        system to use inside the user's LUKS volume. Takes one of <literal>ext4</literal>,
+        <literal>xfs</literal> or <literal>btrfs</literal>. If not specified defaults to
+        <literal>ext4</literal>. This setting has no effect if a different storage mechanism is used. The
+        file system type selected on the <command>homectl</command> command line always takes
+        precedence.</para></listitem>
+      </varlistentry>
+
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+      <title>See Also</title>
+      <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-homed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+      </para>
+  </refsect1>
+
+</refentry>
index 03293382e6e0b56fd748bac1c24a5a8e9e6f2ca3..7ca62f48105a7ab822e6807ac6c502118d33612f 100644 (file)
@@ -57,7 +57,7 @@
 
     <para>Use
     <citerefentry><refentrytitle>systemd-firstboot</refentrytitle><manvolnum>1</manvolnum></citerefentry>
-    to initialize the system host name for mounted (but not booted)
+    to initialize the system hostname for mounted (but not booted)
     system images.</para>
   </refsect1>
 
@@ -84,7 +84,7 @@
         simplified in regards to the character set used before the latter are updated. This is done by removing special
         characters and spaces. This ensures that the pretty and the static hostname are always closely related while
         still following the validity rules of the specific name. This simplification of the hostname string is not done
-        if only the transient and/or static host names are set, and the pretty host name is left untouched.</para>
+        if only the transient and/or static hostnames are set, and the pretty hostname is left untouched.</para>
 
         <para>Pass the empty string <literal></literal> as the
         hostname to reset the selected hostnames to their default
index bc9a668c23f0a6dc8a0ca15bca09834ef16db0cf..b7e79841d080f47ef61d32e367987f80cc0888a1 100755 (executable)
@@ -6,6 +6,9 @@ if [ -z "$1" ]; then
     exit 1
 fi
 
+# make sure the rules have been regenerated (in case man/update-man-rules was just run)
+ninja -C "@BUILD_ROOT@" version.h
+
 target="man/$1.html"
 ninja -C "@BUILD_ROOT@" "$target"
 set -x
diff --git a/man/hwdb-usb-device.c b/man/hwdb-usb-device.c
new file mode 100644 (file)
index 0000000..8a4b86e
--- /dev/null
@@ -0,0 +1,28 @@
+#include <stdio.h>
+#include <stdint.h>
+#include <sd-hwdb.h>
+
+int print_usb_properties(uint16_t vid, uint16_t pid) {
+  char match[15];
+  sd_hwdb *hwdb;
+  const char *key, *value;
+  int r;
+
+  /* Match this USB vendor and product ID combination */
+  snprintf(match, sizeof match, "usb:v%04Xp%04X", vid, pid);
+
+  r = sd_hwdb_new(&hwdb);
+  if (r < 0)
+    return r;
+
+  SD_HWDB_FOREACH_PROPERTY(hwdb, match, key, value)
+    printf("%s: \"%s\" → \"%s\"\n", match, key, value);
+
+  sd_hwdb_unref(hwdb);
+  return 0;
+}
+
+int main(int argc, char **argv) {
+  print_usb_properties(0x046D, 0xC534);
+  return 0;
+}
index ab3dfd0111fbe005c77e47208437135f72d16c3c..f6a9f1565e4b799b0c7b61ece28d95d7a15be90c 100644 (file)
@@ -39,7 +39,7 @@
     <para>These files configure various parameters of
     <citerefentry><refentrytitle>systemd-journal-remote.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
     See
-    <citerefentry><refentrytitle>systemd.syntax</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    <citerefentry><refentrytitle>systemd.syntax</refentrytitle><manvolnum>7</manvolnum></citerefentry>
     for a general description of the syntax.</para>
   </refsect1>
 
@@ -49,7 +49,7 @@
     <title>Options</title>
 
     <para>All options are configured in the
-    <literal>[Remote]</literal> section:</para>
+    [Remote] section:</para>
 
     <variablelist class='config-directives'>
       <varlistentry>
index f2721a75e7d271669a3fcf90d870f516646f757f..2a67e6fd055ad86e4dfbe1947f72f888b23752ce 100644 (file)
@@ -34,7 +34,7 @@
     <para>These files configure various parameters of
     <citerefentry><refentrytitle>systemd-journal-upload.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
     See
-    <citerefentry><refentrytitle>systemd.syntax</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    <citerefentry><refentrytitle>systemd.syntax</refentrytitle><manvolnum>7</manvolnum></citerefentry>
     for a general description of the syntax.</para>
   </refsect1>
 
@@ -43,7 +43,7 @@
   <refsect1>
     <title>Options</title>
 
-    <para>All options are configured in the <literal>[Upload]</literal> section:</para>
+    <para>All options are configured in the [Upload] section:</para>
 
     <variablelist class='config-directives'>
       <varlistentry>
index 1e5f95e19d40b7dfeccbd7093f82d8caba2829af..07310d90a1a99214f92ecb2fa18674602e751703 100644 (file)
                 <option>cat</option>
               </term>
               <listitem>
-                <para>generates a very terse output, only showing the
-                actual message of each journal entry with no metadata,
-                not even a timestamp.</para>
+                <para>generates a very terse output, only showing the actual message of each journal entry
+                with no metadata, not even a timestamp. If combined with the
+                <option>--output-fields=</option> option will output the listed fields for each log record,
+                instead of the message.</para>
               </listitem>
             </varlistentry>
 
       <varlistentry>
         <term><option>--output-fields=</option></term>
 
-        <listitem><para>A comma separated list of the fields which should be included in the output. This has an
-        effect only for the output modes which would normally show all fields (<option>verbose</option>,
-        <option>export</option>, <option>json</option>, <option>json-pretty</option>, <option>json-sse</option> and
-        <option>json-seq</option>). The <literal>__CURSOR</literal>, <literal>__REALTIME_TIMESTAMP</literal>,
+        <listitem><para>A comma separated list of the fields which should be included in the output. This has
+        an effect only for the output modes which would normally show all fields (<option>verbose</option>,
+        <option>export</option>, <option>json</option>, <option>json-pretty</option>,
+        <option>json-sse</option> and <option>json-seq</option>), as well as on <option>cat</option>. For the
+        former, the <literal>__CURSOR</literal>, <literal>__REALTIME_TIMESTAMP</literal>,
         <literal>__MONOTONIC_TIMESTAMP</literal>, and <literal>_BOOT_ID</literal> fields are always
         printed.</para></listitem>
       </varlistentry>
         is also added for <literal>_SYSTEMD_SLICE=<replaceable>UNIT</replaceable></literal>,
         such that if the provided <replaceable>UNIT</replaceable> is a
         <citerefentry><refentrytitle>systemd.slice</refentrytitle><manvolnum>5</manvolnum></citerefentry>
-        unit, all logs of the children of the slice will be logged.
+        unit, all logs of children of the slice will be shown.
        </para>
 
         <para>This parameter can be specified multiple times.</para>
         is also added for <literal>_SYSTEMD_USER_SLICE=<replaceable>UNIT</replaceable></literal>,
         such that if the provided <replaceable>UNIT</replaceable> is a
         <citerefentry><refentrytitle>systemd.slice</refentrytitle><manvolnum>5</manvolnum></citerefentry>
-        unit, all logs of the children of the unit will be logged.</para>
+        unit, all logs of children of the unit will be shown.</para>
 
         <para>This parameter can be specified multiple times.</para>
         </listitem>
         <listitem><para>Filter output to entries where the <varname>MESSAGE=</varname>
         field matches the specified regular expression. PERL-compatible regular expressions
         are used, see
-        <citerefentry><refentrytitle>pcre2pattern</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+        <citerefentry project='url'><refentrytitle url='http://pcre.org/current/doc/html/pcre2pattern.html'>pcre2pattern</refentrytitle><manvolnum>3</manvolnum></citerefentry>
         for a detailed description of the syntax.</para>
 
         <para>If the pattern is all lowercase, matching is case insensitive.
       <varlistentry>
         <term><option>--case-sensitive<optional>=BOOLEAN</optional></option></term>
 
-        <listitem><para>Make pattern matching case sensitive or case insenstive.</para>
+        <listitem><para>Make pattern matching case sensitive or case insensitive.</para>
         </listitem>
       </varlistentry>
 
         underneath the specified directory instead of the root
         directory (e.g. <option>--update-catalog</option> will create
         <filename><replaceable>ROOT</replaceable>/var/lib/systemd/catalog/database</filename>,
-        and journal files under <filename><replaceable>ROOT</replaceable>/run/journal</filename>
-        or <filename><replaceable>ROOT</replaceable>/var/log/journal</filename> will be displayed).
+        and journal files under <filename><replaceable>ROOT</replaceable>/run/journal/</filename>
+        or <filename><replaceable>ROOT</replaceable>/var/log/journal/</filename> will be displayed).
         </para></listitem>
       </varlistentry>
 
         <filename>/run/log/journal/</filename> into <filename>/var/log/journal/</filename>, if persistent
         storage is enabled. This call does not return until the operation is complete. Note that this call is
         idempotent: the data is only flushed from <filename>/run/log/journal/</filename> into
-        <filename>/var/log/journal</filename> once during system runtime (but see
+        <filename>/var/log/journal/</filename> once during system runtime (but see
         <option>--relinquish-var</option> below), and this command exits cleanly without executing any
         operation if this has already happened. This command effectively guarantees that all data is flushed
-        to <filename>/var/log/journal</filename> at the time it returns.</para></listitem>
+        to <filename>/var/log/journal/</filename> at the time it returns.</para></listitem>
       </varlistentry>
 
       <varlistentry>
@@ -1020,7 +1022,7 @@ journalctl _SYSTEMD_CGROUP=/user.slice/user-42.slice/session-c1.scope</programli
   + OBJECT_SYSTEMD_UNIT=<replaceable>name</replaceable>.service _UID=0
   + COREDUMP_UNIT=<replaceable>name</replaceable>.service _UID=0 MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1
     </programlisting>
-    (see <citerefentry><refentrytitle>systemd.journal-fields</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    (see <citerefentry><refentrytitle>systemd.journal-fields</refentrytitle><manvolnum>7</manvolnum></citerefentry>
     for an explanation of those patterns).
     </para>
 
index 957ec36a68c7d5cb3f9c814b8287be28384a12a6..bfd359a903e0822cd8124f228668c4636f185f16 100644 (file)
@@ -36,7 +36,7 @@
     <para>These files configure various parameters of the systemd journal service,
     <citerefentry><refentrytitle>systemd-journald.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
     See
-    <citerefentry><refentrytitle>systemd.syntax</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    <citerefentry><refentrytitle>systemd.syntax</refentrytitle><manvolnum>7</manvolnum></citerefentry>
     for a general description of the syntax.</para>
 
     <para>The <command>systemd-journald</command> instance managing the default namespace is configured by
@@ -53,7 +53,7 @@
     <title>Options</title>
 
     <para>All options are configured in the
-    <literal>[Journal]</literal> section:</para>
+    [Journal] section:</para>
 
     <variablelist class='config-directives'>
 
index 4aac14ea180c8bf39961b3dc231ea6606888350b..b67639c92e5709e618e851597161e1d2b6c34c15 100644 (file)
           <para>During early boot, the generation of core dump files is disabled until a core dump handler (if any)
           takes over. This parameter allows specifying an absolute path where core dump files should be stored until
           a handler is installed. The path should be absolute and may contain specifiers, see
-          <citerefentry><refentrytitle>core</refentrytitle><manvolnum>5</manvolnum></citerefentry> for details.</para>
+          <citerefentry project='man-pages'><refentrytitle>core</refentrytitle><manvolnum>5</manvolnum></citerefentry> for details.</para>
         </listitem>
       </varlistentry>
 
         <term><varname>rd.udev.exec_delay=</varname></term>
         <term><varname>udev.event_timeout=</varname></term>
         <term><varname>rd.udev.event_timeout=</varname></term>
+        <term><varname>udev.timeout_signal=</varname></term>
+        <term><varname>rd.udev.timeout_signal=</varname></term>
+        <term><varname>udev.blockdev_read_only</varname></term>
+        <term><varname>rd.udev.blockdev_read_only</varname></term>
         <term><varname>net.ifnames=</varname></term>
         <term><varname>net.naming-scheme=</varname></term>
 
 
         <listitem><para>Takes a boolean argument, defaults to on. If off,
         <citerefentry><refentrytitle>systemd-firstboot.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
-        will not query the user for basic system settings, even if the system boots up for the first time and the
-        relevant settings are not initialized yet.</para></listitem>
+        will not query the user for basic system settings, even if the system boots up for the first time and
+        the relevant settings are not initialized yet. Not to be confused with
+        <varname>systemd.condition-first-boot=</varname> (see below), which overrides the result of the
+        <varname>ConditionFirstBoot=</varname> unit file condition, and thus controls more than just
+        <filename>systemd-firstboot.service</filename> behaviour.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>systemd.condition-needs-update=</varname></term>
+
+        <listitem><para>Takes a boolean argument. If specified, overrides the result of
+        <varname>ConditionNeedsUpdate=</varname> unit condition checks. See
+        <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
+        details.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>systemd.condition-first-boot=</varname></term>
+
+        <listitem><para>Takes a boolean argument. If specified, overrides the result of
+        <varname>ConditionFirstBoot=</varname> unit condition checks. See
+        <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
+        details. Not to be confused with <varname>systemd.firstboot=</varname> which only controls behaviour
+        of the <filename>systemd-firstboot.service</filename> system service but has no effect on the
+        condition check (see above).</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>systemd.clock-usec=</varname></term>
+
+        <listitem><para>Takes a decimal, numeric timestamp in µs since January 1st 1970, 00:00am, to set the
+        system clock to. The system time is set to the specified timestamp early during boot. It is not
+        propagated to the hardware clock (RTC).</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>systemd.random-seed=</varname></term>
+
+        <listitem><para>Takes a base64 encoded random seed value to credit with full entropy to the kernel's
+        random pool during early service manager initialization. This option is useful in testing
+        environments where delays due to random pool initialization in entropy starved virtual machines shall
+        be avoided.</para>
+
+        <para>Note that if this option is used the seed is accessible to unprivileged programs from
+        <filename>/proc/cmdline</filename>. This option is hence a security risk when used outside of test
+        systems, since the (possibly) only seed used for initialization of the kernel's entropy pool might be
+        easily acquired by unprivileged programs.</para>
+
+        <para>It is recommended to pass 512 bytes of randomized data (as that matches the Linux kernel pool
+        size), which may be generated with a command like the following:</para>
+
+        <programlisting>dd if=/dev/urandom bs=512 count=1 status=none | base64 -w 0</programlisting>
+
+        <para>Again: do not use this option outside of testing environments, it's a security risk elsewhere,
+        as secret key material derived from the entropy pool can possibly be reconstructed by unprivileged
+        programs.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>systemd.hostname=</varname></term>
+
+        <listitem><para>Accepts a hostname to set during early boot. If specified takes precedence over what
+        is set in <filename>/etc/hostname</filename>. Note that this does not bar later runtime changes to
+        the hostname, it simply controls the initial hostname set during early boot.</para></listitem>
       </varlistentry>
     </variablelist>
 
index 34bb5d2f1edded63ba4876f8d3f94ca9be91f49a..273270f55013d55f9dd6895b24b11d8112d0676d 100644 (file)
@@ -36,7 +36,7 @@
     <title>Description</title>
     <para><command>kernel-install</command> is used to install and remove kernel and initramfs images to and
     from the boot loader partition, referred to as <varname>$BOOT</varname> here. It will usually be one of
-    <filename>/boot</filename>, <filename>/efi</filename>, or <filename>/boot/efi</filename>, see below.
+    <filename>/boot/</filename>, <filename>/efi/</filename>, or <filename>/boot/efi/</filename>, see below.
     </para>
 
     <para><command>kernel-install</command> will execute the files
@@ -80,7 +80,7 @@
             </para></listitem>
 
             <listitem><para><filename>50-depmod.install</filename> runs
-            <citerefentry><refentrytitle>depmod</refentrytitle><manvolnum>8</manvolnum></citerefentry> for the
+            <citerefentry project='man-pages'><refentrytitle>depmod</refentrytitle><manvolnum>8</manvolnum></citerefentry> for the
             <replaceable>KERNEL-VERSION</replaceable>.</para></listitem>
 
             <listitem><para><filename>90-loaderentry.install</filename> copies <replaceable>KERNEL-IMAGE</replaceable>
     <para>The partition where the kernels and <ulink url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot
     Loader Specification</ulink> snippets are located is called <varname>$BOOT</varname>.
     <command>kernel-install</command> determines the location of this partition by checking
-    <filename>/efi/</filename>, <filename>/boot/</filename>, and <filename>/boot/efi</filename>
+    <filename>/efi/</filename>, <filename>/boot/</filename>, and <filename>/boot/efi/</filename>
     in turn. The first location where <filename>$BOOT/loader/entries/</filename> or
     <filename>$BOOT/$MACHINE_ID/</filename> exists is used.</para>
   </refsect1>
     <para>
       <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>depmod</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+      <citerefentry project='man-pages'><refentrytitle>depmod</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
       <ulink url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification</ulink>
     </para>
index 14f84c13ee98218d7d9991b2ee51570f5f0fe4ea..4b01a93bd06758436b2c963ceee2411df2af2a53 100644 (file)
         variable is set, and then derive the random seed to pass to the OS from the combination. If
         <literal>always</literal> the boot loader will do so even if <varname>LoaderSystemToken</varname> is
         not set. This mode is useful in environments where protection against OS image reuse is not a
-        concern, and the random seed shall be used even with no further setup in place. User <command>bootctl
+        concern, and the random seed shall be used even with no further setup in place. Use <command>bootctl
         random-seed</command> to initialize both the random seed file in the ESP and the system token EFI
         variable.</para>
 
index 4cbfd09cbf26db75774e784917decf6360f4a206..81b870c46fec431da7e4d50b35ecd8e98db07945 100644 (file)
@@ -36,7 +36,7 @@
 
     <para>These files configure various parameters of the systemd login manager,
     <citerefentry><refentrytitle>systemd-logind.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>. See
-    <citerefentry><refentrytitle>systemd.syntax</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    <citerefentry><refentrytitle>systemd.syntax</refentrytitle><manvolnum>7</manvolnum></citerefentry>
     for a general description of the syntax.</para>
   </refsect1>
 
@@ -46,7 +46,7 @@
     <title>Options</title>
 
     <para>All options are configured in the
-    <literal>[Login]</literal> section:</para>
+    [Login] section:</para>
 
     <variablelist class='config-directives'>
 
       <varlistentry>
         <term><varname>HoldoffTimeoutSec=</varname></term>
 
-        <listitem><para>Specifies the timeout after system startup or
+        <listitem><para>Specifies a period of time after system startup or
         system resume in which systemd will hold off on reacting to
         lid events. This is required for the system to properly
         detect any hotplugged devices so systemd can ignore lid events
         memory as is needed.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>RuntimeDirectoryInodesMax=</varname></term>
+
+        <listitem><para>Sets the limit on number of inodes for the
+        <varname>$XDG_RUNTIME_DIR</varname> runtime directory for each
+        user who logs in. Takes a number, optionally suffixed with the
+        usual K, G, M, and T suffixes, to the base 1024 (IEC).
+        Defaults to <varname>RuntimeDirectorySize=</varname> divided
+        by 4096. Note that this size is a safety limit only.
+        As each runtime directory is a tmpfs file system, it will
+        only consume as much memory as is needed.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>InhibitorsMax=</varname></term>
 
index ebee065a614f901e1000a5ab723be5c1c8ad43db..bd55366ac8805a739ac2d7f4e76a387d1f2b5ec6 100644 (file)
@@ -39,7 +39,7 @@
 
     <para>The machine ID may be set, for example when network booting, with the
     <varname>systemd.machine_id=</varname> kernel command line parameter or by passing the
-    option <option>--machine-id=</option> to systemd. An ID is specified in this manner
+    option <option>--machine-id=</option> to systemd. An ID specified in this manner
     has higher priority and will be used instead of the ID stored in
     <filename>/etc/machine-id</filename>.</para>
 
index 4fb27abe0a32f73d93dc85835e225acab0946139..7a0a396a1ebabcbe4387d9cba7773e12b9d4e482 100644 (file)
@@ -70,7 +70,7 @@
         <literal>Lennart's Computer</literal> an Internet hostname of
         <literal>lennarts-computer</literal> might be a good choice.
         If this parameter is not set, an application should fall back
-        to the Internet host name for presentation
+        to the Internet hostname for presentation
         purposes.</para></listitem>
       </varlistentry>
 
index c211ca02a94cea9d6fb84115d4884cff8a64ad38..37e51f90cf2718bb6c4bf0eee79e706351d536e9 100644 (file)
@@ -56,7 +56,7 @@
     </itemizedlist>
 
     <para>Machines are identified by names that follow the same rules
-    as UNIX and DNS host names. For details, see below.</para>
+    as UNIX and DNS hostnames. For details, see below.</para>
 
     <para>Machines are instantiated from disk or file system images that
     frequently — but not necessarily — carry the same name as machines running
 
         <listitem><para>Copies files or directories from a container
         into the host system. Takes a container name, followed by the
-        source path in the container the destination path on the host.
+        source path in the container and the destination path on the host.
         If the destination path is omitted, the same as the source path
         is used.</para>
 
         image is optimized for file systems that support copy-on-write, and might not be efficient on others, due to
         file system limitations.</para>
 
-        <para>Note that this command leaves host name, machine ID and
+        <para>Note that this command leaves hostname, machine ID and
         all other settings that could identify the instance
         unmodified. The original image and the cloned copy will hence
         share these credentials, and it might be necessary to manually
 
     <para>The <command>machinectl</command> tool operates on machines
     and images whose names must be chosen following strict
-    rules. Machine names must be suitable for use as host names
+    rules. Machine names must be suitable for use as hostnames
     following a conservative subset of DNS and UNIX/Linux
     semantics. Specifically, they must consist of one or more
     non-empty label strings, separated by dots. No leading or trailing
index 75680b860c7a9faca10e543976a6fcaaa1c47766..fa6164b8ded36e17066039ed07852ecf0718346c 100755 (executable)
@@ -6,6 +6,9 @@ if [ -z "$1" ]; then
     exit 1
 fi
 
+# make sure the rules have been regenerated (in case man/update-man-rules was just run)
+ninja -C "@BUILD_ROOT@" version.h
+
 page="$(echo "$1" | sed 's/\./\\./')"
 target=$(ninja -C "@BUILD_ROOT@" -t query man/man | grep -E -m1 "man/$page\.[0-9]$" | awk '{print $2}')
 if [ -z "$target" ]; then
index 8021adedec8d088708566dade8db51327a49a8ba..3a7143a4b6a7377a71326af8cf80a291e31d0f72 100644 (file)
@@ -34,6 +34,7 @@ custom_entities_ent = configure_file(
 man_pages = []
 html_pages = []
 source_xml_files = []
+dbus_docs = []
 foreach tuple : xsltproc.found() ? manpages : []
         stem = tuple[0]
         section = tuple[1]
@@ -90,7 +91,11 @@ foreach tuple : xsltproc.found() ? manpages : []
                         install_dir : join_paths(docdir, 'html'))
                 html_pages += p3
 
-                source_xml_files += files(tuple[0] + '.xml')
+                file = files(tuple[0] + '.xml')
+                source_xml_files += file
+                if tuple[0].startswith('org.freedesktop.')
+                        dbus_docs += file
+                endif
         else
                 message('Skipping @0@.@1@ because @2@ is false'.format(stem, section, condition))
         endif
@@ -105,9 +110,9 @@ endif
 
 systemd_directives_xml = custom_target(
         'systemd.directives.xml',
-        input : source_xml_files,
+        input : ['directives-template.xml', source_xml_files],
         output : 'systemd.directives.xml',
-        command : [make_directive_index_py, '@OUTPUT@'] + source_xml_files)
+        command : [make_directive_index_py, '@OUTPUT@', '@INPUT@'])
 
 nonindex_xml_files = source_xml_files + [systemd_directives_xml]
 systemd_index_xml = custom_target(
@@ -193,13 +198,26 @@ run_target(
 
 ############################################################
 
+if dbus_docs.length() > 0
+        custom_target(
+                'update-dbus-docs',
+                output : 'update-dbus-docs',
+                command : ['python3',
+                           '@0@/tools/update-dbus-docs.py'.format(project_source_root),
+                           '--build-dir=@0@'.format(project_build_root),
+                           '@INPUT@'],
+                input : dbus_docs)
+endif
+
+############################################################
+
 if git.found()
         custom_target(
                 'update-man-rules',
                 output : 'update-man-rules',
                 command : ['sh', '-c',
                            'cd @0@ && '.format(meson.build_root()) +
-                           'python3 @0@/tools/make-man-rules.py $(git ls-files ":/man/*.xml") >t && '.format(project_source_root) +
+                           'python3 @0@/tools/update-man-rules.py $(git ls-files ":/man/*.xml") >t && '.format(project_source_root) +
                            'mv t @0@/rules/meson.build'.format(meson.current_source_dir())],
                 depend_files : custom_entities_ent)
 endif
index 52876f3776b9d9e4cf232394fe31e64cd8967346..bd958fb1e0152905dd027e2a43f7ce98309202ad 100644 (file)
@@ -233,7 +233,7 @@ s - Service VLAN, m - Two-port MAC Relay (TPMR)
 
         <listitem><para>Show numerical address labels that can be used for address selection.
         This is the same information that
-        <citerefentry><refentrytitle>ip-addrlabel</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+        <citerefentry project='die-net'><refentrytitle>ip-addrlabel</refentrytitle><manvolnum>8</manvolnum></citerefentry>
         shows. See <ulink url="https://tools.ietf.org/html/rfc3484">RFC 3484</ulink>
         for a discussion of address labels.</para>
 
@@ -259,6 +259,20 @@ s - Service VLAN, m - Two-port MAC Relay (TPMR)
         <listitem><para>Deletes virtual netdevs. Takes interface name or index number.</para></listitem>
       </varlistentry>
 
+       <varlistentry>
+        <term>
+          <command>up</command>
+        </term>
+        <listitem><para>Bring devices up. Takes interface name or index number.</para></listitem>
+      </varlistentry>
+
+       <varlistentry>
+        <term>
+          <command>down</command>
+        </term>
+        <listitem><para>Bring devices down. Takes interface name or index number.</para></listitem>
+      </varlistentry>
+
        <varlistentry>
         <term>
           <command>renew</command>
@@ -267,6 +281,14 @@ s - Service VLAN, m - Two-port MAC Relay (TPMR)
         Takes interface name or index number.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term>
+          <command>forcerenew</command>
+        </term>
+        <listitem><para>Send a FORCERENEW message to all connected clients, triggering DHCP reconfiguration.
+        Takes interface name or index number.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term>
           <command>reconfigure</command>
index 2041ddd42754bc9f754d9c56190db7240c2209cd..e8e41ebe920f73676562bef89707ad598e07ce73 100644 (file)
@@ -45,7 +45,7 @@
   <refsect1>
     <title>[Network] Section Options</title>
 
-    <para>The following options are available in the <literal>[Network]</literal> section:</para>
+    <para>The following options are available in the [Network] section:</para>
 
     <variablelist class='network-directives'>
       <varlistentry>
         <listitem><para>Specifies the time interval to calculate the traffic speed of each interface.
         If <varname>SpeedMeter=no</varname>, the value is ignored. Defaults to 10sec.</para></listitem>
       </varlistentry>
+
+      <varlistentry>
+        <term><varname>ManageForeignRoutes=</varname></term>
+        <listitem><para>A boolean. When true, <command>systemd-networkd</command> will store any routes
+        configured by other tools in its memory. When false, <command>systemd-networkd</command> will
+        not manage the foreign routes, thus they are kept even if <varname>KeepConfiguration=</varname>
+        is false. Defaults to yes.</para></listitem>
+      </varlistentry>
+
     </variablelist>
   </refsect1>
 
index 908c91eb7cdb00e362b436f0944ec4db9c35a9f1..a41c383bb33ba2de455f84cfe4fb48500e90d5ad 100644 (file)
@@ -18,8 +18,7 @@
   <refnamediv>
     <refname>nss-myhostname</refname>
     <refname>libnss_myhostname.so.2</refname>
-    <refpurpose>Provide hostname resolution for the locally
-    configured system hostname.</refpurpose>
+    <refpurpose>Hostname resolution for the locally configured system hostname</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
     <para>To activate the NSS modules, add <literal>myhostname</literal> to the line starting with
     <literal>hosts:</literal> in <filename>/etc/nsswitch.conf</filename>.</para>
 
-    <para>It is recommended to place <literal>myhostname</literal> last in the <filename>nsswitch.conf</filename>'
-    <literal>hosts:</literal> line to make sure that this mapping is only used as fallback, and that any DNS or
-    <filename>/etc/hosts</filename> based mapping takes precedence.</para>
+    <para>It is recommended to place <literal>myhostname</literal> either between <literal>resolve</literal>
+    and "traditional" modules like <literal>files</literal> and <literal>dns</literal>, or after them. In the
+    first version, well-known names like <literal>localhost</literal> and the machine hostname are given
+    higher priority than the external configuration. This is recommended when the external DNS servers and
+    network are not absolutely trusted. In the second version, external configuration is given higher
+    priority and <command>nss-myhostname</command> only provides a fallback mechanism. This might be suitable
+    in closely controlled networks, for example on a company LAN.</para>
   </refsect1>
 
   <refsect1>
     <command>nss-myhostname</command> correctly:</para>
 
     <!-- synchronize with other nss-* man pages and factory/etc/nsswitch.conf -->
-<programlisting>passwd:         compat mymachines systemd
-group:          compat mymachines systemd
+<programlisting>passwd:         compat systemd
+group:          compat systemd
 shadow:         compat
 
-hosts:          files mymachines resolve [!UNAVAIL=return] dns <command>myhostname</command>
+# Either (untrusted network):
+hosts:          mymachines resolve [!UNAVAIL=return] <command>myhostname</command> files dns
+# Or (only trusted networks):
+hosts:          mymachines resolve [!UNAVAIL=return] files dns <command>myhostname</command>
 networks:       files
 
 protocols:      db files
index 40b0abee344ea7805a36dc3cea0eda83f73d6368..e0e6989c40c292d6242406257f692cde2f07b922 100644 (file)
@@ -18,8 +18,7 @@
   <refnamediv>
     <refname>nss-mymachines</refname>
     <refname>libnss_mymachines.so.2</refname>
-    <refpurpose>Provide hostname resolution for local
-    container instances.</refpurpose>
+    <refpurpose>Hostname resolution for local container instances</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
     Note that the name that is resolved is the one registered with <command>systemd-machined</command>, which
     may be different than the hostname configured inside of the container.</para>
 
-    <para>The module also provides name resolution for user and group identifiers mapped to containers. All names from
-    the range allocated to a given container <replaceable>container</replaceable> are exposed on the host as
-    <literal>vu-<replaceable>container</replaceable>-<replaceable>uid</replaceable></literal> and
-    <literal>vg-<replaceable>container</replaceable>-<replaceable>gid</replaceable></literal> (see example below). This
-    functionality only applies to containers using user namespacing (see the description of
-    <option>--private-users</option> in
-    <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>).</para>
-
-    <para>To activate the NSS module, add <literal>mymachines</literal> to the lines starting with
-    <literal>hosts:</literal>, <literal>passwd:</literal> and <literal>group:</literal> in
-    <filename>/etc/nsswitch.conf</filename>.</para>
+    <para>To activate the NSS module, add <literal>mymachines</literal> to the line starting with
+    <literal>hosts:</literal> in <filename>/etc/nsswitch.conf</filename>.</para>
 
     <para>It is recommended to place <literal>mymachines</literal> after the <literal>files</literal> or
-    <literal>compat</literal> entry of the <filename>/etc/nsswitch.conf</filename> lines to make sure that its mappings
-    are preferred over other resolvers such as DNS, but so that <filename>/etc/hosts</filename>,
-    <filename>/etc/passwd</filename> and <filename>/etc/group</filename> based mappings take precedence.</para>
+    <literal>compat</literal> entry of the <filename>/etc/nsswitch.conf</filename> line to make sure that its
+    mappings are preferred over other resolvers such as DNS, but so that <filename>/etc/hosts</filename>
+    based mappings take precedence.</para>
   </refsect1>
 
   <refsect1>
     <command>nss-mymachines</command> correctly:</para>
 
     <!-- synchronize with other nss-* man pages and factory/etc/nsswitch.conf -->
-    <programlisting>passwd:         compat <command>mymachines</command> systemd
-group:          compat <command>mymachines</command> systemd
+    <programlisting>passwd:         compat systemd
+group:          compat systemd
 shadow:         compat
 
-hosts:          files <command>mymachines</command> resolve [!UNAVAIL=return] dns myhostname
+hosts:          <command>mymachines</command> resolve [!UNAVAIL=return] myhostname files dns
 networks:       files
 
 protocols:      db files
@@ -82,7 +72,7 @@ netgroup:       nis</programlisting>
   </refsect1>
 
   <refsect1>
-    <title>Mappings provided by <filename>nss-mymachines</filename></title>
+    <title>Example: Mappings provided by <filename>nss-mymachines</filename></title>
 
     <para>The container <literal>rawhide</literal> is spawned using
     <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>:
@@ -97,29 +87,6 @@ $ machinectl --max-addresses=3
 MACHINE CLASS     SERVICE        OS     VERSION ADDRESSES
 rawhide container systemd-nspawn fedora 30      169.254.40.164 fe80::94aa:3aff:fe7b:d4b9
 
-$ getent passwd vu-rawhide-0 vu-rawhide-81
-vu-rawhide-0:*:20119552:65534:vu-rawhide-0:/:/usr/sbin/nologin
-vu-rawhide-81:*:20119633:65534:vu-rawhide-81:/:/usr/sbin/nologin
-
-$ getent group vg-rawhide-0 vg-rawhide-81
-vg-rawhide-0:*:20119552:
-vg-rawhide-81:*:20119633:
-
-$ ps -o user:15,pid,tty,command -e|grep '^vu-rawhide'
-vu-rawhide-0      692 ?        /usr/lib/systemd/systemd
-vu-rawhide-0      731 ?        /usr/lib/systemd/systemd-journald
-vu-rawhide-192    734 ?        /usr/lib/systemd/systemd-networkd
-vu-rawhide-193    738 ?        /usr/lib/systemd/systemd-resolved
-vu-rawhide-0      742 ?        /usr/lib/systemd/systemd-logind
-vu-rawhide-81     744 ?        /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only
-vu-rawhide-0      746 ?        /usr/sbin/sshd -D ...
-vu-rawhide-0      752 ?        /usr/lib/systemd/systemd --user
-vu-rawhide-0      753 ?        (sd-pam)
-vu-rawhide-0     1628 ?        login -- zbyszek
-vu-rawhide-1000  1630 ?        /usr/lib/systemd/systemd --user
-vu-rawhide-1000  1631 ?        (sd-pam)
-vu-rawhide-1000  1637 pts/8    -zsh
-
 $ ping -c1 rawhide
 PING rawhide(fe80::94aa:3aff:fe7b:d4b9%ve-rawhide (fe80::94aa:3aff:fe7b:d4b9%ve-rawhide)) 56 data bytes
 64 bytes from fe80::94aa:3aff:fe7b:d4b9%ve-rawhide (fe80::94aa:3aff:fe7b:d4b9%ve-rawhide): icmp_seq=1 ttl=64 time=0.045 ms
index e4ea4e189822e5a11a30cada3bc47a9736884047..c377468953dbac286af1405ab1cf77f2e6b0785b 100644 (file)
@@ -18,7 +18,7 @@
   <refnamediv>
     <refname>nss-resolve</refname>
     <refname>libnss_resolve.so.2</refname>
-    <refpurpose>Provide hostname resolution via <filename>systemd-resolved.service</filename></refpurpose>
+    <refpurpose>Hostname resolution via <filename>systemd-resolved.service</filename></refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
     <title>Description</title>
 
     <para><command>nss-resolve</command> is a plug-in module for the GNU Name Service Switch (NSS) functionality of the
-    GNU C Library (<command>glibc</command>) enabling it to resolve host names via the
+    GNU C Library (<command>glibc</command>) enabling it to resolve hostnames via the
     <citerefentry><refentrytitle>systemd-resolved</refentrytitle><manvolnum>8</manvolnum></citerefentry> local network
     name resolution service. It replaces the <command>nss-dns</command> plug-in module that traditionally resolves
     hostnames via DNS.</para>
 
-    <para>To activate the NSS module, add <literal>resolve</literal> to the line starting with
-    <literal>hosts:</literal> in <filename>/etc/nsswitch.conf</filename>. Specifically, it is recommended to place
-    <literal>resolve</literal> early in <filename>/etc/nsswitch.conf</filename>'s <literal>hosts:</literal> line (but
-    after the <literal>files</literal> or <literal>mymachines</literal> entries), right before the
-    <literal>dns</literal> entry if it exists, followed by <literal>[!UNAVAIL=return]</literal>, to ensure DNS queries
-    are always routed via
-    <citerefentry><refentrytitle>systemd-resolved</refentrytitle><manvolnum>8</manvolnum></citerefentry> if it is
-    running, but are routed to <command>nss-dns</command> if this service is not available.</para>
+    <para>To activate the NSS module, add <literal>resolve [!UNAVAIL=return]</literal> to the line starting
+    with <literal>hosts:</literal> in <filename>/etc/nsswitch.conf</filename>. Specifically, it is
+    recommended to place <literal>resolve</literal> early in <filename>/etc/nsswitch.conf</filename>'s
+    <literal>hosts:</literal> line. It should be before the <literal>files</literal> entry, since
+    <filename>systemd-resolved</filename> supports <filename>/etc/hosts</filename> internally, but with
+    caching. To the contrary, it should be after <literal>mymachines</literal>, to give hostnames given to
+    local VMs and containers precedence over names received over DNS. Finally, we recommend placing
+    <literal>dns</literal> somewhere after <literal>resolve</literal>, to fall back to
+    <command>nss-dns</command> if <filename>systemd-resolved.service</filename> is not available.</para>
 
     <para>Note that <command>systemd-resolved</command> will synthesize DNS resource
     records in a few cases, for example for <literal>localhost</literal> and the
     correctly:</para>
 
     <!-- synchronize with other nss-* man pages and factory/etc/nsswitch.conf -->
-<programlisting>passwd:         compat mymachines systemd
-group:          compat mymachines systemd
+<programlisting>passwd:         compat systemd
+group:          compat systemd
 shadow:         compat
 
-hosts:          files mymachines <command>resolve [!UNAVAIL=return]</command> dns myhostname
+hosts:          mymachines <command>resolve [!UNAVAIL=return]</command> myhostname files dns
 networks:       files
 
 protocols:      db files
index e343c406f29931b0e3eac1caa3507b6ec5128627..34aee0e88016970f87d4b9cea5d68dec4a0c438b 100644 (file)
@@ -18,7 +18,7 @@
   <refnamediv>
     <refname>nss-systemd</refname>
     <refname>libnss_systemd.so.2</refname>
-    <refpurpose>Provide UNIX user and group name resolution for user/group lookup via Varlink</refpurpose>
+    <refpurpose>UNIX user and group name resolution for user/group lookup via Varlink</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
@@ -35,8 +35,8 @@
     <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry> (for its
     <varname>DynamicUser=</varname> feature, see
     <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
-    details) or
-    <citerefentry><refentrytitle>systemd-homed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
+    details),
+    <citerefentry><refentrytitle>systemd-homed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>, or <citerefentry><refentrytitle>systemd-machined.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
 
     <para>This module also ensures that the root and nobody users and groups (i.e. the users/groups with the UIDs/GIDs
     0 and 65534) remain resolvable at all times, even if they aren't listed in <filename>/etc/passwd</filename> or
   </refsect1>
 
   <refsect1>
-    <title>Example</title>
+    <title>Configuration in <filename>/etc/nsswitch.conf</filename></title>
 
     <para>Here is an example <filename>/etc/nsswitch.conf</filename> file that enables
     <command>nss-systemd</command> correctly:</para>
 
     <!-- synchronize with other nss-* man pages and factory/etc/nsswitch.conf -->
-    <programlisting>passwd:         compat mymachines <command>systemd</command>
-group:          compat [SUCCESS=merge] mymachines [SUCCESS=merge] <command>systemd</command>
+    <programlisting>passwd:         compat <command>systemd</command>
+group:          compat [SUCCESS=merge] <command>systemd</command>
 shadow:         compat
 
-hosts:          files mymachines resolve [!UNAVAIL=return] dns myhostname
+hosts:          mymachines resolve [!UNAVAIL=return] myhostname files dns
 networks:       files
 
 protocols:      db files
@@ -77,6 +77,47 @@ netgroup:       nis</programlisting>
 
   </refsect1>
 
+  <refsect1>
+    <title>Example: Mappings provided by <filename>systemd-machined.service</filename></title>
+
+    <para>The container <literal>rawhide</literal> is spawned using
+    <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>:
+    </para>
+
+    <programlisting># systemd-nspawn -M rawhide --boot --network-veth --private-users=pick
+Spawning container rawhide on /var/lib/machines/rawhide.
+Selected user namespace base 20119552 and range 65536.
+...
+
+$ machinectl --max-addresses=3
+MACHINE CLASS     SERVICE        OS     VERSION ADDRESSES
+rawhide container systemd-nspawn fedora 30      169.254.40.164 fe80::94aa:3aff:fe7b:d4b9
+
+$ getent passwd vu-rawhide-0 vu-rawhide-81
+vu-rawhide-0:*:20119552:65534:vu-rawhide-0:/:/usr/sbin/nologin
+vu-rawhide-81:*:20119633:65534:vu-rawhide-81:/:/usr/sbin/nologin
+
+$ getent group vg-rawhide-0 vg-rawhide-81
+vg-rawhide-0:*:20119552:
+vg-rawhide-81:*:20119633:
+
+$ ps -o user:15,pid,tty,command -e|grep '^vu-rawhide'
+vu-rawhide-0      692 ?        /usr/lib/systemd/systemd
+vu-rawhide-0      731 ?        /usr/lib/systemd/systemd-journald
+vu-rawhide-192    734 ?        /usr/lib/systemd/systemd-networkd
+vu-rawhide-193    738 ?        /usr/lib/systemd/systemd-resolved
+vu-rawhide-0      742 ?        /usr/lib/systemd/systemd-logind
+vu-rawhide-81     744 ?        /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only
+vu-rawhide-0      746 ?        /usr/sbin/sshd -D ...
+vu-rawhide-0      752 ?        /usr/lib/systemd/systemd --user
+vu-rawhide-0      753 ?        (sd-pam)
+vu-rawhide-0     1628 ?        login -- zbyszek
+vu-rawhide-1000  1630 ?        /usr/lib/systemd/systemd --user
+vu-rawhide-1000  1631 ?        (sd-pam)
+vu-rawhide-1000  1637 pts/8    -zsh
+</programlisting>
+  </refsect1>
+
   <refsect1>
     <title>See Also</title>
     <para>
@@ -85,6 +126,9 @@ netgroup:       nis</programlisting>
       <citerefentry><refentrytitle>nss-resolve</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>nss-myhostname</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>nss-mymachines</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-userdbd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-homed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-machined.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
       <citerefentry project='man-pages'><refentrytitle>nsswitch.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
       <citerefentry project='man-pages'><refentrytitle>getent</refentrytitle><manvolnum>1</manvolnum></citerefentry>
     </para>
diff --git a/man/org.freedesktop.LogControl1.xml b/man/org.freedesktop.LogControl1.xml
new file mode 100644 (file)
index 0000000..d8ce392
--- /dev/null
@@ -0,0 +1,106 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" >
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="org.freedesktop.LogControl1"
+    xmlns:xi="http://www.w3.org/2001/XInclude">
+  <refentryinfo>
+    <title>org.freedesktop.LogControl1</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>org.freedesktop.LogControl1</refentrytitle>
+    <manvolnum>5</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>org.freedesktop.LogControl1</refname>
+    <refpurpose>D-Bus interface to query and set logging configuration</refpurpose>
+  </refnamediv>
+
+  <refsect1>
+    <title>Introduction</title>
+
+    <para><interfacename>org.freedesktop.LogControl1</interfacename> is a generic interface that is intended
+    to be used by any daemon which should allow setting the log level and target over D-Bus. It is implemented
+    by various daemons that are part of the
+    <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry> suite.</para>
+
+    <para>It is assumed that those settings are global for the whole program, so a fixed object path is
+    used. The interface should always be available under the path
+    <filename>/org/freedesktop/LogControl1</filename>.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>The following interface is exposed:</para>
+
+    <programlisting executable="systemd" node="/org/freedesktop/LogControl1" interface="org.freedesktop.LogControl1">
+node /org/freedesktop/LogControl1 {
+  interface org.freedesktop.LogControl1 {
+    properties:
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      @org.freedesktop.systemd1.Privileged("true")
+      readwrite s LogLevel = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      @org.freedesktop.systemd1.Privileged("true")
+      readwrite s LogTarget = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly s SyslogIdentifier = '...';
+  };
+  interface org.freedesktop.DBus.Peer { ... };
+  interface org.freedesktop.DBus.Introspectable { ... };
+  interface org.freedesktop.DBus.Properties { ... };
+};
+    </programlisting>
+
+    <!--Autogenerated cross-references for systemd.directives, do not edit-->
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.LogControl1"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.LogControl1"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LogLevel"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LogTarget"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SyslogIdentifier"/>
+
+    <!--End of Autogenerated section-->
+
+    <refsect2>
+      <title>Properties</title>
+
+      <para><varname>LogLevel</varname> describes the
+      <citerefentry project='man-pages'><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry>-style
+      log-level, and should be one of <literal>emerg</literal>, <literal>alert</literal>,
+      <literal>crit</literal>, <literal>err</literal>, <literal>warning</literal>, <literal>notice</literal>,
+      <literal>info</literal>, <literal>debug</literal>, in order of increasing verbosity.</para>
+
+      <para><varname>LogTarget</varname> describes the log target (mechanism). It should be one of
+      <literal>console</literal> (log to the console or standard output),
+      <literal>kmsg</literal> (log to the kernel ring buffer),
+      <literal>journal</literal> (log to the journal natively, see
+      <citerefentry><refentrytitle>systemd-journald.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>),
+      <literal>syslog</literal> (log using the
+      <citerefentry project='man-pages'><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry> call).
+      </para>
+
+      <para>Those two properties are writable, so they may be set by sufficiently privileged users.</para>
+
+      <para><varname>SyslogIdentifier</varname> is a read-only property that shows the "syslog identifier".
+      It is a short string that identifies the program that is the source of log messages that is passed to
+      the <citerefentry project='man-pages'><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry> call.
+      </para>
+
+      <para>Note: <command>journalctl</command> option <option>-p</option>/<option>--priority=</option> may
+      be used to filter log messages by log level, option <option>-t</option>/<option>--identifier=</option>
+      may be used to by the syslog identifier, and filters like <literal>_TRANSPORT=syslog</literal>,
+      <literal>_TRANSPORT=journal</literal>, and <literal>_TRANSPORT=kernel</literal> may be used to filter
+      messages by the mechanism through which they reached <command>systemd-journald</command>.</para>
+    </refsect2>
+  </refsect1>
+</refentry>
diff --git a/man/org.freedesktop.home1.xml b/man/org.freedesktop.home1.xml
new file mode 100644 (file)
index 0000000..5cf1159
--- /dev/null
@@ -0,0 +1,502 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" >
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="org.freedesktop.home1" conditional='ENABLE_HOMED'
+    xmlns:xi="http://www.w3.org/2001/XInclude">
+  <refentryinfo>
+    <title>org.freedesktop.home1</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>org.freedesktop.home1</refentrytitle>
+    <manvolnum>5</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>org.freedesktop.home1</refname>
+    <refpurpose>The D-Bus interface of systemd-homed</refpurpose>
+  </refnamediv>
+
+  <refsect1>
+    <title>Introduction</title>
+
+    <para><citerefentry><refentrytitle>systemd-homed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    is a system service which may be used to create, remove, change or inspect home areas. This page
+    describes the D-Bus interface.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>The Manager Object</title>
+
+    <para>The service exposes the following interfaces on the Manager object on the bus:</para>
+
+    <programlisting executable="systemd-homed" node="/org/freedesktop/home1" interface="org.freedesktop.home1.Manager">
+node /org/freedesktop/home1 {
+  interface org.freedesktop.home1.Manager {
+    methods:
+      GetHomeByName(in  s user_name,
+                    out u uid,
+                    out s home_state,
+                    out u gid,
+                    out s real_name,
+                    out s home_directory,
+                    out s shell,
+                    out o bus_path);
+      GetHomeByUID(in  u uid,
+                   out s user_name,
+                   out s home_state,
+                   out u gid,
+                   out s real_name,
+                   out s home_directory,
+                   out s shell,
+                   out o bus_path);
+      GetUserRecordByName(in  s user_name,
+                          out s user_record,
+                          out b incomplete,
+                          out o bus_path);
+      GetUserRecordByUID(in  u uid,
+                         out s user_record,
+                         out b incomplete,
+                         out o bus_path);
+      ListHomes(out a(susussso) home_areas);
+      ActivateHome(in  s user_name,
+                   in  s secret);
+      DeactivateHome(in  s user_name);
+      RegisterHome(in  s user_record);
+      UnregisterHome(in  s user_name);
+      CreateHome(in  s user_record);
+      RealizeHome(in  s user_name,
+                  in  s secret);
+      RemoveHome(in  s user_name);
+      FixateHome(in  s user_name,
+                 in  s secret);
+      AuthenticateHome(in  s user_name,
+                       in  s secret);
+      UpdateHome(in  s user_record);
+      ResizeHome(in  s user_name,
+                 in  t size,
+                 in  s secret);
+      ChangePasswordHome(in  s user_name,
+                         in  s new_secret,
+                         in  s old_secret);
+      LockHome(in  s user_name);
+      UnlockHome(in  s user_name,
+                 in  s secret);
+      AcquireHome(in  s user_name,
+                  in  s secret,
+                  in  b please_suspend,
+                  out h send_fd);
+      RefHome(in  s user_name,
+              in  b please_suspend,
+              out h send_fd);
+      ReleaseHome(in  s user_name);
+      LockAllHomes();
+    properties:
+      readonly a(sso) AutoLogin = [...];
+  };
+  interface org.freedesktop.DBus.Peer { ... };
+  interface org.freedesktop.DBus.Introspectable { ... };
+  interface org.freedesktop.DBus.Properties { ... };
+};
+    </programlisting>
+
+    <!--Autogenerated cross-references for systemd.directives, do not edit-->
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.home1.Manager"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.home1.Manager"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetHomeByName()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetHomeByUID()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetUserRecordByName()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetUserRecordByUID()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ListHomes()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ActivateHome()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="DeactivateHome()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="RegisterHome()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="UnregisterHome()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="CreateHome()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="RealizeHome()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="RemoveHome()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="FixateHome()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="AuthenticateHome()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="UpdateHome()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ResizeHome()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ChangePasswordHome()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="LockHome()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="UnlockHome()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="AcquireHome()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="RefHome()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ReleaseHome()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="LockAllHomes()"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="AutoLogin"/>
+
+    <!--End of Autogenerated section-->
+
+    <refsect2>
+      <title>Methods</title>
+
+      <para><function>GetHomeByName()</function> returns basic user information (a minimal subset of the full
+      user record), provided a user name. The information supplied more or less matches what
+      <citerefentry project='man-pages'><refentrytitle>getpwnam</refentrytitle><manvolnum>3</manvolnum></citerefentry> returns:
+      the numeric UID and GID, the real name, home directory and shell. In addition it returns a state
+      identifier describing the state the user's home directory is in, as well as a bus path referring to the
+      bus object encapsulating the user record and home directory. This object implements the
+      <classname>org.freedesktop.home1.Home</classname> interface documented below.</para>
+
+      <para><function>GetHomeByUID()</function> is similar to <function>GetHomeByName()</function> but
+      acquires the information based on the numeric UID of the user.</para>
+
+      <para><function>GetUserRecordByName()</function> is also similar to
+      <function>GetHomeByName()</function> but returns the full JSON user record data instead of the broken
+      down records. An additional returned boolean indicates whether the record is complete or not. A record
+      is considered complete when its <literal>privileged</literal> section is included, and incomplete if it
+      was removed (see <ulink url="https://systemd.io/USER_RECORD">JSON User Records</ulink> for details
+      about the various sections of a user record). Generally, only privileged clients and clients running
+      under the identity of the user itself get access to the <literal>privileged</literal> section and will
+      thus see complete records.</para>
+
+      <para><function>GetUserRecordByUID()</function> is similar to <function>GetUserRecordByName()</function>
+      but returns the user record matching the specified numeric UID.</para>
+
+      <para><function>ListHomes()</function> returns an array of all locally managed users. The array
+      contains the same fields <function>GetHomeByName()</function> returns: user name, numeric UID, state,
+      numeric GID, real name, home directory, shell and bus path of the matching bus object.</para>
+
+      <para><function>ActivateHome()</function> activates (i.e. mounts) the home directory of the specified
+      user. The second argument shall contain a user record consisting only of a <literal>secret</literal>
+      section (all other sections should be stripped, see <ulink url="https://systemd.io/USER_RECORD">JSON
+      User Records</ulink> for details), and should contain only the secret credentials necessary for
+      unlocking the home directory. Typically a client would invoke this function first with an entirely
+      empty record (which is possibly sufficient if single-factor authentication with a plugged-in security
+      token is configured), and would then retry with a record populated with more information, depending on
+      the returned error code, in case more credentials are necessary. This function is synchronous and
+      returns only after the home directory was fully activated (or the operation failed), which might take
+      some time. Clients must be prepared for that, and typically should extend the D-Bus method call
+      timeout accordingly. This method is equivalent to the <function>Activate()</function> method on the
+      <classname>org.freedesktop.home1.Home</classname> interface documented below, but may be called on the
+      manager object and takes a user name as additional argument, instead.</para>
+
+      <para><function>DeactivateHome()</function> deactivates (i.e. unmounts) the home directory of the
+      specified user. It is equivalent to the <function>Deactivate()</function> method on the
+      <classname>org.freedesktop.home1.Home</classname> interface documented below.</para>
+
+      <para><function>RegisterHome()</function> registers a new home directory locally. It receives the JSON
+      user record as only argument (which typically excludes the <literal>secret</literal>
+      section). Registering a home directory just makes the user record known to the system, it does not
+      create a home directory or such (which is expected to exist already, or created later). This operation
+      is useful to register home directories locally that are not located where
+      <filename>systemd-homed.service</filename> would find them automatically.</para>
+
+      <para><function>UnregisterHome()</function> unregisters an existing home directory. It takes a user
+      name as argument and undoes what <function>RegisterHome()</function> does. It does not attempt to
+      remove the home directory itself, it just unregisters it with the local system. Note that if the home
+      directory is placed where <filename>systemd-homed.service</filename> looks for home directories anyway
+      this call will only undo fixation (see below), but the record will remain known to
+      <filename>systemd-homed.service</filename> and be listed among known records. Since the user record is
+      embedded into the home directory this operation generally does not discard data belonging to the user
+      or their record. This method is equivalent to
+      <function>Unregister()</function> on the <classname>org.freedesktop.home1.Home</classname>
+      interface.</para>
+
+      <para><function>CreateHome()</function> registers and creates a new home directory. This takes a fully
+      specified JSON user record as argument (including the <literal>secret</literal> section). This registers
+      the user record locally and creates a home directory matching it, depending on the settings specified
+      in the record in combination with local configuration.</para>
+
+      <para><function>RealizeHome()</function> creates a home directory whose user record is already
+      registered locally. This takes a user name plus a user record consisting only of the
+      <literal>secret</literal> section. Invoking <function>RegisterHome()</function> followed by
+      <function>RealizeHome()</function> is mostly equivalent to calling <function>CreateHome()</function>,
+      except that the latter combines the two in atomic fashion. This method is equivalent to
+      <function>Realize()</function> on the <classname>org.freedesktop.home1.Home</classname>
+      interface.</para>
+
+      <para><function>RemoveHome()</function> unregisters a user record locally, and removes the home
+      directory belonging to it, if it is accessible. It takes a user name as argument. This method is equivalent to
+      <function>Remove()</function> on the <classname>org.freedesktop.home1.Home</classname>
+      interface.</para>
+
+      <para><function>FixateHome()</function> <literal>fixates</literal> an automatically discovered home
+      directory. <filename>systemd-homed.service</filename> automatically discovers home directories dropped
+      in our plugged in and adds them to the runtime list of user records it manages. A user record
+      discovered that way may be <literal>fixated</literal>, in which case it is copied out of the home
+      directory, onto persistent storage, to fixate the UID/GID assignment of the record, and extract
+      additional (typically previously encrypted) user record data from the home directory. A home directory
+      mus be fixated before it can be logged into. This method call takes a user name and a JSON user record
+      consisting only of the <literal>secret</literal> section as argument. This method is equivalent to
+      <function>Fixate()</function> on the <classname>org.freedesktop.home1.Home</classname> interface.</para>
+
+      <para><function>AuthenticateHome()</function> checks passwords or other authentication credentials
+      associated with the home directory. It takes a user name and a JSON user record consisting only of the
+      <literal>secret</literal> section as argument. Note that many of the other method calls authenticate
+      the user first, in order to execute some other operation. This method call only authenticates and
+      executes no further operation. Like <function>ActivateHome()</function> it is usually first invoked
+      with an empty JSON user record, which is then populated for subsequent tries with additional
+      authentication data supplied. This method is equivalent to
+      <function>Authenticate()</function> on the <classname>org.freedesktop.home1.Home</classname>
+      interface.</para>
+
+      <para><function>UpdateHome()</function> updates a locally registered user record. Takes a fully
+      specified JSON user record as argument (including the <literal>secret</literal> section). A user with a
+      matching name and realm must be registered locally already, and the last change timestamp of the newly
+      supplied record must be newer than the previously existing user record. Note this operation updates the
+      user record only, it does not propagate passwords/authentication tokens from the user record to the
+      storage back-end, or resizes the storage back-end. Typically a home directory is first updated, and then
+      the password of the underlying storage updated using <function>ChangePasswordHome()</function> as well
+      as the storage resized using <function>ResizeHome()</function>. This method is equivalent to
+      <function>Update()</function> on the <classname>org.freedesktop.home1.Home</classname> interface.</para>
+
+      <para><function>ResizeHome()</function> resizes the storage associated with a user record. Takes a user
+      name, a disk size in bytes and a user record consisting only of the <literal>secret</literal> section
+      as argument. If the size is specified as <constant>UINT64_MAX</constant> the storage is resized to the
+      size already specified in the user record. Typically, if the user record is updated using
+      <function>UpdateHome()</function> above this is used to propagate the size configured there-in down to
+      the underlying storage back-end. This method is equivalent to
+      <function>Resize()</function> on the <classname>org.freedesktop.home1.Home</classname>
+      interface.</para>
+
+      <para><function>ChangePasswordHome()</function> changes the passwords/authentication tokens of a home
+      directory. Takes a user name, and two JSON user record objects, each consisting only of the
+      <literal>secret</literal> section, for the old and for the new passwords/authentication tokens. If the
+      user record with the new passwords/authentication token data is specified as empty the existing user
+      record's settings are propagated down to the home directory storage. This is typically used after a
+      user record is updated using <function>UpdateHome()</function> in order to propagate the
+      secrets/authentication tokens down to the storage. This method is equivalent to
+      <function>ChangePassword()</function> on the <classname>org.freedesktop.home1.Home</classname>
+      interface.</para>
+
+      <para><function>LockHome()</function> temporarily suspends access to a home directory, flushing out any
+      cryptographic keys from memory. This is only supported on some back-ends, and usually done during system
+      suspend, in order to effectively secure home directories while the system is sleeping. Takes a user
+      name as single argument. If an application attempts to access a home directory while it is locked it
+      will typically freeze until the home directory is unlocked again. This method is equivalent to
+      <function>Lock()</function> on the <classname>org.freedesktop.home1.Home</classname> interface.</para>
+
+      <para><function>UnlockHome()</function> undoes the effect of <function>LockHome()</function>. Takes a
+      user name and a user record consisting only of the <literal>secret</literal> section as arguments. This
+      method is equivalent to <function>Unlock()</function> on the
+      <classname>org.freedesktop.home1.Home</classname> interface.</para>
+
+      <para><function>AcquireHome()</function> activates or unlocks a home directory in a reference counted
+      mode of operation. Takes a user name and user record consisting only of <literal>secret</literal>
+      section as argument. If the home directory is not active yet, it is activated. If it is currently
+      locked it is unlocked. After completion a reference to the activation/unlocking of the home directory
+      is returned via a file descriptor. When the last client which acquired such a file descriptor closes it
+      the home directory is automatically deactivated again. This method is typically invoked when a user
+      logs in, and the file descriptor is held until the user logs out again, thus ensuring the user's home
+      directory can be unmounted automatically again in a robust fashion, when the user logs out. The third
+      argument is a boolean which indicates whether the client invoking the call is able to automatically
+      re-authenticate when the system comes back from suspending. It should be set by all clients that
+      implement a secure lock screen running outside of the user's context, that is brought up when the
+      system comes back from suspend and can be used to re-acquire the credentials to unlock the user's home
+      directory. If a home directory has at least one client with an open reference to the home directory
+      that does not support this it is not suspended automatically at system suspend, otherwise it is. This
+      method is equivalent to <function>Acquire()</function> on the
+      <classname>org.freedesktop.home1.Home</classname> interface.</para>
+
+      <para><function>RefHome()</function> is similar to <function>AcquireHome()</function> but takes no user
+      record with <literal>secret</literal> section, i.e. will take an additional reference to an already
+      activated/unlocked home directory without attempting to activate/unlock it itself. It will fail if the
+      home directory is not already activated. This method is equivalent to
+      <function>Ref()</function> on the <classname>org.freedesktop.home1.Home</classname>
+      interface.</para>
+
+      <para><function>ReleaseHome()</function> releases a home directory again, if all file descriptors
+      referencing it are already closed, that where acquired through <function>AcquireHome()</function> or
+      <function>RefHome()</function>. Note that this call does not actually cause the deactivation of the
+      home directory (which happens automatically when the last referencing file descriptor is closed), but
+      is simply a synchronization mechanism that allows delaying of the user session's termination until any
+      triggered deactivation is completed. This method is equivalent to <function>Release()</function> on the
+      <classname>org.freedesktop.home1.Home</classname> interface.</para>
+
+      <para><function>LockAllHomes()</function> locks all active home directories that only have references
+      that opted into automatic suspending during system suspend. This is usually invoked automatically
+      shortly before system suspend.</para>
+    </refsect2>
+
+    <refsect2>
+      <title>Properties</title>
+
+      <para><varname>AutoLogin</varname> exposes an array of structures consisting of user name, seat name
+      and object path of an home directory object. All locally managed users that have the
+      <literal>autoLogin</literal> field set are listed here, with the seat name they are associated with. A
+      display manager may watch this property and pre-fill the login screen with the users exposed this
+      way.</para>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>The Home Object</title>
+
+    <programlisting executable="systemd-homed" node="/org/freedesktop/home1/home" interface="org.freedesktop.home1.Home">
+node /org/freedesktop/home1/home {
+  interface org.freedesktop.home1.Home {
+    methods:
+      Activate(in  s secret);
+      Deactivate();
+      Unregister();
+      Realize(in  s secret);
+      Remove();
+      Fixate(in  s secret);
+      Authenticate(in  s secret);
+      Update(in  s user_record);
+      Resize(in  t size,
+             in  s secret);
+      ChangePassword(in  s new_secret,
+                     in  s old_secret);
+      Lock();
+      Unlock(in  s secret);
+      Acquire(in  s secret,
+              in  b please_suspend,
+              out h send_fd);
+      Ref(in  b please_suspend,
+          out h send_fd);
+      Release();
+    properties:
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s UserName = '...';
+      readonly u UID = ...;
+      readonly (suusss) UnixRecord = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly s State = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates")
+      readonly (sb) UserRecord = ...;
+  };
+  interface org.freedesktop.DBus.Peer { ... };
+  interface org.freedesktop.DBus.Introspectable { ... };
+  interface org.freedesktop.DBus.Properties { ... };
+  interface org.freedesktop.DBus.ObjectManager { ... };
+};
+    </programlisting>
+
+    <!--Autogenerated cross-references for systemd.directives, do not edit-->
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.DBus.ObjectManager"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.home1.Home"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.DBus.ObjectManager"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.home1.Home"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Activate()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Deactivate()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Unregister()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Realize()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Remove()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Fixate()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Authenticate()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Update()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Resize()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ChangePassword()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Lock()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Unlock()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Acquire()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Ref()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Release()"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="UserName"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="UID"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="UnixRecord"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="State"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="UserRecord"/>
+
+    <!--End of Autogenerated section-->
+
+    <refsect2>
+      <title>Methods</title>
+
+      <para><function>Activate()</function>, <function>Deactivate()</function>,
+      <function>Unregister()</function>, <function>Realize()</function>, <function>Remove()</function>,
+      <function>Fixate()</function>, <function>Authenticate()</function>, <function>Update()</function>,
+      <function>Resize()</function>, <function>ChangePassword()</function>, <function>Lock()</function>,
+      <function>Unlock()</function>, <function>Acquire()</function>, <function>Ref()</function>,
+      <function>Release()</function> operate like their matching counterparts on the
+      <classname>org.freedesktop.home1.Manager</classname> interface (see above). The main difference is that
+      they are methods of the home directory objects, and hence carry no additional user name
+      parameter. Which of the two flavors of methods to call depends on the handles to the user known on the
+      client side: if only the user name is known, it's preferable to use the methods on the manager object
+      since they operate with user names only. If however the home object path was already acquired some way
+      it is preferable to operate on the <classname>org.freedesktop.home1.Home</classname> objects
+      instead.</para>
+    </refsect2>
+
+    <refsect2>
+      <title>Properties</title>
+
+      <para><varname>UserName</varname> contains the user name of the user account/home directory.</para>
+
+      <para><varname>UID</varname> contains the numeric UNIX UID of the user account.</para>
+
+      <para><varname>UnixRecord</varname> contains a structure encapsulating the six fields a
+      <structname>struct passwd</structname> typically contains (the password field is suppressed).</para>
+
+      <para><varname>State</varname> exposes the current state home the home directory.</para>
+
+      <para><varname>UserRecord</varname> contains the full JSON user record string of the user account.</para>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>Versioning</title>
+
+    <para>These D-Bus interfaces follow <ulink url="http://0pointer.de/blog/projects/versioning-dbus.html">
+    the usual interface versioning guidelines</ulink>.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-homed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>homectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
diff --git a/man/org.freedesktop.hostname1.xml b/man/org.freedesktop.hostname1.xml
new file mode 100644 (file)
index 0000000..6b2341e
--- /dev/null
@@ -0,0 +1,367 @@
+<?xml version="1.0"?>
+<!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
+<!ENTITY % entities SYSTEM "custom-entities.ent" >
+%entities;
+]>
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="org.freedesktop.hostname1" conditional='ENABLE_HOSTNAMED'
+    xmlns:xi="http://www.w3.org/2001/XInclude">
+  <refentryinfo>
+    <title>org.freedesktop.hostname1</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>org.freedesktop.hostname1</refentrytitle>
+    <manvolnum>5</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>org.freedesktop.hostname1</refname>
+    <refpurpose>The D-Bus interface of systemd-hostnamed</refpurpose>
+  </refnamediv>
+
+  <refsect1>
+    <title>Introduction</title>
+
+    <para>
+    <citerefentry><refentrytitle>systemd-hostnamed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    is a system service that can be used to control the hostname and related machine metadata from user
+    programs. This page describes the hostname semantics and the D-Bus interface.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>The D-Bus API</title>
+
+    <para>The service exposes the following interfaces on the bus:</para>
+
+    <programlisting executable="systemd-hostnamed" node="/org/freedesktop/hostname1" interface="org.freedesktop.hostname1">
+node /org/freedesktop/hostname1 {
+  interface org.freedesktop.hostname1 {
+    methods:
+      SetHostname(in  s hostname,
+                  in  b interactive);
+      SetStaticHostname(in  s hostname,
+                        in  b interactive);
+      SetPrettyHostname(in  s hostname,
+                        in  b interactive);
+      SetIconName(in  s icon,
+                  in  b interactive);
+      SetChassis(in  s chassis,
+                 in  b interactive);
+      SetDeployment(in  s deployment,
+                    in  b interactive);
+      SetLocation(in  s location,
+                  in  b interactive);
+      GetProductUUID(in  b interactive,
+                     out ay uuid);
+    properties:
+      readonly s Hostname = '...';
+      readonly s StaticHostname = '...';
+      readonly s PrettyHostname = '...';
+      readonly s IconName = '...';
+      readonly s Chassis = '...';
+      readonly s Deployment = '...';
+      readonly s Location = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s KernelName = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s KernelRelease = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s KernelVersion = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s OperatingSystemPrettyName = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s OperatingSystemCPEName = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s HomeURL = '...';
+  };
+  interface org.freedesktop.DBus.Peer { ... };
+  interface org.freedesktop.DBus.Introspectable { ... };
+  interface org.freedesktop.DBus.Properties { ... };
+};
+    </programlisting>
+
+    <!--Autogenerated cross-references for systemd.directives, do not edit-->
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.hostname1"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.hostname1"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetHostname()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetStaticHostname()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetPrettyHostname()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetIconName()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetChassis()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetDeployment()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetLocation()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetProductUUID()"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Hostname"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StaticHostname"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="PrettyHostname"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IconName"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Chassis"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Deployment"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Location"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="KernelName"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="KernelRelease"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="KernelVersion"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="OperatingSystemPrettyName"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="OperatingSystemCPEName"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="HomeURL"/>
+
+    <!--End of Autogenerated section-->
+
+    <para>Whenever the hostname or other metadata is changed via the daemon,
+    <function>PropertyChanged</function> signals are sent out to subscribed clients. Changing a hostname
+    using this interface is authenticated via
+    <ulink url="https://www.freedesktop.org/software/polkit/docs/latest/">polkit</ulink>.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Semantics</title>
+
+    <para>The <emphasis>static (configured) hostname</emphasis> is the one configured in
+    <filename>/etc/hostname</filename>. It is chosen by the local user. It is not always in sync with the
+    current hostname as returned by the
+    <citerefentry project="man-pages"><refentrytitle>gethostname</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    system call. If no hostname is configured this property will be the empty string. Setting this property
+    to the empty string will remove <filename>/etc/hostname</filename>. This property should be an
+    internet-style hostname, 7-bit lowercase ASCII, no special chars/spaces.</para>
+
+    <para>The <emphasis>transient (dynamic) hostname</emphasis> is the one configured via the kernel's
+    <citerefentry project="man-pages"><refentrytitle>sethostname</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+    It can be different from the static hostname if DHCP or mDNS have been configured to change the name
+    based on network information. <!-- FIXME: it's not DHCP that configures this... -->
+    This property is never empty. If no hostname is set this will default to
+    <literal>&FALLBACK_HOSTNAME;</literal> (configurable at compilation time). Setting this property to the
+    empty string will reset the dynamic hostname to the static hostname. If no static hostname is
+    configured the dynamic hostname will be reset to <literal>&FALLBACK_HOSTNAME;</literal>. This property
+    should be an internet-style hostname, 7-bit lowercase ASCII, no special chars/spaces.</para>
+
+    <para>The <emphasis>pretty hostname</emphasis> is a free-form UTF-8 hostname for presentation to the
+    user. User interfaces should ensure that the pretty hostname and the static hostname stay in sync.
+    I.e. when the former is <literal>Lennart’s Computer</literal> the latter should be
+    <literal>lennarts-computer</literal>. If no pretty hostname is set this setting will be the empty
+    string. Applications should then find a suitable fallback, such as the dynamic hostname.</para>
+
+    <para>The <emphasis>icon name</emphasis> is a name following the XDG icon naming spec. If not set,
+    information such as the chassis type (see below) is used to find a suitable fallback icon name
+    (i.e. <literal>computer-laptop</literal> vs. <literal>computer-desktop</literal> is picked based on the
+    chassis information). If no such data is available, the empty string is returned. In that case an application
+    should fall back to a replacement icon, for example <literal>computer</literal>. If this property is set
+    to the empty string, the automatic fallback name selection is enabled again.</para>
+
+    <para>The <emphasis>chassis type</emphasis> should be one of the currently defined chassis types:
+    <literal>desktop</literal>, <literal>laptop</literal>, <literal>server</literal>,
+    <literal>tablet</literal>, <literal>handset</literal>, as well as the special chassis types
+    <literal>vm</literal> and <literal>container</literal> for virtualized systems. Note that in most cases
+    the chassis type will be determined automatically from DMI/SMBIOS/ACPI firmware information. Writing to
+    this setting is hence useful only to override misdetected chassis types, or to configure the chassis type if
+    it could not be auto-detected. Set this property to the empty string to reenable the automatic detection of
+    the chassis type from firmware information.</para>
+
+    <para>Note that <filename>systemd-hostnamed</filename> starts only on request and terminates after a
+    short idle period. This effectively means that <function>PropertyChanged</function> messages are not sent
+    out for changes made directly on the files (as in: administrator edits the files with vi). This is
+    the intended behavior: manual configuration changes should require manual reloading.</para>
+
+    <para>The transient (dynamic) hostname maps directly to the kernel hostname. This hostname should be
+    assumed to be highly dynamic, and hence should be watched directly, without depending on
+    <function>PropertyChanged</function> messages from <filename>systemd-hostnamed</filename>. To accomplish
+    this, open <filename>/proc/sys/kernel/hostname</filename> and
+    <citerefentry project="man-pages"><refentrytitle>poll</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    for <constant>SIGHUP</constant> which is triggered by the kernel every time the hostname changes. Again:
+    this is special for the transient (dynamic) hostname, and does not apply to the configured (fixed)
+    hostname.</para>
+
+    <para>Applications may read the hostname data directly if hostname change notifications
+    are not necessary. Use
+    <citerefentry project="man-pages"><refentrytitle>gethostname</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <filename>/etc/hostname</filename> (possibly with per-distribution fallbacks), and
+    <citerefentry><refentrytitle>machine-info</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    for that. For more information on these files and syscalls see the respective man pages.</para>
+
+    <refsect2>
+      <title>Methods and Properties</title>
+
+      <para><function>SetHostname()</function> sets the transient (dynamic) hostname which is exposed by the
+      <varname>Hostname</varname> property. If empty, the transient hostname is set to the static hostname.
+      </para>
+
+      <para><function>SetStaticHostname()</function> sets the static hostname which is exposed by the
+      <varname>StaticHostname</varname> property. If empty, the built-in default of
+      <literal>&FALLBACK_HOSTNAME;</literal> is used.</para>
+
+      <para><function>SetPrettyHostname()</function> sets the pretty hostname which is exposed by the
+      <varname>PrettyHostname</varname> property.</para>
+
+      <para><function>SetIconName()</function>, <function>SetChassis()</function>,
+      <function>SetDeployment()</function>, and <function>SetLocation()</function> set the properties
+      <varname>IconName</varname> (the name of the icon representing for the machine),
+      <varname>Chassis</varname> (the machine form factor), <varname>Deployment</varname> (the system
+      deployment environment), and <varname>Location</varname> (physical system location), respectively.
+      </para>
+
+      <para><varname>PrettyHostname</varname>, <varname>IconName</varname>, <varname>Chassis</varname>,
+      <varname>Deployment</varname>, and <varname>Location</varname> are stored in
+      <filename>/etc/machine-info</filename>. See
+      <citerefentry><refentrytitle>machine-info</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
+      the semantics of those settings.</para>
+
+      <para><function>GetProductUUID()</function> returns the "product uuid" as exposed by the kernel based
+      on DMI information in <filename>/sys/class/dmi/id/product_uuid</filename>. Reading the file directly
+      requires root privileges, and this method allows access to unprivileged clients through the polkit
+      framework.</para>
+
+      <para><varname>KernelName</varname>, <varname>KernelRelease</varname>, and
+      <varname>KernelVersion</varname> expose the kernel name (e.g. <literal>Linux</literal>), release
+      (e.g. <literal>5.0.0-11</literal>), and version (i.e. the build number, e.g. <literal>#11</literal>) as
+      reported by
+      <citerefentry project="man-pages"><refentrytitle>uname</refentrytitle><manvolnum>2</manvolnum></citerefentry>.
+      <varname>OperatingSystemPrettyName</varname>, <varname>OperatingSystemCPEName</varname>, and
+      <varname>HomeURL</varname> expose the <varname>PRETTY_NAME=</varname>, <varname>CPE_NAME=</varname> and
+      <varname>HOME_URL=</varname> fields from
+      <citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry>. The
+      purpose of those properties is to allow remote clients to access this information over D-Bus. Local
+      clients can access the information directly.</para>
+    </refsect2>
+
+    <refsect2>
+      <title>Security</title>
+
+      <para>The <varname>interactive</varname> boolean parameters can be used to control whether polkit
+      should interactively ask the user for authentication credentials if required.</para>
+
+      <para>The polkit action for <function>SetHostname()</function> is
+      <interfacename>org.freedesktop.hostname1.set-hostname</interfacename>. For
+      <function>SetStaticHostname()</function> and <function>SetPrettyHostname()</function> it is
+      <interfacename>org.freedesktop.hostname1.set-static-hostname</interfacename>. For
+      <function>SetIconName()</function> and <function>SetChassis()</function> it is
+      <interfacename>org.freedesktop.hostname1.set-machine-info</interfacename>.</para>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>Recommendations</title>
+
+    <para>Here are three examples that show how the pretty hostname and the icon name should be used:
+    <itemizedlist>
+      <listitem><para>When registering DNS-SD services: use the pretty hostname in the service name, and pass
+      the icon name in the TXT data, if there is an icon name. Browsing clients can then show the server icon
+      on each service. This is especially useful for WebDAV applications or UPnP media sharing.
+      </para></listitem>
+
+      <listitem><para>Set the bluetooth name to the pretty hostname.</para></listitem>
+
+      <listitem><para>When your file browser has a "Computer" icon, replace the name with the pretty hostname
+      if set, and the icon with the icon name, if it is set.</para></listitem>
+    </itemizedlist></para>
+
+    <para>To properly handle name lookups with changing local hostnames without having to edit
+    <filename>/etc/hosts</filename>, we recommend using <filename>systemd-hostnamed</filename> in combination
+    with <citerefentry><refentrytitle>nss-myhostname</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+    </para>
+
+    <para>A client that wants to change the local hostname for DHCP/mDNS should invoke
+    <code>SetHostname("newname", false)</code> as soon as the name is available and afterwards reset it via
+    <code>SetHostname("")</code>.</para>
+
+    <para>Here are some recommendations to follow when generating a static (internet) hostname from a pretty
+    name:
+    <itemizedlist>
+      <listitem><para>Generate a single DNS label only, not an FQDN. That means no dots allowed. Strip them,
+      or replace them with <literal>-</literal>.</para></listitem>
+
+      <listitem><para>It's probably safer to not use any non-ASCII chars, even if DNS allows this in some way
+      these days. In fact, restrict your charset to <literal>a-zA-Z0-9</literal> and <literal>-</literal>.
+      Strip other chars, or try to replace them in some smart way with chars from this set, for example
+      <literal>ä</literal> → <literal>ae</literal>, and use <literal>-</literal> as the replacement for all
+      punctuation characters and whitespace.</para></listitem>
+
+      <listitem><para>Try to avoid creating repeated <literal>-</literal>, as well as <literal>-</literal> as
+      the first or last char.</para></listitem>
+
+      <listitem><para>Limit the hostname to 63 chars, which is the length of a DNS label.</para></listitem>
+
+      <listitem><para>If after stripping special chars the empty string is the result, you can pass this
+      as-is to <filename>systemd-hostnamed</filename> in which case it will automatically use
+      <literal>&FALLBACK_HOSTNAME;</literal>.</para></listitem>
+
+      <listitem><para>Uppercase charaacters should be replaced with their lowercase equivalents.
+      </para></listitem>
+    </itemizedlist></para>
+
+    <para>Note that while <filename>systemd-hostnamed</filename> applies some checks to the hostname you pass
+    they are much looser than the recommendations above. For example, <filename>systemd-hostnamed</filename>
+    will also accept <literal>_</literal> in the hostname, but we recommend not using this to avoid clashes
+    with DNS-SD service types. Also <filename>systemd-hostnamed</filename> allows longer hostnames, but
+    because of the DNS label limitations, we recommend not making use of this.</para>
+
+    <para>Here are a couple of example conversions:
+    <itemizedlist>
+      <listitem><para><literal>Lennart's PC</literal> → <literal>lennarts-pc</literal></para></listitem>
+      <listitem><para><literal>Müllers Computer</literal> → <literal>muellers-computer</literal></para></listitem>
+      <listitem><para><literal>Voran!</literal> → <literal>voran</literal></para></listitem>
+      <listitem><para><literal>Es war einmal ein Männlein</literal> → <literal>es-war-einmal-ein-maennlein</literal></para></listitem>
+      <listitem><para><literal>Jawoll. Ist doch wahr!</literal> → <literal>jawoll-ist-doch-wahr</literal></para></listitem>
+      <listitem><para><literal>レナート</literal> → <literal>localhost</literal></para></listitem>
+      <listitem><para><literal>...zack!!! zack!...</literal> → <literal>zack-zack</literal></para></listitem>
+    </itemizedlist></para>
+
+    <para>Of course, an already valid internet hostname label you enter and pass through this
+    conversion should stay unmodified, so that users have direct control of it, if they want — by simply
+    ignoring the fact that the pretty hostname is pretty and just edit it as if it was the normal internet
+    name.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Versioning</title>
+
+    <para>These D-Bus interfaces follow <ulink url="http://0pointer.de/blog/projects/versioning-dbus.html">
+    the usual interface versioning guidelines</ulink>.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Examples</title>
+
+    <example>
+      <title>Introspect <interfacename>org.freedesktop.hostname1</interfacename> on the bus</title>
+
+      <programlisting>$ gdbus introspect --system \
+  --dest org.freedesktop.hostname1 \
+  --object-path /org/freedesktop/hostname1
+      </programlisting>
+    </example>
+  </refsect1>
+
+  <refsect1>
+    <title>See also</title>
+
+    <para>David Zeuthen's original Fedora
+    <ulink url="https://fedoraproject.org/wiki/Features/BetterHostname">Feature page about xdg-hostname</ulink></para>
+  </refsect1>
+</refentry>
diff --git a/man/org.freedesktop.import1.xml b/man/org.freedesktop.import1.xml
new file mode 100644 (file)
index 0000000..56ce9f0
--- /dev/null
@@ -0,0 +1,348 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" >
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="org.freedesktop.import1" conditional='ENABLE_IMPORTD'
+    xmlns:xi="http://www.w3.org/2001/XInclude">
+  <refentryinfo>
+    <title>org.freedesktop.import1</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>org.freedesktop.import1</refentrytitle>
+    <manvolnum>5</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>org.freedesktop.import1</refname>
+    <refpurpose>The D-Bus interface of systemd-importd</refpurpose>
+  </refnamediv>
+
+  <refsect1>
+    <title>Introduction</title>
+
+    <para>
+    <citerefentry><refentrytitle>systemd-importd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    is a system service which may be used to import, export and download additional system images. These
+    images can be used by tools such as
+    <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+     to run local containers. The service is used as the backend for <command>machinectl pull-raw</command>,
+     <command>machinectl pull-tar</command> and related commands. This page describes the D-Bus interface.
+    </para>
+
+    <para>Note that
+    <citerefentry><refentrytitle>systemd-importd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    is mostly a small companion service for
+    <citerefentry><refentrytitle>systemd-machined.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+    Many operations to manipulate local container and VM images are hence available via the <command>systemd-machined</command> D-Bus API, c.f.
+    <citerefentry><refentrytitle>org.freedesktop.machine1</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>The Manager Object</title>
+
+    <para>The service exposes the following interfaces on the Manager object on the bus:</para>
+
+    <programlisting executable="systemd-importd" node="/org/freedesktop/import1" interface="org.freedesktop.import1.Manager">
+node /org/freedesktop/import1 {
+  interface org.freedesktop.import1.Manager {
+    methods:
+      ImportTar(in  h fd,
+                in  s local_name,
+                in  b force,
+                in  b read_only,
+                out u transfer_id,
+                out o transfer_path);
+      ImportRaw(in  h fd,
+                in  s local_name,
+                in  b force,
+                in  b read_only,
+                out u transfer_id,
+                out o transfer_path);
+      ImportFileSystem(in  h fd,
+                       in  s local_name,
+                       in  b force,
+                       in  b read_only,
+                       out u transfer_id,
+                       out o transfer_path);
+      ExportTar(in  s local_name,
+                in  h fd,
+                in  s format,
+                out u transfer_id,
+                out o transfer_path);
+      ExportRaw(in  s local_name,
+                in  h fd,
+                in  s format,
+                out u transfer_id,
+                out o transfer_path);
+      PullTar(in  s url,
+              in  s local_name,
+              in  s verify_mode,
+              in  b force,
+              out u transfer_id,
+              out o transfer_path);
+      PullRaw(in  s url,
+              in  s local_name,
+              in  s verify_mode,
+              in  b force,
+              out u transfer_id,
+              out o transfer_path);
+      ListTransfers(out a(usssdo) transfers);
+      CancelTransfer(in  u transfer_id);
+    signals:
+      TransferNew(u transfer_id,
+                  o transfer_path);
+      TransferRemoved(u transfer_id,
+                      o transfer_path,
+                      s result);
+  };
+  interface org.freedesktop.DBus.Peer { ... };
+  interface org.freedesktop.DBus.Introspectable { ... };
+  interface org.freedesktop.DBus.Properties { ... };
+};
+    </programlisting>
+
+    <!--method ImportFileSystem is not documented!-->
+
+    <!--Autogenerated cross-references for systemd.directives, do not edit-->
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.import1.Manager"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.import1.Manager"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ImportTar()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ImportRaw()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ImportFileSystem()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ExportTar()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ExportRaw()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="PullTar()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="PullRaw()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ListTransfers()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="CancelTransfer()"/>
+
+    <variablelist class="dbus-signal" generated="True" extra-ref="TransferNew"/>
+
+    <variablelist class="dbus-signal" generated="True" extra-ref="TransferRemoved"/>
+
+    <!--End of Autogenerated section-->
+
+    <refsect2>
+      <title>Methods</title>
+
+      <para><function>ImportTar()</function> and <function>ImportRaw()</function> import a system image and
+      place it into <filename>/var/lib/machines/</filename>. The first argument should be a file descriptor
+      (opened for reading) referring to the tar or raw file to import. It should reference a file on disk,
+      a pipe or a socket. When <function>ImportTar()</function> is used the file descriptor should
+      refer to a tar file, optionally compressed with
+      <citerefentry project="die-net"><refentrytitle>gzip</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry project="die-net"><refentrytitle>bzip2</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      or
+      <citerefentry project="die-net"><refentrytitle>xz</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
+      <command>systemd-importd</command> will detect the used compression scheme (if any) automatically. When
+      <function>ImportRaw()</function> is used the file descriptor should refer to a raw or qcow2 disk image
+      containing an MBR or GPT disk label, also optionally compressed with gzip, bzip2 or xz. In either case,
+      if the file is specified as a file descriptor on disk, progress information is generated for the import
+      operation (as in that case we know the total size on disk). If a socket or pipe is specified, progress information is not
+      available. The file descriptor argument is followed by a local name for the image. This should be a
+      name suitable as a hostname and will be used to name the imported image below
+      <filename>/var/lib/machines</filename>. A tar import is placed as a directory tree or a
+      <citerefentry project="man-pages"><refentrytitle>btrfs</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+      subvolume below <filename>/var/lib/machines/</filename> under the specified name with no suffix
+      appended. A raw import is placed as a file in <filename>/var/lib/machines/</filename> with the
+      <filename>.raw</filename> suffix appended. If the <option>force</option> argument is true, any
+      pre-existing image with the same name is removed before starting the operation. Otherwise, the
+      operation fails if an image with the same name already exists. Finally, the
+      <option>read_only</option> argument controls
+      whether to create a writable or read-only image. Both methods return immediately after starting the import,
+      with the import transfer ongoing. They return a pair of transfer identifier and object path, which may
+      be used to retrieve progress information about the transfer or to cancel it. The transfer identifier is a
+      simple numeric identifier, the object path references an
+      <interfacename>org.freedesktop.import1.Transfer</interfacename> object, see below. Listen for a
+      <function>TransferRemoved</function> signal for the transfer ID in order to detect when a transfer is
+      complete. The returned transfer object is useful to determine the current progress or log output of the
+      ongoing import operation.</para>
+
+      <para><function>ExportTar()</function> and <function>ExportRaw()</function> implement the reverse
+      operation, and may be used to export a system image in order to place it in a tar or raw image. They
+      take the machine name to export as their first parameter, followed by a file descriptor (opened for writing)
+      where the tar or raw file will be written. It may either reference a file on disk or a pipe/socket. The
+      third argument specifies in which compression format to write the image. It takes one of
+      <literal>uncompressed</literal>, <literal>xz</literal>, <literal>bzip2</literal> or
+      <literal>gzip</literal>, depending on which compression scheme is required. The image written to the
+      specified file descriptor will be a tar file in case of <function>ExportTar()</function> or a raw disk
+      image in case of <function>ExportRaw()</function>. Note that currently raw disk images may not be
+      exported as tar files, and vice versa. This restriction might be lifted eventually. The method
+      returns a transfer identifier and object path for cancelling or tracking the export operation, similar
+      to <function>ImportTar()</function> or <function>ImportRaw()</function> as described above.</para>
+
+      <para><function>PullTar()</function> and <function>PullRaw()</function> may be used to download, verify
+      and import a system image from a URL. They take an URL argument which should point to a tar or
+      raw file on the <literal>http://</literal> or <literal>https://</literal> protocols, possibly
+      compressed with xz, bzip2 or gzip. The second argument is a local name for the image. It should be
+      suitable as a hostname, similar to the matching argument of the <function>ImportTar()</function> and
+      <function>ImportRaw()</function> methods above. The third argument indicates the verification mode for
+      the image. It may be one of <literal>no</literal>, <literal>checksum</literal>,
+      <literal>signature</literal>. <literal>no</literal> turns off any kind of verification of the image;
+      <literal>checksum</literal> looks for a <filename>SHA256SUM</filename> file next to the downloaded
+      image and verifies any SHA256 hash value in that file against the image; <literal>signature</literal>
+      does the same but also tries to authenticate the <filename>SHA256SUM</filename> file via
+      <citerefentry project="man-pages"><refentrytitle>gpg</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+      first. The last argument indicates whether to replace a possibly pre-existing image with the same local
+      name (if <literal>true</literal>), or whether to fail (if <literal>false</literal>). Like the import
+      and export calls above, these calls return a pair of transfer identifier and object path for the ongoing
+      download.</para>
+
+      <para><function>ListTransfers()</function> returns a list of ongoing import, export or download
+      operations as created with the six calls described above. It returns an array of structures which
+      consist of the numeric transfer identifier, a string indicating the operation (one of
+      <literal>import-tar</literal>, <literal>import-raw</literal>, <literal>export-tar</literal>,
+      <literal>export-raw</literal>, <literal>pull-tar</literal> or <literal>pull-raw</literal>), a string
+      describing the remote file (in case of download operations this is the source URL, in case of
+      import/export operations this is a short string describing the file descriptor passed in), a string
+      with the local machine image name, a progress value between 0.0 (for 0%) and 1.0 (for 100%), as well as
+      the transfer object path.</para>
+
+      <para><function>CancelTransfer()</function> may be used to cancel an ongoing import, export or download
+      operation. Simply specify the transfer identifier to cancel the ongoing operation.</para>
+    </refsect2>
+
+    <refsect2>
+      <title>Signals</title>
+
+      <para>The <function>TransferNew</function> signal is generated each time a new transfer is started with
+      the import, export or download calls described above. It carries the transfer ID and object path that
+      have just been created.</para>
+
+      <para>The <function>TransferRemoved</function> signal is sent each time a transfer finishes,
+      is canceled or fails. It also carries the transfer ID and object path, followed by a string indicating
+      the result of the operation, which is one of <literal>done</literal> (on success),
+      <literal>canceled</literal> or <literal>failed</literal>.</para>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>The Transfer Object</title>
+
+    <programlisting executable="systemd-importd" node="/org/freedesktop/import1/transfer/_1" interface="org.freedesktop.import1.Transfer">
+node /org/freedesktop/import1/transfer/_1 {
+  interface org.freedesktop.import1.Transfer {
+    methods:
+      Cancel();
+    signals:
+      LogMessage(u priority,
+                 s line);
+    properties:
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u Id = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s Local = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s Remote = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s Type = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s Verify = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly d Progress = ...;
+  };
+  interface org.freedesktop.DBus.Peer { ... };
+  interface org.freedesktop.DBus.Introspectable { ... };
+  interface org.freedesktop.DBus.Properties { ... };
+};
+    </programlisting>
+
+    <!--signal LogMessage is not documented!-->
+
+    <!--Autogenerated cross-references for systemd.directives, do not edit-->
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.import1.Transfer"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.import1.Transfer"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Cancel()"/>
+
+    <variablelist class="dbus-signal" generated="True" extra-ref="LogMessage"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Id"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Local"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Remote"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Type"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Verify"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Progress"/>
+
+    <!--End of Autogenerated section-->
+
+    <refsect2>
+      <title>Methods</title>
+
+      <para>The <function>Cancel()</function> method may be used to cancel the transfer. It takes no
+      parameters. This method is pretty much equivalent to the <function>CancelTransfer()</function> method
+      on the <structname>Manager</structname> interface (see above), but is exposed on the
+      <structname>Transfer</structname> object itself instead of taking a transfer ID.</para>
+    </refsect2>
+
+    <refsect2>
+      <title>Properties</title>
+
+      <para>The <varname>Id</varname> property exposes the numeric transfer ID of the transfer object.</para>
+
+      <para>The <varname>Local</varname>, <varname>Remote</varname> and <varname>Type</varname> properties
+      expose the local container name of this transfer, the remote source (in case of download: the URL, in
+      case of import/export: a string describing the file descriptor passed in), and the type of operation
+      (see the Manager's <function>ListTransfer()</function> method above for an explanation of the possible
+      values).</para>
+
+      <para>The <varname>Verify</varname> property exposes the selected verification setting and is only
+      defined for download operations (see above).</para>
+
+      <para>The <varname>Progress</varname> property exposes the current progress of the transfer as a value
+      between 0.0 and 1.0. To show a progress bar on screen we recommend to query this value in regular
+      intervals, for example every 500 ms or so.</para>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>Examples</title>
+
+    <example>
+      <title>Introspect <interfacename>org.freedesktop.import1.Manager</interfacename> on the bus</title>
+
+      <programlisting>$ gdbus introspect --system \
+  --dest org.freedesktop.import1 \
+  --object-path /org/freedesktop/import1
+      </programlisting>
+    </example>
+
+    <example>
+      <title>Introspect <interfacename>org.freedesktop.import1.Transfer</interfacename> on the bus</title>
+
+      <programlisting>$ gdbus introspect --system \
+  --dest org.freedesktop.import1 \
+  --object-path /org/freedesktop/import1/transfer/_1
+      </programlisting>
+    </example>
+  </refsect1>
+
+  <refsect1>
+    <title>Versioning</title>
+
+    <para>These D-Bus interfaces follow <ulink url="http://0pointer.de/blog/projects/versioning-dbus.html">
+    the usual interface versioning guidelines</ulink>.</para>
+  </refsect1>
+</refentry>
diff --git a/man/org.freedesktop.locale1.xml b/man/org.freedesktop.locale1.xml
new file mode 100644 (file)
index 0000000..3956eaf
--- /dev/null
@@ -0,0 +1,188 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" >
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="org.freedesktop.locale1" conditional='ENABLE_LOCALED'
+    xmlns:xi="http://www.w3.org/2001/XInclude">
+  <refentryinfo>
+    <title>org.freedesktop.locale1</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>org.freedesktop.locale1</refentrytitle>
+    <manvolnum>5</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>org.freedesktop.locale1</refname>
+    <refpurpose>The D-Bus interface of systemd-localed</refpurpose>
+  </refnamediv>
+
+  <refsect1>
+    <title>Introduction</title>
+
+    <para>
+    <citerefentry><refentrytitle>systemd-localed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    is a system service that can be used to control the system locale and keyboard mapping from user
+    programs. This page describes the D-Bus interface.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>The D-Bus API</title>
+
+    <para>The service exposes the following interfaces on the bus:</para>
+
+    <programlisting executable="systemd-localed" node="/org/freedesktop/locale1" interface="org.freedesktop.locale1">
+node /org/freedesktop/locale1 {
+  interface org.freedesktop.locale1 {
+    methods:
+      SetLocale(in  as locale,
+                in  b interactive);
+      SetVConsoleKeyboard(in  s keymap,
+                          in  s keymap_toggle,
+                          in  b convert,
+                          in  b interactive);
+      SetX11Keyboard(in  s layout,
+                     in  s model,
+                     in  s variant,
+                     in  s options,
+                     in  b convert,
+                     in  b interactive);
+    properties:
+      readonly as Locale = ['...', ...];
+      readonly s X11Layout = '...';
+      readonly s X11Model = '...';
+      readonly s X11Variant = '...';
+      readonly s X11Options = '...';
+      readonly s VConsoleKeymap = '...';
+      readonly s VConsoleKeymapToggle = '...';
+  };
+  interface org.freedesktop.DBus.Peer { ... };
+  interface org.freedesktop.DBus.Introspectable { ... };
+  interface org.freedesktop.DBus.Properties { ... };
+};
+    </programlisting>
+
+    <!--Autogenerated cross-references for systemd.directives, do not edit-->
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.locale1"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.locale1"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetLocale()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetVConsoleKeyboard()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetX11Keyboard()"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Locale"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="X11Layout"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="X11Model"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="X11Variant"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="X11Options"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="VConsoleKeymap"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="VConsoleKeymapToggle"/>
+
+    <!--End of Autogenerated section-->
+
+    <refsect2>
+      <title>Methods</title>
+
+      <para>If you set a new system locale all old system locale settings will be dropped and the new
+      settings will be saved to disk. The locale will also be passed to the system manager, and subsequently started
+      daemons will inherit the new system locale. Note that already running daemons will not learn about the
+      new value.</para>
+
+      <para>The <function>SetVConsoleKeyboard()</function> method may be used to set the key mapping for the
+      virtual console. Similarly, <function>SetX11Keyboard()</function> may be used to set the default key
+      mapping of any X11 servers.</para>
+
+      <para>Note that <function>SetVConsoleKeyboard()</function> instantly applies the new key mapping to the
+      console, while <function>SetX11Keyboard()</function> simply sets a default that may be used by later
+      sessions.</para>
+
+      <para>The boolean argument <varname>convert</varname> may be set to optionally convert the console
+      keyboard configuration to X11 keyboard mappings and vice versa. If true and
+      <function>SetVConsoleKeyboard()</function> is used, the nearest X11 keyboard setting for the chosen
+      console setting is set. If true and <function>SetX11Keyboard()</function> is used, the nearest console
+      keyboard setting for the chosen X11 setting is set. Hence, it is usually sufficient to call only one of the
+      two functions.</para>
+
+      <para>For graphical UIs that need to set the system keyboard mapping simply invoke
+      <function>SetX11Keyboard()</function>, set <varname>convert=true</varname> and ignore
+      <function>SetVConsoleKeyboard()</function>.</para>
+
+      <para>Use the empty string for the keymap parameters you wish not to set.</para>
+
+      <para>The <varname>interactive</varname> boolean parameters can be used to control whether
+      <ulink url="https://www.freedesktop.org/software/polkit/docs/latest/">polkit</ulink>
+      should interactively ask the user for authentication credentials if required.</para>
+    </refsect2>
+
+    <refsect2>
+      <title>Signals</title>
+
+      <para>Whenever the system locale or keymap is changed via the daemon,
+      <function>PropertyChanged</function> signals are sent out to which clients can subscribe.</para>
+    </refsect2>
+
+    <refsect2>
+      <title>Properties</title>
+
+      <para>The system locale is shown in the <varname>Locale</varname> property. It is an array containing
+      environment-variable-assignment-like strings. The following strings are known:
+      <varname>LANG=</varname>, <varname>LC_CTYPE=</varname>, <varname>LC_NUMERIC=</varname>,
+      <varname>LC_TIME=</varname>, <varname>LC_COLLATE=</varname>, <varname>LC_MONETARY=</varname>,
+      <varname>LC_MESSAGES=</varname>, <varname>LC_PAPER=</varname>, <varname>LC_NAME=</varname>,
+      <varname>LC_ADDRESS=</varname>, <varname>LC_TELEPHONE=</varname>, <varname>LC_MEASUREMENT=</varname>,
+      <varname>LC_IDENTIFICATION=</varname>.</para>
+
+      <para>The <varname>X11Layout</varname>, <varname>X11Model</varname>, <varname>X11Variant</varname>, and
+      <varname>X11Options</varname> properties show values configurable with
+      <function>SetX11Keyboard()</function> described above (or <function>SetVConsoleKeyboard()</function>
+      with <varname>convert=true</varname>). The <varname>VConsoleKeymap</varname> and
+      <varname>VConsoleKeymapToggle</varname> properties show values configurable with
+      <function>SetVConsoleKeyboard()</function> (or <function>SetX11Keyboard()</function> with
+      <varname>convert=true</varname>).</para>
+    </refsect2>
+
+    <refsect2>
+      <title>Security</title>
+
+      <para>Changing the system locale or keymap using this interface is authenticated via polkit. The
+      polkit action for <function>SetLocale()</function> is
+      <constant>org.freedesktop.locale1.set-locale</constant>. The polkit action for
+      <function>SetX11Keyboard()</function> and <function>SetVConsoleKeyboard()</function> is
+      <constant>org.freedesktop.locale1.set-keyboard</constant>.</para>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>Examples</title>
+
+    <example>
+      <title>Introspect <interfacename>org.freedesktop.locale1</interfacename> on the bus</title>
+
+      <programlisting>
+$ gdbus introspect --system \
+  --dest org.freedesktop.locale1 \
+  --object-path /org/freedesktop/locale1
+      </programlisting>
+    </example>
+  </refsect1>
+
+  <refsect1>
+    <title>Versioning</title>
+
+    <para>These D-Bus interfaces follow <ulink url="http://0pointer.de/blog/projects/versioning-dbus.html">
+    the usual interface versioning guidelines</ulink>.</para>
+  </refsect1>
+</refentry>
diff --git a/man/org.freedesktop.login1.xml b/man/org.freedesktop.login1.xml
new file mode 100644 (file)
index 0000000..1b7c3b6
--- /dev/null
@@ -0,0 +1,1390 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" >
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="org.freedesktop.login1" conditional='ENABLE_LOGIND'
+    xmlns:xi="http://www.w3.org/2001/XInclude">
+  <refentryinfo>
+    <title>org.freedesktop.login1</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>org.freedesktop.login1</refentrytitle>
+    <manvolnum>5</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>org.freedesktop.login1</refname>
+    <refpurpose>The D-Bus interface of systemd-logind</refpurpose>
+  </refnamediv>
+
+  <refsect1>
+    <title>Introduction</title>
+
+    <para><citerefentry><refentrytitle>systemd-logind.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    is a system service that keeps track of user logins and seats.</para>
+
+    <para>The daemon provides both a C library interface as well as a D-Bus interface. The library interface
+    may be used to introspect and watch the state of user logins and seats. The bus interface provides the
+    same functionality but in addition may also be used to make changes to the system state. For more information please
+    consult <citerefentry><refentrytitle>sd-login</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>The Manager Object</title>
+
+    <para>The service exposes the following interfaces on the Manager object on the bus:</para>
+
+    <programlisting executable="systemd-logind" node="/org/freedesktop/login1" interface="org.freedesktop.login1.Manager">
+node /org/freedesktop/login1 {
+  interface org.freedesktop.login1.Manager {
+    methods:
+      GetSession(in  s session_id,
+                 out o object_path);
+      GetSessionByPID(in  u pid,
+                      out o object_path);
+      GetUser(in  u uid,
+              out o object_path);
+      GetUserByPID(in  u pid,
+                   out o object_path);
+      GetSeat(in  s seat_id,
+              out o object_path);
+      ListSessions(out a(susso) sessions);
+      ListUsers(out a(uso) users);
+      ListSeats(out a(so) seats);
+      ListInhibitors(out a(ssssuu) inhibitors);
+      CreateSession(in  u uid,
+                    in  u pid,
+                    in  s service,
+                    in  s type,
+                    in  s class,
+                    in  s desktop,
+                    in  s seat_id,
+                    in  u vtnr,
+                    in  s tty,
+                    in  s display,
+                    in  b remote,
+                    in  s remote_user,
+                    in  s remote_host,
+                    in  a(sv) properties,
+                    out s session_id,
+                    out o object_path,
+                    out s runtime_path,
+                    out h fifo_fd,
+                    out u uid,
+                    out s seat_id,
+                    out u vtnr,
+                    out b existing);
+      ReleaseSession(in  s session_id);
+      ActivateSession(in  s session_id);
+      ActivateSessionOnSeat(in  s session_id,
+                            in  s seat_id);
+      LockSession(in  s session_id);
+      UnlockSession(in  s session_id);
+      LockSessions();
+      UnlockSessions();
+      KillSession(in  s session_id,
+                  in  s who,
+                  in  i signal_number);
+      KillUser(in  u uid,
+               in  i signal_number);
+      TerminateSession(in  s session_id);
+      TerminateUser(in  u uid);
+      TerminateSeat(in  s seat_id);
+      SetUserLinger(in  u uid,
+                    in  b enable,
+                    in  b interactive);
+      AttachDevice(in  s seat_id,
+                   in  s sysfs_path,
+                   in  b interactive);
+      FlushDevices(in  b interactive);
+      PowerOff(in  b interactive);
+      Reboot(in  b interactive);
+      Halt(in  b interactive);
+      Suspend(in  b interactive);
+      Hibernate(in  b interactive);
+      HybridSleep(in  b interactive);
+      SuspendThenHibernate(in  b interactive);
+      CanPowerOff(out s result);
+      CanReboot(out s result);
+      CanHalt(out s result);
+      CanSuspend(out s result);
+      CanHibernate(out s result);
+      CanHybridSleep(out s result);
+      CanSuspendThenHibernate(out s result);
+      ScheduleShutdown(in  s type,
+                       in  t usec);
+      CancelScheduledShutdown(out b cancelled);
+      Inhibit(in  s what,
+              in  s who,
+              in  s why,
+              in  s mode,
+              out h pipe_fd);
+      CanRebootParameter(out s result);
+      SetRebootParameter(in  s parameter);
+      CanRebootToFirmwareSetup(out s result);
+      SetRebootToFirmwareSetup(in  b enable);
+      CanRebootToBootLoaderMenu(out s result);
+      SetRebootToBootLoaderMenu(in  t timeout);
+      CanRebootToBootLoaderEntry(out s result);
+      SetRebootToBootLoaderEntry(in  s boot_loader_entry);
+      SetWallMessage(in  s wall_message,
+                     in  b enable);
+    signals:
+      SessionNew(s session_id,
+                 o object_path);
+      SessionRemoved(s session_id,
+                     o object_path);
+      UserNew(u uid,
+              o object_path);
+      UserRemoved(u uid,
+                  o object_path);
+      SeatNew(s seat_id,
+              o object_path);
+      SeatRemoved(s seat_id,
+                  o object_path);
+      PrepareForShutdown(b start);
+      PrepareForSleep(b start);
+    properties:
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      @org.freedesktop.systemd1.Privileged("true")
+      readwrite b EnableWallMessages = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      @org.freedesktop.systemd1.Privileged("true")
+      readwrite s WallMessage = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u NAutoVTs = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as KillOnlyUsers = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as KillExcludeUsers = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b KillUserProcesses = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly s RebootParameter = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b RebootToFirmwareSetup = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t RebootToBootLoaderMenu = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly s RebootToBootLoaderEntry = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as BootLoaderEntries = ['...', ...];
+      readonly b IdleHint = ...;
+      readonly t IdleSinceHint = ...;
+      readonly t IdleSinceHintMonotonic = ...;
+      readonly s BlockInhibited = '...';
+      readonly s DelayInhibited = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t InhibitDelayMaxUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t UserStopDelayUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s HandlePowerKey = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s HandleSuspendKey = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s HandleHibernateKey = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s HandleLidSwitch = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s HandleLidSwitchExternalPower = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s HandleLidSwitchDocked = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t HoldoffTimeoutUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s IdleAction = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t IdleActionUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b PreparingForShutdown = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b PreparingForSleep = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly (st) ScheduledShutdown = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b Docked = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b LidClosed = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b OnExternalPower = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b RemoveIPC = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t RuntimeDirectorySize = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t RuntimeDirectoryInodesMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t InhibitorsMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t NCurrentInhibitors = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t SessionsMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t NCurrentSessions = ...;
+  };
+  interface org.freedesktop.DBus.Peer { ... };
+  interface org.freedesktop.DBus.Introspectable { ... };
+  interface org.freedesktop.DBus.Properties { ... };
+};
+    </programlisting>
+
+    <!--Autogenerated cross-references for systemd.directives, do not edit-->
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.login1.Manager"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.login1.Manager"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetSession()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetSessionByPID()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetUser()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetUserByPID()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetSeat()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ListSessions()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ListUsers()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ListSeats()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ListInhibitors()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="CreateSession()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ReleaseSession()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ActivateSession()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ActivateSessionOnSeat()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="LockSession()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="UnlockSession()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="LockSessions()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="UnlockSessions()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="KillSession()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="KillUser()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="TerminateSession()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="TerminateUser()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="TerminateSeat()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetUserLinger()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="AttachDevice()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="FlushDevices()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="PowerOff()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Reboot()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Halt()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Suspend()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Hibernate()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="HybridSleep()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SuspendThenHibernate()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="CanPowerOff()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="CanReboot()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="CanHalt()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="CanSuspend()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="CanHibernate()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="CanHybridSleep()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="CanSuspendThenHibernate()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ScheduleShutdown()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="CancelScheduledShutdown()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Inhibit()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="CanRebootParameter()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetRebootParameter()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="CanRebootToFirmwareSetup()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetRebootToFirmwareSetup()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="CanRebootToBootLoaderMenu()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetRebootToBootLoaderMenu()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="CanRebootToBootLoaderEntry()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetRebootToBootLoaderEntry()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetWallMessage()"/>
+
+    <variablelist class="dbus-signal" generated="True" extra-ref="SessionNew"/>
+
+    <variablelist class="dbus-signal" generated="True" extra-ref="SessionRemoved"/>
+
+    <variablelist class="dbus-signal" generated="True" extra-ref="UserNew"/>
+
+    <variablelist class="dbus-signal" generated="True" extra-ref="UserRemoved"/>
+
+    <variablelist class="dbus-signal" generated="True" extra-ref="SeatNew"/>
+
+    <variablelist class="dbus-signal" generated="True" extra-ref="SeatRemoved"/>
+
+    <variablelist class="dbus-signal" generated="True" extra-ref="PrepareForShutdown"/>
+
+    <variablelist class="dbus-signal" generated="True" extra-ref="PrepareForSleep"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="EnableWallMessages"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="WallMessage"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="NAutoVTs"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="KillOnlyUsers"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="KillExcludeUsers"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="KillUserProcesses"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RebootParameter"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RebootToFirmwareSetup"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RebootToBootLoaderMenu"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RebootToBootLoaderEntry"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BootLoaderEntries"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IdleHint"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IdleSinceHint"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IdleSinceHintMonotonic"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BlockInhibited"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DelayInhibited"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="InhibitDelayMaxUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="UserStopDelayUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="HandlePowerKey"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="HandleSuspendKey"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="HandleHibernateKey"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="HandleLidSwitch"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="HandleLidSwitchExternalPower"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="HandleLidSwitchDocked"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="HoldoffTimeoutUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IdleAction"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IdleActionUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="PreparingForShutdown"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="PreparingForSleep"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ScheduledShutdown"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Docked"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LidClosed"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="OnExternalPower"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RemoveIPC"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RuntimeDirectorySize"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RuntimeDirectoryInodesMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="InhibitorsMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="NCurrentInhibitors"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SessionsMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="NCurrentSessions"/>
+
+    <!--End of Autogenerated section-->
+
+    <refsect2>
+      <title>Methods</title>
+
+      <para><function>GetSession()</function> may be used to get the session object path for the session with
+      the specified ID. Similarly, <function>GetUser()</function> and <function>GetSeat()</function> get the
+      user and seat objects, respectively. <function>GetSessionByPID()</function> and
+      <function>GetUserByPID()</function> get the session/user object the specified PID belongs to if there
+      is any.</para>
+
+      <para><function>ListSessions()</function> returns an array of all current sessions. The structures in
+      the array consist of the following fields: session id, user id, user name, seat id, session object
+      path. If a session does not have a seat attached, the seat id field will be an empty string.</para>
+
+      <para><function>ListUsers()</function> returns an array of all currently logged in users. The
+      structures in the array consist of the following fields: user id, user name, user object path.</para>
+
+      <para><function>ListSeats()</function> returns an array of all currently available seats. The
+      structure in the array consists of the following fields: seat id, seat object path.</para>
+
+      <para><function>ListInhibitors()</function> lists all currently active inhibitors. It returns an array of
+      structures consisting of <varname>what</varname>, <varname>who</varname>, <varname>why</varname>,
+      <varname>mode</varname>, <varname>uid</varname> (user ID), and <varname>pid</varname> (process ID).</para>
+
+      <para><function>CreateSession()</function> and <function>ReleaseSession()</function> may be used to
+      open or close login sessions. These calls should <emphasis>never</emphasis> be invoked directly by
+      clients. Creating/closing sessions is exclusively the job of PAM and its
+      <citerefentry><refentrytitle>pam_systemd</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+      module.</para>
+
+      <para><function>ActivateSession()</function> brings the session with the specified ID into the
+      foreground. <function>ActivateSessionOnSeat()</function> does the same, but only if the seat id
+      matches.</para>
+
+      <para><function>LockSession()</function> asks the session with the specified ID to activate the screen
+      lock. <function>UnlockSession()</function> asks the session with the specified ID to remove an active
+      screen lock, if there is any. This is implemented by sending out the Lock() and Unlock() signals from
+      the respective session object which session managers are supposed to listen on.</para>
+
+      <para><function>LockSessions()</function> asks all sessions to activate their screen locks. This may be
+      used to lock access to the entire machine in one action. Similarly, <function>UnlockSessions()</function>
+      asks all sessions to deactivate their screen locks.</para>
+
+      <para><function>KillSession()</function> may be used to send a Unix signal to one or all processes of a
+      session. As arguments it takes the session id, either the string <literal>leader</literal> or
+      <literal>all</literal> and a signal number. If <literal>leader</literal> is passed only the session
+      <literal>leader</literal> is killed. If <literal>all</literal> is passed all processes of the session
+      are killed.</para>
+
+      <para><function>KillUser()</function> may be used to send a Unix signal to all processes of a user. As
+      arguments it takes the user id and a signal number.</para>
+
+      <para><function>TerminateSession()</function>, <function>TerminateUser()</function>,
+      <function>TerminateSeat()</function> may be used to forcibly terminate one specific session, all
+      processes of a user, and all sessions attached to a specific seat, respectively. The session, user,
+      and seat are identified by their respective IDs.</para>
+
+      <para><function>SetUserLinger()</function> enables or disables user lingering. If enabled, the runtime
+      directory of a user is kept around and they may continue to run processes while logged out. If
+      disabled, the runtime directory goes away as soon as they log out. <function>SetUserLinger()</function>
+      expects three arguments: the UID, a boolean whether to enable/disable and a boolean controlling the
+      <ulink url="https://www.freedesktop.org/software/polkit/docs/latest/">polkit</ulink>
+      authorization interactivity (see below). Note that the user linger state is persistently
+      stored on disk.</para>
+
+      <para><function>AttachDevice()</function> may be used to assign a specific device to a specific
+      seat. The device is identified by its <filename>/sys</filename> path and must be eligible for seat
+      assignments. <function>AttachDevice()</function> takes three arguments: the seat id, the sysfs path,
+      and a boolean for controlling polkit interactivity (see below). Device assignments are persistently
+      stored on disk. To create a new seat, simply specify a previously unused seat id. For more information
+      about the seat assignment logic see
+      <ulink url="https://www.freedesktop.org/wiki/Software/systemd/multiseat">Multi-Seat for Linux</ulink>.
+      </para>
+
+      <para><function>FlushDevices()</function> removes all explicit seat assignments for devices, resetting
+      all assignments to the automatic defaults. The only argument it takes is the polkit interactivity
+      boolean (see below).</para>
+
+      <para><function>PowerOff()</function>, <function>Reboot()</function>, <function>Halt()</function>,
+      <function>Suspend()</function>, and <function>Hibernate()</function> result in the system being powered
+      off, rebooted, halted (shut down without turning off power), suspended (the system state is
+      saved to RAM and the CPU is turned off), or hibernated (the system state is saved to disk and
+      the machine is powered down). <function>HybridSleep()</function> results in the system entering a
+      hybrid-sleep mode, i.e. the system is both hibernated and suspended.
+      <function>SuspendThenHibernate()</function> results in the system being suspended, then later woken
+      using an RTC timer and hibernated. The only argument is the polkit interactivity boolean
+      <varname>interactive</varname> (see below). The main purpose of these calls is that they enforce
+      polkit policy and hence allow powering off/rebooting/suspending/hibernating even by unprivileged
+      users. They also enforce inhibition locks. UIs should expose these calls as the primary mechanism to
+      poweroff/reboot/suspend/hibernate the machine.</para>
+
+      <para><function>SetRebootParameter()</function> sets a parameter for a subsequent reboot operation.
+      See the description of <command>reboot</command> in
+      <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry> and
+      <citerefentry project="man-pages"><refentrytitle>reboot</refentrytitle><manvolnum>2</manvolnum></citerefentry>
+      for more information.</para>
+
+      <para><function>SetRebootToFirmwareSetup()</function>,
+      <function>SetRebootToBootLoaderMenu()</function>, and <function>SetRebootToBootLoaderEntry()</function>
+      configure the action to be taken from the boot loader after a reboot: respectively entering firmware
+      setup mode, the boot loader menu, or a specific boot loader entry. See
+      <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry> for the
+      corresponding command line interface.</para>
+
+      <para><function>CanPowerOff()</function>, <function>CanReboot()</function>,
+      <function>CanHalt()</function>, <function>CanSuspend()</function>, <function>CanHibernate()</function>,
+      <function>CanHybridSleep()</function>, <function>CanSuspendThenHibernate()</function>,
+      <function>CanRebootParameter()</function>, <function>CanRebootToFirmwareSetup()</function>,
+      <function>CanRebootToBootLoaderMenu()</function>, and
+      <function>CanRebootToBootLoaderEntry()</function> test whether the system supports the respective
+      operation and whether the calling user is allowed to execute it. Returns one of <literal>na</literal>,
+      <literal>yes</literal>, <literal>no</literal>, and <literal>challenge</literal>. If
+      <literal>na</literal> is returned, the operation is not available because hardware, kernel, or drivers
+      do not support it. If <literal>yes</literal> is returned, the operation is supported and the user may
+      execute the operation without further authentication. If <literal>no</literal> is returned, the
+      operation is available but the user is not allowed to execute the operation. If
+      <literal>challenge</literal> is returned, the operation is available but only after
+      authorization.</para>
+
+      <para><function>ScheduleShutdown()</function> schedules a shutdown operation <varname>type</varname> at
+      time <varname>usec</varname> in microseconds since the UNIX epoch. <varname>type</varname> can be one
+      of <literal>poweroff</literal>, <literal>dry-poweroff</literal>, <literal>reboot</literal>,
+      <literal>dry-reboot</literal>, <literal>halt</literal>, and <literal>dry-halt</literal>. (The
+      <literal>dry-</literal> variants do not actually execute the shutdown action.)
+      <function>CancelScheduledShutdown()</function> cancels a scheduled shutdown. The output parameter
+      <varname>cancelled</varname> is true if a shutdown operation was scheduled.</para>
+
+      <para><function>SetWallMessage()</function> sets the wall message (the message that will be sent out to
+      all terminals and stored in a
+      <citerefentry project='man-pages'><refentrytitle>utmp</refentrytitle><manvolnum>5</manvolnum></citerefentry> record) for a
+      subsequent scheduled shutdown operation. The parameter <varname>wall_message</varname> specifies the
+      shutdown reason (and may be empty) which will be included in the shutdown message. The parameter
+      <varname>enable</varname> specifies whether to print a wall message on shutdown.</para>
+
+      <para><function>Inhibit()</function> creates an inhibition lock. It takes four parameters:
+      <varname>what</varname>, <varname>who</varname>, <varname>why</varname>, and
+      <varname>mode</varname>. <varname>what</varname> is one or more of <literal>shutdown</literal>,
+      <literal>sleep</literal>, <literal>idle</literal>, <literal>handle-power-key</literal>,
+      <literal>handle-suspend-key</literal>, <literal>handle-hibernate-key</literal>,
+      <literal>handle-lid-switch</literal>, separated by colons, for inhibiting poweroff/reboot,
+      suspend/hibernate, the automatic idle logic, or hardware key handling. <varname>who</varname> should be
+      a short human readable string identifying the application taking the lock. <varname>why</varname>
+      should be a short human readable string identifying the reason why the lock is taken. Finally,
+      <varname>mode</varname> is either <literal>block</literal> or <literal>delay</literal> which encodes
+      whether the inhibit shall be consider mandatory or whether it should just delay the operation to a
+      certain maximum time. The method returns a file descriptor. The lock is released the moment this file
+      descriptor and all its duplicates are closed. For more information on the inhibition logic see
+      <ulink url="http://www.freedesktop.org/wiki/Software/systemd/inhibit">Inhibitor Locks</ulink>.
+      </para>
+    </refsect2>
+
+    <refsect2>
+      <title>Signals</title>
+
+      <para>Whenever the inhibition state or idle hint changes, <function>PropertyChanged</function>
+      signals are sent out to which clients can subscribe.</para>
+
+      <para>The <function>SessionNew</function>, <function>SessionRemoved</function>,
+      <function>UserNew</function>, <function>UserRemoved</function>, <function>SeatNew</function>, and
+      <function>SeatRemoved</function> signals are sent each time a session is created or removed, a user
+      logs in or out, or a seat is added or removed. They each contain the ID of the object plus the object
+      path.</para>
+
+      <para>The <function>PrepareForShutdown()</function> and <function>PrepareForSleep()</function> signals
+      are sent right before (with the argument <literal>true</literal>) or after (with the argument
+      <literal>false</literal>) the system goes down for reboot/poweroff and suspend/hibernate,
+      respectively. This may be used by applications to save data on disk, release memory, or do other jobs
+      that should be done shortly before shutdown/sleep, in conjunction with delay inhibitor locks. After
+      completion of this work they should release their inhibition locks in order to not delay the operation
+      any further. For more information see
+      <ulink url="http://www.freedesktop.org/wiki/Software/systemd/inhibit">Inhibitor Locks</ulink>.
+      </para>
+    </refsect2>
+
+    <refsect2>
+      <title>Properties</title>
+
+      <para>Most properties simply reflect the configuration, see
+      <citerefentry><refentrytitle>logind.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>. This
+      includes: <varname>NAutoVTs</varname>, <varname>KillOnlyUsers</varname>,
+      <varname>KillExcludeUsers</varname>, <varname>KillUserProcesses</varname>, <varname>IdleAction</varname>,
+      <varname>InhibitDelayMaxUSec</varname>,
+      <varname>InhibitorsMax</varname>,
+      <varname>UserStopDelayUSec</varname>,
+      <varname>HandlePowerKey</varname>, <varname>HandleSuspendKey</varname>,
+      <varname>HandleHibernateKey</varname>, <varname>HandleLidSwitch</varname>,
+      <varname>HandleLidSwitchExternalPower</varname>, <varname>HandleLidSwitchDocked</varname>,
+      <varname>IdleActionUSec</varname>, <varname>HoldoffTimeoutUSec</varname>,
+      <varname>RemoveIPC</varname>, <varname>RuntimeDirectorySize</varname>,
+      <varname>RuntimeDirectoryInodesMax</varname>, <varname>InhibitorsMax</varname>, and
+      <varname>SessionsMax</varname>.
+      </para>
+
+      <para>The <varname>IdleHint</varname> property reflects the idle hint state of the system. If the
+      system is idle it might get into automatic suspend or shutdown depending on the configuration.</para>
+
+      <para><varname>IdleSinceHint</varname> and <varname>IdleSinceHintMonotonic</varname> encode the
+      timestamps of the last change of the idle hint boolean, in <constant>CLOCK_REALTIME</constant> and
+      <constant>CLOCK_MONOTONIC</constant> timestamps, respectively, in microseconds since the epoch.</para>
+
+      <para>The <varname>BlockInhibited</varname> and <varname>DelayInhibited</varname> properties encode
+      the currently active locks of the respective modes. They are colon separated lists of
+      <literal>shutdown</literal>, <literal>sleep</literal>, and <literal>idle</literal> (see above).</para>
+
+      <para><varname>NCurrentSessions</varname> and <varname>NCurrentInhibitors</varname> contain the number
+      of currently registered sessions and inhibitors.</para>
+
+      <para>The <varname>BootLoaderEntries</varname> property contains a list of boot loader entries.
+      This includes boot loader entries defined in configuration and any additional loader entries
+      reported by the boot loader. See
+      <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+      for more information.</para>
+
+      <para>The <varname>PreparingForShutdown</varname> and <varname>PreparingForSleep</varname> boolean
+      properties are true during the interval between the two <function>PrepareForShutdown</function> and
+      <function>PrepareForSleep</function> signals respectively. Note that these properties do not
+      send out <function>PropertyChanged</function> signals.</para>
+
+      <para>The <varname>RebootParameter</varname> property shows the value set with the
+      <function>SetRebootParameter()</function> method described above.</para>
+
+      <para><varname>ScheduledShutdown</varname> shows the value pair set with the
+      <function>ScheduleShutdown()</function> method described above.</para>
+
+      <para><varname>RebootToFirmwareSetup</varname>, <varname>RebootToBootLoaderMenu</varname>, and
+      <varname>RebootToBootLoaderEntry</varname> are true when the resprective post-reboot operation was
+      selected with <function>SetRebootToFirmwareSetup</function>,
+      <function>SetRebootToBootLoaderMenu</function>, or
+      <function>SetRebootToBootLoaderEntry</function>.</para>
+
+      <para>The <varname>WallMessage</varname> and <varname>EnableWallMessages</varname> properties reflect the
+      shutdown reason and wall message enablement switch which can be set with the
+      <function>SetWallMessage()</function> method described above.</para>
+
+      <para><varname>Docked</varname> is true if the machine is connected to a dock.
+      <varname>LidClosed</varname> is true when the lid (of a laptop) is closed.
+      <varname>OnExternalPower</varname> is true when the machine is connected to an external power supply.
+      </para>
+    </refsect2>
+
+    <refsect2>
+      <title>Security</title>
+
+      <para>A number of operations are protected via the polkit privilege
+      system. <function>SetUserLinger()</function> requires the
+      <interfacename>org.freedesktop.login1.set-user-linger</interfacename>
+      privilege. <function>AttachDevice()</function> requires
+      <interfacename>org.freedesktop.login1.attach-device</interfacename> and
+      <function>FlushDevices()</function> requires
+      <interfacename>org.freedesktop.login1.flush-devices</interfacename>. <function>PowerOff()</function>,
+      <function>Reboot()</function>, <function>Halt()</function>, <function>Suspend()</function>,
+      <function>Hibernate()</function> require
+      <interfacename>org.freedesktop.login1.power-off</interfacename>,
+      <interfacename>org.freedesktop.login1.power-off-multiple-sessions</interfacename>,
+      <interfacename>org.freedesktop.login1.power-off-ignore-inhibit</interfacename>,
+      <interfacename>org.freedesktop.login1.reboot</interfacename>,
+      <interfacename>org.freedesktop.login1.reboot-multiple-sessions</interfacename>,
+      <interfacename>org.freedesktop.login1.reboot-ignore-inhibit</interfacename>,
+      <interfacename>org.freedesktop.login1.halt</interfacename>,
+      <interfacename>org.freedesktop.login1.halt-multiple-sessions</interfacename>,
+      <interfacename>org.freedesktop.login1.halt-ignore-inhibit</interfacename>,
+      <interfacename>org.freedesktop.login1.suspend</interfacename>,
+      <interfacename>org.freedesktop.login1.suspend-multiple-sessions</interfacename>,
+      <interfacename>org.freedesktop.login1.suspend-ignore-inhibit</interfacename>,
+      <interfacename>org.freedesktop.login1.hibernate</interfacename>,
+      <interfacename>org.freedesktop.login1.hibernate-multiple-sessions</interfacename>,
+      <interfacename>org.freedesktop.login1.hibernate-ignore-inhibit</interfacename>,
+      respectively depending on whether there are other sessions around or active inhibits are present.
+      <function>HybridSleep()</function> and <function>SuspendThenHibernate()</function>
+      use the same privileges as <function>Hibernate()</function>.
+      <function>SetRebootParameter()</function> requires
+      <interfacename>org.freedesktop.login1.set-reboot-parameter</interfacename>.</para>
+
+      <para><function>SetRebootToFirmwareSetup</function> requires
+      <interfacename>org.freedesktop.login1.set-reboot-to-firmware-setup</interfacename>.
+      <function>SetRebootToBootLoaderMenu</function> requires
+      <interfacename>org.freedesktop.login1.set-reboot-to-boot-loader-menu</interfacename>.
+      <function>SetRebootToBootLoaderEntry</function> requires
+      <interfacename>org.freedesktop.login1.set-reboot-to-boot-loader-entry</interfacename>.
+      </para>
+
+      <para><function>ScheduleShutdown</function> and <function>CancelScheduledShutdown</function> require
+      the same privileges (listed above) as the immediate poweroff/reboot/halt operations.</para>
+
+      <para><function>Inhibit()</function> is protected via one of
+      <interfacename>org.freedesktop.login1.inhibit-block-shutdown</interfacename>,
+      <interfacename>org.freedesktop.login1.inhibit-delay-shutdown</interfacename>,
+      <interfacename>org.freedesktop.login1.inhibit-block-sleep</interfacename>,
+      <interfacename>org.freedesktop.login1.inhibit-delay-sleep</interfacename>,
+      <interfacename>org.freedesktop.login1.inhibit-block-idle</interfacename>,
+      <interfacename>org.freedesktop.login1.inhibit-handle-power-key</interfacename>,
+      <interfacename>org.freedesktop.login1.inhibit-handle-suspend-key</interfacename>,
+      <interfacename>org.freedesktop.login1.inhibit-handle-hibernate-key</interfacename>,
+      <interfacename>org.freedesktop.login1.inhibit-handle-lid-switch</interfacename> depending on the lock
+      type and mode taken.</para>
+
+      <para>The <varname>interactive</varname> boolean parameters can be used to control whether polkit
+      should interactively ask the user for authentication credentials if required.</para>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>Seat Objects</title>
+
+    <programlisting executable="systemd-logind" node="/org/freedesktop/login1/seat/seat0" interface="org.freedesktop.login1.Seat">
+node /org/freedesktop/login1/seat/seat0 {
+  interface org.freedesktop.login1.Seat {
+    methods:
+      Terminate();
+      ActivateSession(in  s session_id);
+      SwitchTo(in  u vtnr);
+      SwitchToNext();
+      SwitchToPrevious();
+    properties:
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s Id = '...';
+      readonly (so) ActiveSession = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b CanTTY = ...;
+      readonly b CanGraphical = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(so) Sessions = [...];
+      readonly b IdleHint = ...;
+      readonly t IdleSinceHint = ...;
+      readonly t IdleSinceHintMonotonic = ...;
+  };
+  interface org.freedesktop.DBus.Peer { ... };
+  interface org.freedesktop.DBus.Introspectable { ... };
+  interface org.freedesktop.DBus.Properties { ... };
+};
+    </programlisting>
+
+    <!--Autogenerated cross-references for systemd.directives, do not edit-->
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.login1.Seat"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.login1.Seat"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Terminate()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ActivateSession()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SwitchTo()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SwitchToNext()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SwitchToPrevious()"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Id"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ActiveSession"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CanTTY"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CanGraphical"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Sessions"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IdleHint"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IdleSinceHint"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IdleSinceHintMonotonic"/>
+
+    <!--End of Autogenerated section-->
+
+    <refsect2>
+      <title>Methods</title>
+
+      <para><function>Terminate()</function> and <function>ActivateSession()</function> work similar to
+      TerminateSeat(), ActivationSessionOnSeat() on the Manager object.</para>
+
+      <para><function>SwitchTo()</function> switches to the session on the virtual terminal
+      <varname>vtnr</varname>. <function>SwitchToNext()</function> and
+      <function>SwitchToPrevious()</function> switch to, respectively, the next and previous sessions on the
+      seat in the order of virtual terminals. If there is no active session, they switch to, respectively,
+      the first and last session on the seat.</para>
+    </refsect2>
+
+    <refsect2>
+      <title>Signals</title>
+
+      <para>Whenever <function>ActiveSession</function>, <function>Sessions</function>,
+      <function>CanGraphical</function>, <function>CanTTY</function>,
+      or the idle state changes, <function>PropertyChanged</function> signals are sent out to which clients
+      can subscribe.</para>
+    </refsect2>
+
+    <refsect2>
+      <title>Properties</title>
+
+      <para>The <varname>Id</varname> property encodes the ID of the seat.</para>
+
+      <para><varname>ActiveSession</varname> encodes the currently active session if there is one. It is a
+      structure consisting of the session id and the object path.</para>
+
+      <para><varname>CanTTY</varname> encodes whether the session is suitable for text logins, and
+      <varname>CanGraphical</varname> whether it is suitable for graphical sessions.</para>
+
+      <para>The <varname>Sessions</varname> property is an array of all current sessions of this seat, each
+      encoded in a structure consisting of the ID and the object path.</para>
+
+      <para>The <varname>IdleHint</varname>, <varname>IdleSinceHint</varname>, and
+      <varname>IdleSinceHintMonotonic</varname> properties encode the idle state, similar to the ones exposed
+      on the <interfacename>Manager</interfacename> object, but specific for this seat.</para>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>User Objects</title>
+
+    <programlisting executable="systemd-logind" node="/org/freedesktop/login1/user/_1000" interface="org.freedesktop.login1.User">
+node /org/freedesktop/login1/user/_1000 {
+  interface org.freedesktop.login1.User {
+    methods:
+      Terminate();
+      Kill(in  i signal_number);
+    properties:
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u UID = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u GID = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s Name = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t Timestamp = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t TimestampMonotonic = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s RuntimePath = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s Service = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s Slice = '...';
+      readonly (so) Display = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly s State = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(so) Sessions = [...];
+      readonly b IdleHint = ...;
+      readonly t IdleSinceHint = ...;
+      readonly t IdleSinceHintMonotonic = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b Linger = ...;
+  };
+  interface org.freedesktop.DBus.Peer { ... };
+  interface org.freedesktop.DBus.Introspectable { ... };
+  interface org.freedesktop.DBus.Properties { ... };
+};
+    </programlisting>
+
+    <!--Autogenerated cross-references for systemd.directives, do not edit-->
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.login1.User"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.login1.User"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Terminate()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Kill()"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="UID"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="GID"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Name"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Timestamp"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TimestampMonotonic"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RuntimePath"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Service"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Slice"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Display"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="State"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Sessions"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IdleHint"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IdleSinceHint"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IdleSinceHintMonotonic"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Linger"/>
+
+    <!--End of Autogenerated section-->
+
+    <refsect2>
+      <title>Methods</title>
+
+      <para><function>Terminate()</function> and <function>Kill()</function> work similar to the
+      <function>TerminateUser()</function> and <function>KillUser()</function> methods on the manager
+      object.</para>
+    </refsect2>
+
+    <refsect2>
+      <title>Signals</title>
+
+      <para>Whenever <varname>Sessions</varname> or the idle state changes,
+      <function>PropertyChanged</function> signals are sent out to which clients can subscribe.</para>
+    </refsect2>
+
+    <refsect2>
+      <title>Properties</title>
+
+      <para>The <varname>UID</varname> and <varname>GID</varname> properties encode the Unix UID and primary
+      GID of the user.</para>
+
+      <para>The <varname>Name</varname> property encodes the user name.</para>
+
+      <para><varname>Timestamp</varname> and <varname>TimestampMonotonic</varname> encode the login time of
+      the user in microseconds since the epoch, in the <constant>CLOCK_REALTIME</constant> and
+      <constant>CLOCK_MONOTONIC</constant> clocks, respectively.</para>
+
+      <para><varname>RuntimePath</varname> encodes the runtime path of the user,
+      i.e. <varname>$XDG_RUNTIME_DIR</varname>. For details see the
+      <ulink url="https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html">
+        XDG Basedir Specification
+      </ulink>.</para>
+
+      <para><varname>Service</varname> contains the unit name of the user systemd service of this
+      user. Each logged in user is assigned a user service that runs a user systemd instance. This is
+      usually an instance of <filename>user@.service</filename>.</para>
+
+      <para><varname>Slice</varname> contains the unit name of the user systemd slice of this user. Each
+      logged in user gets a private slice.</para>
+
+      <para><varname>Display</varname> encodes which graphical session should be used as the primary UI display
+      for the user. It is a structure encoding the session ID and the object path of the session to use.</para>
+
+      <para><varname>State</varname> encodes the user state and is one of <literal>offline</literal>,
+      <literal>lingering</literal>, <literal>online</literal>, <literal>active</literal>, or
+      <literal>closing</literal>. See
+      <citerefentry><refentrytitle>sd_uid_get_state</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+      for more information about the states.</para>
+
+      <para><varname>Sessions</varname> is an array of structures encoding all current sessions of the
+      user. Each structure consists of the ID and object path.</para>
+
+      <para>The <varname>IdleHint</varname>, <varname>IdleSinceHint</varname>, and
+      <varname>IdleSinceHintMonotonic</varname> properties encode the idle hint state of the user, similar to
+      the <interfacename>Manager</interfacename>'s properties, but specific for this user.</para>
+
+      <para>The <varname>Linger</varname> property shows whether lingering is enabled for this user.</para>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>Session Objects</title>
+
+    <programlisting executable="systemd-logind" node="/org/freedesktop/login1/session/1" interface="org.freedesktop.login1.Session">
+node /org/freedesktop/login1/session/1 {
+  interface org.freedesktop.login1.Session {
+    methods:
+      Terminate();
+      Activate();
+      Lock();
+      Unlock();
+      SetIdleHint(in  b idle);
+      SetLockedHint(in  b locked);
+      Kill(in  s who,
+           in  i signal_number);
+      TakeControl(in  b force);
+      ReleaseControl();
+      SetType(in  s type);
+      TakeDevice(in  u major,
+                 in  u minor,
+                 out h fd,
+                 out b inactive);
+      ReleaseDevice(in  u major,
+                    in  u minor);
+      PauseDeviceComplete(in  u major,
+                          in  u minor);
+      SetBrightness(in  s subsystem,
+                    in  s name,
+                    in  u brightness);
+    signals:
+      PauseDevice(u major,
+                  u minor,
+                  s type);
+      ResumeDevice(u major,
+                   u minor,
+                   h fd);
+      Lock();
+      Unlock();
+    properties:
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s Id = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly (uo) User = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s Name = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t Timestamp = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t TimestampMonotonic = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u VTNr = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly (so) Seat = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s TTY = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s Display = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b Remote = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s RemoteHost = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s RemoteUser = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s Service = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s Desktop = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s Scope = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u Leader = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u Audit = ...;
+      readonly s Type = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s Class = '...';
+      readonly b Active = ...;
+      readonly s State = '...';
+      readonly b IdleHint = ...;
+      readonly t IdleSinceHint = ...;
+      readonly t IdleSinceHintMonotonic = ...;
+      readonly b LockedHint = ...;
+  };
+  interface org.freedesktop.DBus.Peer { ... };
+  interface org.freedesktop.DBus.Introspectable { ... };
+  interface org.freedesktop.DBus.Properties { ... };
+};
+    </programlisting>
+
+    <!--Autogenerated cross-references for systemd.directives, do not edit-->
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.login1.Session"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.login1.Session"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Terminate()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Activate()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Lock()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Unlock()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetIdleHint()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetLockedHint()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Kill()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="TakeControl()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ReleaseControl()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetType()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="TakeDevice()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ReleaseDevice()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="PauseDeviceComplete()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetBrightness()"/>
+
+    <variablelist class="dbus-signal" generated="True" extra-ref="PauseDevice"/>
+
+    <variablelist class="dbus-signal" generated="True" extra-ref="ResumeDevice"/>
+
+    <variablelist class="dbus-signal" generated="True" extra-ref="Lock"/>
+
+    <variablelist class="dbus-signal" generated="True" extra-ref="Unlock"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Id"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="User"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Name"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Timestamp"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TimestampMonotonic"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="VTNr"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Seat"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TTY"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Display"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Remote"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RemoteHost"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RemoteUser"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Service"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Desktop"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Scope"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Leader"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Audit"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Type"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Class"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Active"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="State"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IdleHint"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IdleSinceHint"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IdleSinceHintMonotonic"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LockedHint"/>
+
+    <!--End of Autogenerated section-->
+
+    <refsect2>
+      <title>Methods</title>
+
+      <para><function>Terminate()</function>, <function>Activate()</function>, <function>Lock()</function>,
+      <function>Unlock()</function>, and <function>Kill()</function> work similarly to the respective calls on
+      the <interfacename>Manager</interfacename> object.</para>
+
+      <para><function>SetIdleHint()</function> is called by the session object to update the idle state
+      of the session whenever it changes.</para>
+
+      <para><function>TakeControl()</function> allows a process to take exclusive managed device
+      access-control for that session. Only one D-Bus connection can be a controller for a given session at any
+      time. If the <varname>force</varname> argument is set (root only), an existing controller is kicked
+      out and replaced. Otherwise, this method fails if there is already a controller. Note that this method is
+      limited to D-Bus users with the effective UID set to the user of the session or root.</para>
+
+      <para><function>ReleaseControl()</function> drops control of a given session. Closing the D-Bus
+      connection implicitly releases control as well. See <function>TakeControl()</function> for more
+      information. This method also releases all devices for which the controller requested ownership via
+      <function>TakeDevice()</function>.</para>
+
+      <para><function>SetType()</function> allows the type of the session to be changed dynamically.  It can
+      only be called by session's current controller. If <function>TakeControl()</function> has not been
+      called, this method will fail. In addition, the session type will be reset to its original value once
+      control is released, either by calling <function>ReleaseControl()</function> or closing the D-Bus
+      connection. This should help prevent a session from entering an inconsistent state, for example if the
+      controller crashes. The only argument <varname>type</varname> is the new session type.</para>
+
+      <para><function>TakeDevice()</function> allows a session controller to get a file descriptor for a
+      specific device. Pass in the major and minor numbers of the character device and
+      <filename>systemd-logind</filename> will return a file descriptor for the device. Only a limited set of
+      device-types is currently supported (but may be extended). <filename>systemd-logind</filename>
+      automatically mutes the file descriptor if the session is inactive and resumes it once the session is
+      activated again. This guarantees that a session can only access session devices if the session is
+      active. Note that this revoke/resume mechanism is asynchronous and may happen at any given time.  This
+      only works on devices that are attached to the seat of the given session. A process is not required to
+      have direct access to the device node. <filename>systemd-logind</filename> only requires you to be the
+      active session controller (see <function>TakeControl()</function>). Also note that any device can only
+      be requested once. As long as you don't release it, further <function>TakeDevice()</function> calls
+      will fail.</para>
+
+      <para><function>ReleaseDevice()</function> releases a device again (see
+      <function>TakeDevice()</function>). This is also implicitly done by
+      <function>ReleaseControl()</function> or when closing the D-Bus connection.</para>
+
+      <para><function>PauseDeviceComplete()</function> allows a session controller to synchronously pause a
+      device after receiving a <function>PauseDevice(<literal>pause</literal>)</function> signal. Forced
+      signals (or after an internal timeout) are automatically completed by
+      <filename>systemd-logind</filename> asynchronously.</para>
+
+      <para><function>SetLockedHint()</function> may be used to set the "idle hint" to
+      <varname>locked</varname>, i.e. information whether the session is locked. This is intended to be used
+      by the desktop environment to tell <command>systemd-logind</command> when the session is locked and
+      unlocked.</para>
+
+      <para><function>SetBrightness()</function> may be used to set the display brightness. This is intended
+      to be used by the desktop environment and allows unprivileged programs to access hardware settings in
+      a controlled way. The <varname>subsystem</varname> parameter specifies a kernel subsystem, either
+      <literal>backlight</literal> or <literal>leds</literal>. The <varname>name</varname> parameter
+      specifies a device name under the specified subsystem. The <varname>brightness</varname> parameter
+      specifies the brightness. The range is defined by individual drivers, see
+      <filename>/sys/class/<varname>subsystem</varname>/<varname>name</varname>/max_brightness</filename>.
+      </para>
+    </refsect2>
+
+    <refsect2>
+      <title>Signals</title>
+
+      <para>The active session controller exclusively gets <function>PauseDevice</function> and
+      <function>ResumeDevice</function> events for any device it requested via
+      <function>TakeDevice()</function>. They notify the controller whenever a device is paused or resumed. A
+      device is never resumed if its session is inactive. Also note that <function>PauseDevice</function>
+      signals are sent before the <function>PropertyChanged</function> signal for the
+      <function>Active</function> state. The inverse is true for <function>ResumeDevice</function>. A device
+      may remain paused for unknown reasons even though the <interfacename>Session</interfacename> is active.
+      </para>
+
+      <para>A <function>PauseDevice</function> signal carries the major and minor numbers and a string describing the
+      type as arguments. <function>force</function> means the device was already paused by
+      <filename>systemd-logind</filename> and the signal is only an asynchronous
+      notification. <function>pause</function> means <filename>systemd-logind</filename> grants you a limited amount of time to pause the device. You must respond to this via
+      <function>PauseDeviceComplete()</function>. This synchronous pausing mechanism is used for
+      backwards-compatibility to VTs and <filename>systemd-logind</filename> is free to not make use of
+      it. It is also free to send a forced <function>PauseDevice</function> if you don't respond in a timely
+      manner (or for any other reason). <function>gone</function> means the device was unplugged from the
+      system and you will no longer get any notifications about it. There is no need to call
+      <function>ReleaseDevice()</function>. You may call <function>TakeDevice()</function> again if a new
+      device is assigned the major+minor combination.</para>
+
+      <para><function>ResumeDevice</function> is sent whenever a session is active and a device is
+      resumed. It carries the major/minor numbers as arguments and provides a new open file descriptor. You should
+      switch to the new descriptor and close the old one. They are not guaranteed to have the same underlying
+      open file descriptor in the kernel (except for a limited set of device types).</para>
+
+      <para>Whenever <function>Active</function> or the idle state changes,
+      <function>PropertyChanged</function> signals are sent out to which clients can subscribe.</para>
+
+      <para><function>Lock</function>/<function>Unlock</function> is sent when the session is asked to be
+      screen-locked/unlocked. A session manager of the session should listen to this signal and act
+      accordingly. This signal is sent out as a result of the <function>Lock()</function> and
+      <function>Unlock()</function> methods, respectively.</para>
+    </refsect2>
+
+    <refsect2>
+      <title>Properties</title>
+
+      <para><varname>Id</varname> encodes the session ID.</para>
+
+      <para><varname>User</varname> encodes the user ID of the user this session belongs to. This is a
+      structure consisting of the Unix UID and the object path.</para>
+
+      <para><varname>Name</varname> encodes the user name.</para>
+
+      <para><varname>Timestamp</varname> and <varname>TimestampMonotonic</varname> encode the microseconds
+      since the epoch when the session was created, in <constant>CLOCK_REALTIME</constant> or
+      <constant>CLOCK_MONOTONIC</constant>, respectively.</para>
+
+      <para><varname>VTNr</varname> encodes the virtual terminal number of the session if there is any, 0
+      otherwise.</para>
+
+      <para><varname>Seat</varname> encodes the seat this session belongs to if there is any. This is a
+      structure consisting of the ID and the seat object path.</para>
+
+      <para><varname>TTY</varname> encodes the kernel TTY path of the session if this is a text login. If not
+      this is an empty string.</para>
+
+      <para><varname>Display</varname> encodes the X11 display name if this is a graphical login. If not,
+      this is an empty string.</para>
+
+      <para><varname>Remote</varname> encodes whether the session is local or remote.</para>
+
+      <para><varname>RemoteHost</varname> and <varname>RemoteUser</varname> encode the remote host and user
+      if this is a remote session, or an empty string otherwise.</para>
+
+      <para><varname>Service</varname> encodes the PAM service name that registered the session.</para>
+
+      <para><varname>Desktop</varname> describes the desktop environment running in the session (if
+      known).</para>
+
+      <para><varname>Scope</varname> contains the systemd scope unit name of this session.</para>
+
+      <para><varname>Leader</varname> encodes the PID of the process that registered the session.</para>
+
+      <para><varname>Audit</varname> encodes the Kernel Audit session ID of the session if auditing is
+      available.</para>
+
+      <para><varname>Type</varname> encodes the session type. It's one of <literal>unspecified</literal> (for
+      cron PAM sessions and suchlike), <literal>tty</literal> (for text logins) or
+      <literal>x11</literal>/<literal>mir</literal>/<literal>wayland</literal> (for graphical logins).</para>
+
+      <para><varname>Class</varname> encodes the session class. It's one of <literal>user</literal> (for
+      normal user sessions), <literal>greeter</literal> (for display manager pseudo-sessions), or
+      <literal>lock-screen</literal> (for display lock screens).</para>
+
+      <para><varname>Active</varname> is a boolean that is true if the session is active, i.e. currently in the
+      foreground. This field is semi-redundant due to <varname>State</varname>.</para>
+
+      <para><varname>State</varname> encodes the session state and one of <literal>online</literal>,
+      <literal>active</literal>, or <literal>closing</literal>. See
+      <citerefentry><refentrytitle>sd_session_get_state</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+      for more information about the states.</para>
+
+      <para><varname>IdleHint</varname>, <varname>IdleSinceHint</varname>, and
+      <varname>IdleSinceHintMonotonic</varname> encapsulate the idle hint state of this session, similarly to
+      how the respective properties on the manager object do it for the whole system.</para>
+
+      <para><varname>LockedHint</varname> shows the locked hint state of this session, as set by the
+      <function>SetLockedHint()</function> method described above.</para>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>Examples</title>
+
+    <example>
+      <title>Introspect <interfacename>org.freedesktop.login1.Manager</interfacename> on the bus</title>
+
+      <programlisting>$ gdbus introspect --system --dest org.freedesktop.login1 \
+  --object-path /org/freedesktop/login1
+      </programlisting>
+    </example>
+
+    <example>
+      <title>Introspect <interfacename>org.freedesktop.login1.Seat</interfacename> on the bus</title>
+
+      <programlisting>$ gdbus introspect --system --dest org.freedesktop.login1 \
+ --object-path /org/freedesktop/login1/seat/seat0
+      </programlisting>
+    </example>
+
+    <example>
+      <title>Introspect <interfacename>org.freedesktop.login1.User</interfacename> on the bus</title>
+
+      <programlisting>$ gdbus introspect --system --dest org.freedesktop.login1 \
+  --object-path /org/freedesktop/login1/user/_1000
+      </programlisting>
+    </example>
+
+    <example>
+      <title>Introspect <interfacename>org.freedesktop.login1.Session</interfacename> on the bus</title>
+
+      <programlisting>$ gdbus introspect --system --dest org.freedesktop.login1 \
+  --object-path /org/freedesktop/login1/session/45
+      </programlisting>
+    </example>
+  </refsect1>
+
+  <refsect1>
+    <title>Versioning</title>
+
+    <para>These D-Bus interfaces follow <ulink url="http://0pointer.de/blog/projects/versioning-dbus.html">
+    the usual interface versioning guidelines</ulink>.</para>
+  </refsect1>
+</refentry>
diff --git a/man/org.freedesktop.machine1.xml b/man/org.freedesktop.machine1.xml
new file mode 100644 (file)
index 0000000..a54fa04
--- /dev/null
@@ -0,0 +1,643 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" >
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="org.freedesktop.machine1" conditional='ENABLE_MACHINED'
+    xmlns:xi="http://www.w3.org/2001/XInclude">
+  <refentryinfo>
+    <title>org.freedesktop.machine1</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>org.freedesktop.machine1</refentrytitle>
+    <manvolnum>5</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>org.freedesktop.machine1</refname>
+    <refpurpose>The D-Bus interface of systemd-machined</refpurpose>
+  </refnamediv>
+
+  <refsect1>
+    <title>Introduction</title>
+
+    <para>
+    <citerefentry><refentrytitle>systemd-machined.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    is a system service that keeps track of locally running virtual machines and containers.
+    This page describes the D-Bus interface.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>The Manager Object</title>
+
+    <para>The service exposes the following interfaces on the Manager object on the bus:</para>
+
+    <programlisting executable="systemd-machined" node="/org/freedesktop/machine1" interface="org.freedesktop.machine1.Manager">
+node /org/freedesktop/machine1 {
+  interface org.freedesktop.machine1.Manager {
+    methods:
+      GetMachine(in  s name,
+                 out o machine);
+      GetImage(in  s name,
+               out o image);
+      GetMachineByPID(in  u pid,
+                      out o machine);
+      ListMachines(out a(ssso) machines);
+      ListImages(out a(ssbttto) images);
+      CreateMachine(in  s name,
+                    in  ay id,
+                    in  s service,
+                    in  s class,
+                    in  u leader,
+                    in  s root_directory,
+                    in  a(sv) scope_properties,
+                    out o path);
+      CreateMachineWithNetwork(in  s name,
+                               in  ay id,
+                               in  s service,
+                               in  s class,
+                               in  u leader,
+                               in  s root_directory,
+                               in  ai ifindices,
+                               in  a(sv) scope_properties,
+                               out o path);
+      RegisterMachine(in  s name,
+                      in  ay id,
+                      in  s service,
+                      in  s class,
+                      in  u leader,
+                      in  s root_directory,
+                      out o path);
+      RegisterMachineWithNetwork(in  s name,
+                                 in  ay id,
+                                 in  s service,
+                                 in  s class,
+                                 in  u leader,
+                                 in  s root_directory,
+                                 in  ai ifindices,
+                                 out o path);
+      UnregisterMachine(in  s name);
+      TerminateMachine(in  s id);
+      KillMachine(in  s name,
+                  in  s who,
+                  in  i signal);
+      GetMachineAddresses(in  s name,
+                          out a(iay) addresses);
+      GetMachineOSRelease(in  s name,
+                          out a{ss} fields);
+      OpenMachinePTY(in  s name,
+                     out h pty,
+                     out s pty_path);
+      OpenMachineLogin(in  s name,
+                       out h pty,
+                       out s pty_path);
+      OpenMachineShell(in  s name,
+                       in  s user,
+                       in  s path,
+                       in  as args,
+                       in  as environment,
+                       out h pty,
+                       out s pty_path);
+      BindMountMachine(in  s name,
+                       in  s source,
+                       in  s destination,
+                       in  b read_only,
+                       in  b mkdir);
+      CopyFromMachine(in  s name,
+                      in  s source,
+                      in  s destination);
+      CopyToMachine(in  s name,
+                    in  s source,
+                    in  s destination);
+      OpenMachineRootDirectory(in  s name,
+                               out h fd);
+      GetMachineUIDShift(in  s name,
+                         out u shift);
+      RemoveImage(in  s name);
+      RenameImage(in  s name,
+                  in  s new_name);
+      CloneImage(in  s name,
+                 in  s new_name,
+                 in  b read_only);
+      MarkImageReadOnly(in  s name,
+                        in  b read_only);
+      GetImageHostname(in  s name,
+                       out s hostname);
+      GetImageMachineID(in  s name,
+                        out ay id);
+      GetImageMachineInfo(in  s name,
+                          out a{ss} machine_info);
+      GetImageOSRelease(in  s name,
+                        out a{ss} os_release);
+      SetPoolLimit(in  t size);
+      SetImageLimit(in  s name,
+                    in  t size);
+      CleanPool(in  s mode,
+                out a(st) images);
+      MapFromMachineUser(in  s name,
+                         in  u uid_inner,
+                         out u uid_outer);
+      MapToMachineUser(in  u uid_outer,
+                       out s machine_name,
+                       out o machine_path,
+                       out u uid_inner);
+      MapFromMachineGroup(in  s name,
+                          in  u gid_inner,
+                          out u gid_outer);
+      MapToMachineGroup(in  u gid_outer,
+                        out s machine_name,
+                        out o machine_path,
+                        out u gid_inner);
+    signals:
+      MachineNew(s machine,
+                 o path);
+      MachineRemoved(s machine,
+                     o path);
+    properties:
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly s PoolPath = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t PoolUsage = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t PoolLimit = ...;
+  };
+  interface org.freedesktop.DBus.Peer { ... };
+  interface org.freedesktop.DBus.Introspectable { ... };
+  interface org.freedesktop.DBus.Properties { ... };
+};
+    </programlisting>
+
+    <!--method UnregisterMachine is not documented!-->
+
+    <!--method OpenMachineRootDirectory is not documented!-->
+
+    <!--method GetMachineUIDShift is not documented!-->
+
+    <!--method GetImageHostname is not documented!-->
+
+    <!--method GetImageMachineID is not documented!-->
+
+    <!--method GetImageMachineInfo is not documented!-->
+
+    <!--method GetImageOSRelease is not documented!-->
+
+    <!--method CleanPool is not documented!-->
+
+    <!--Autogenerated cross-references for systemd.directives, do not edit-->
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.machine1.Manager"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.machine1.Manager"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetMachine()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetImage()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetMachineByPID()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ListMachines()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ListImages()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="CreateMachine()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="CreateMachineWithNetwork()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="RegisterMachine()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="RegisterMachineWithNetwork()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="UnregisterMachine()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="TerminateMachine()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="KillMachine()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetMachineAddresses()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetMachineOSRelease()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="OpenMachinePTY()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="OpenMachineLogin()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="OpenMachineShell()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="BindMountMachine()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="CopyFromMachine()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="CopyToMachine()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="OpenMachineRootDirectory()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetMachineUIDShift()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="RemoveImage()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="RenameImage()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="CloneImage()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="MarkImageReadOnly()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetImageHostname()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetImageMachineID()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetImageMachineInfo()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetImageOSRelease()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetPoolLimit()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetImageLimit()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="CleanPool()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="MapFromMachineUser()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="MapToMachineUser()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="MapFromMachineGroup()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="MapToMachineGroup()"/>
+
+    <variablelist class="dbus-signal" generated="True" extra-ref="MachineNew"/>
+
+    <variablelist class="dbus-signal" generated="True" extra-ref="MachineRemoved"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="PoolPath"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="PoolUsage"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="PoolLimit"/>
+
+    <!--End of Autogenerated section-->
+
+    <refsect2>
+      <title>Methods</title>
+
+      <para><function>GetMachine()</function> may be used to get the machine object path for the machine with
+      the specified name. Similarly, <function>GetMachineByPID()</function> gets the machine object the
+      specified PID belongs to if there is any.</para>
+
+      <para><function>GetImage()</function> may be used to get the image object path of the image with the
+      specified name.</para>
+
+      <para><function>ListMachines()</function> returns an array of all currently registered machines. The
+      structures in the array consist of the following fields: machine name, machine class, an identifier for
+      the service that registered the machine and the machine object path.</para>
+
+      <para><function>ListImages()</function> returns an array of all currently known images. The
+      structures in the array consist of the following fields: image name, type, read-only flag, creation
+      time, modification time, current disk space, and image object path.</para>
+
+      <para><function>CreateMachine()</function> may be used to register a new virtual machine or container
+      with <command>systemd-machined</command>, creating a scope unit for it. It accepts the following arguments: a
+      machine name chosen by the registrar, an optional UUID as a 32 byte array, a string that identifies the
+      service that registers the machine, a class string, the PID of the leader process of the machine, an
+      optional root directory of the container, and an array of additional properties to use for the scope
+      registration. The virtual machine name must be suitable as a hostname, and hence should follow the usual
+      DNS hostname rules, as well as the Linux hostname restrictions. Specifically, only 7 bit ASCII is
+      permitted, a maximum length of 64 characters is enforced, only characters from the set
+      <literal>a-zA-Z0-9-_.</literal> are allowed, the name may not begin with a dot, and it may not contain
+      two dots immediately following each other. Container and VM managers should ideally use the hostname
+      used internally in the machine for this parameter. This recommendation is made in order to make the
+      machine name naturally resolvable using
+      <citerefentry><refentrytitle>nss-mymachines</refentrytitle><manvolnum>8</manvolnum></citerefentry>. If
+      a container manager needs to embed characters outside of the indicated range, escaping is required,
+      possibly using <literal>_</literal> as the escape character. Another (somewhat natural) option would be
+      to utilize Internet IDNA encoding. The UUID is passed as a 32 byte array or, if no suitable UUID is
+      available, an empty array (zero length) or zeroed out array shall be passed. The UUID should identify
+      the virtual machine/container uniquely and should ideally be the same UUID that
+      <filename>/etc/machine-id</filename> in the VM/container is initialized from. The service string can be
+      free-form, but it is recommended to pass a short lowercase identifier like
+      <literal>systemd-nspawn</literal>, <literal>libvirt-lxc</literal> or similar. The class string should
+      be either <literal>container</literal> or <literal>vm</literal> indicating whether the machine to
+      register is of the respective class. The leader PID should be the host PID of the init process of the
+      container or the encapsulating process of the VM. If the root directory of the container is known and
+      available in the host's hierarchy, it should be passed. Otherwise, pass the empty string instead. Finally, the
+      scope properties are passed as array in the same way as to PID1's
+      <function>StartTransientUnit()</function> method. Calling this method will internally register a transient scope
+      unit for the calling client (utilizing the passed scope_properties) and move the leader PID into
+      it. The method returns an object path for the registered machine object that implements the
+      <interfacename>org.freedesktop.machine1.Machine</interfacename> interface (see below). Also see the
+      <ulink url="https://www.freedesktop.org/wiki/Software/systemd/ControlGroupInterface/">New Control Group
+      Interfaces</ulink> for details about scope units and how to alter resource control settings on the
+      created machine at runtime.</para>
+
+      <para><function>RegisterMachine()</function> is similar to <function>CreateMachine()</function>.
+      However, it only registers a machine and does not create a scope unit for it. Instead, the caller's unit is
+      registered. We recommend to only use this method for container or VM managers that are run
+      multiple times, one instance for each container/VM they manage, and are invoked as system
+      services.</para>
+
+      <para><function>CreateMachineWithNetwork()</function> and
+      <function>RegisterMachineWithNetwork()</function> are similar to <function>CreateMachine()</function>
+      and <function>RegisterMachine()</function> but take an extra argument: an array of network interface
+      indices that point towards the virtual machine or container. The interface indices should reference one
+      or more network interfaces on the host that can be used to communicate with the guest. Commonly, the
+      passed interface index refers to the host side of a "veth" link (in case of containers), a
+      "tun"/"tap" link (in case of VMs), or the host side of a bridge interface that bridges access to the
+      VM/container interfaces. Specifying this information is useful to enable support for link-local IPv6
+      communication to the machines since the scope field of sockaddr_in6 can be initialized by the
+      specified ifindex.
+      <citerefentry><refentrytitle>nss-mymachines</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+      makes use of this information.</para>
+
+      <para><function>KillMachine()</function> sends a UNIX signal to the machine's processes. As its arguments, it takes a
+      machine name (as originally passed to <function>CreateMachine()</function> or returned by
+      <function>ListMachines()</function>), an identifier that specifies what precisely to send the signal to (either
+      <literal>leader</literal> or <literal>all</literal>), and a numeric UNIX signal integer.</para>
+
+      <para><function>TerminateMachine()</function> terminates a virtual machine, killing its processes. It
+      takes a machine name as its only argument.</para>
+
+      <para><function>GetMachineAddresses()</function> retrieves the IP addresses of a container. This method
+      returns an array of pairs consisting of an address family specifier (<constant>AF_INET</constant> or
+      <constant>AF_INET6</constant>) and a byte array containing the addresses. This is only supported for
+      containers that make use of network namespacing.</para>
+
+      <para><function>GetMachineOSRelease()</function> retrieves the OS release information of a
+      container. This method returns an array of key value pairs read from the
+      <citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry> file in
+      the container and is useful to identify the operating system used in a container.</para>
+
+      <para><function>OpenMachinePTY()</function> allocates a pseudo TTY in the container and returns a file
+      descriptor and its path. This is equivalent to transitioning into the container and invoking
+      <citerefentry project='man-pages'><refentrytitle>posix_openpt</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+      </para>
+
+      <para><function>OpenMachineLogin()</function> allocates a pseudo TTY in the container and ensures that
+      a getty login prompt of the container is running on the other end. It returns the file descriptor of
+      the PTY and the PTY path. This is useful for acquiring a pty with a login prompt from the
+      container.</para>
+
+      <para><function>OpenMachineShell()</function> allocates a pseudo TTY in the container, as the specified
+      user, and invokes the executable at the specified path with a list of arguments (starting from
+      argv[0]) and an environment block. It then returns the file descriptor of the PTY and the PTY
+      path.</para>
+
+      <para><function>BindMountMachine()</function> bind mounts a file or directory from the host into the
+      container. Its arguments consist of a machine name, the source directory on the host, the destination directory in the
+      container, and two booleans, one indicating whether the bind mount shall be
+      read-only, the other indicating whether the destination mount point shall be created first, if it is
+      missing.</para>
+
+      <para><function>CopyFromMachine()</function> copies files or directories from a container into the
+      host. It takes a container name, a source directory in the container and a destination directory on the
+      host as arguments. <function>CopyToMachine()</function> does the opposite and copies files from a source
+      directory on the host into a destination directory in the container.</para>
+
+      <para><function>RemoveImage()</function> removes the image with the specified name.</para>
+
+      <para><function>RenameImage()</function> renames the specified image.</para>
+
+      <para><function>CloneImage()</function> clones the specified image under a new name. It also takes a
+      boolean argument indicating whether the resulting image shall be read-only or not.</para>
+
+      <para><function>MarkImageReadOnly()</function> toggles the read-only flag of an image.</para>
+
+      <para><function>SetPoolLimit()</function> sets an overall quota limit on the pool of images.</para>
+
+      <para><function>SetImageLimit()</function> sets a per-image quota limit.</para>
+
+      <para><function>MapFromMachineUser()</function>, <function>MapToMachineUser()</function>,
+      <function>MapFromMachineGroup()</function>, and <function>MapToMachineGroup()</function> may be used to map
+      UIDs/GIDs from the host user namespace to a container user namespace or vice versa.</para>
+    </refsect2>
+
+    <refsect2>
+      <title>Signals</title>
+
+      <para><function>MachineNew</function> and <function>MachineRemoved</function> are sent whenever a new
+      machine is registered or removed. These signals carry the machine name and the object path to the corresponding
+      <interfacename>org.freedesktop.machine1.Machine</interfacename> interface (see below).</para>
+    </refsect2>
+
+    <refsect2>
+      <title>Properties</title>
+
+      <para><varname>PoolPath</varname> specifies the file system path where images are written to.</para>
+
+      <para><varname>PoolUsage</varname> specifies the current usage size of the image pool in bytes.</para>
+
+      <para><varname>PoolLimit</varname> specifies the size limit of the image pool in bytes.</para>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>Machine Objects</title>
+
+    <programlisting executable="systemd-machined" node="/org/freedesktop/machine1/machine/rawhide" interface="org.freedesktop.machine1.Machine">
+node /org/freedesktop/machine1/machine/rawhide {
+  interface org.freedesktop.machine1.Machine {
+    methods:
+      Terminate();
+      Kill(in  s who,
+           in  i signal);
+      GetAddresses(out a(iay) addresses);
+      GetOSRelease(out a{ss} fields);
+      GetUIDShift(out u shift);
+      OpenPTY(out h pty,
+              out s pty_path);
+      OpenLogin(out h pty,
+                out s pty_path);
+      OpenShell(in  s user,
+                in  s path,
+                in  as args,
+                in  as environment,
+                out h pty,
+                out s pty_path);
+      BindMount(in  s source,
+                in  s destination,
+                in  b read_only,
+                in  b mkdir);
+      CopyFrom(in  s source,
+               in  s destination);
+      CopyTo(in  s source,
+             in  s destination);
+      OpenRootDirectory(out h fd);
+    properties:
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s Name = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly ay Id = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t Timestamp = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t TimestampMonotonic = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s Service = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s Unit = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u Leader = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s Class = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s RootDirectory = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly ai NetworkInterfaces = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly s State = '...';
+  };
+  interface org.freedesktop.DBus.Peer { ... };
+  interface org.freedesktop.DBus.Introspectable { ... };
+  interface org.freedesktop.DBus.Properties { ... };
+};
+    </programlisting>
+
+    <!--method GetUIDShift is not documented!-->
+
+    <!--method OpenPTY is not documented!-->
+
+    <!--method OpenLogin is not documented!-->
+
+    <!--method OpenShell is not documented!-->
+
+    <!--method BindMount is not documented!-->
+
+    <!--method CopyFrom is not documented!-->
+
+    <!--method CopyTo is not documented!-->
+
+    <!--method OpenRootDirectory is not documented!-->
+
+    <!--Autogenerated cross-references for systemd.directives, do not edit-->
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.machine1.Machine"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.machine1.Machine"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Terminate()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Kill()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetAddresses()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetOSRelease()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetUIDShift()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="OpenPTY()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="OpenLogin()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="OpenShell()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="BindMount()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="CopyFrom()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="CopyTo()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="OpenRootDirectory()"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Name"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Id"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Timestamp"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TimestampMonotonic"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Service"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Unit"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Leader"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Class"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RootDirectory"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="NetworkInterfaces"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="State"/>
+
+    <!--End of Autogenerated section-->
+
+    <refsect2>
+      <title>Methods</title>
+
+      <para><function>Terminate()</function> and <function>Kill()</function> terminate/kill the machine. These methods
+      take the same arguments as <function>TerminateMachine()</function> and
+      <function>KillMachine()</function> on the Manager interface, respectively.</para>
+
+      <para><function>GetAddresses()</function> and <function>GetOSRelease()</function> get the IP address and OS
+      release information from the machine. These methods take the same arguments as
+      <function>GetMachineAddresses()</function> and <function>GetMachineOSRelease()</function> of the
+      Manager interface, respectively.</para>
+    </refsect2>
+
+    <refsect2>
+      <title>Properties</title>
+
+      <para><varname>Name</varname> is the machine name as it was passed in during registration with
+      <function>CreateMachine()</function> on the manager object.</para>
+
+      <para><varname>Id</varname> is the machine UUID.</para>
+
+      <para><varname>Timestamp</varname> and <varname>TimestampMonotonic</varname> are the realtime and
+      monotonic timestamps when the virtual machines where created in microseconds since the epoch.</para>
+
+      <para><varname>Service</varname> contains a short string identifying the registering service as passed
+      in during registration of the machine.</para>
+
+      <para><varname>Unit</varname> is the systemd scope or service unit name for the machine.</para>
+
+      <para><varname>Leader</varname> is the PID of the leader process of the machine.</para>
+
+      <para><varname>Class</varname> is the class of the machine and is either the string "vm" (for real VMs
+      based on virtualized hardware) or "container" (for light-weight userspace virtualization sharing the
+      same kernel as the host).</para>
+
+      <para><varname>RootDirectory</varname> is the root directory of the container if it is known and
+      applicable or the empty string.</para>
+
+      <para><varname>NetworkInterfaces</varname> contains an array of network interface indices that point
+      towards the container, the VM or the host. For details about this information see the description of
+      <function>CreateMachineWithNetwork()</function> above.</para>
+
+      <para><varname>State</varname> is the state of the machine and is one of <literal>opening</literal>,
+      <literal>running</literal>, or <literal>closing</literal>. Note that the state machine is not considered
+      part of the API and states might be removed or added without this being considered API breakage.
+      </para>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>Examples</title>
+
+    <example>
+      <title>Introspect <interfacename>org.freedesktop.machine1.Manager</interfacename> on the bus</title>
+
+      <programlisting>
+$ gdbus introspect --system \
+  --dest org.freedesktop.machine1 \
+  --object-path /org/freedesktop/machine1
+      </programlisting>
+    </example>
+
+    <example>
+      <title>Introspect <interfacename>org.freedesktop.machine1.Machine</interfacename> on the bus</title>
+
+      <programlisting>
+$ gdbus introspect --system \
+  --dest org.freedesktop.machine1 \
+  --object-path /org/freedesktop/machine1/machine/rawhide
+      </programlisting>
+    </example>
+  </refsect1>
+
+  <refsect1>
+    <title>Versioning</title>
+
+    <para>These D-Bus interfaces follow <ulink url="http://0pointer.de/blog/projects/versioning-dbus.html">
+    the usual interface versioning guidelines</ulink>.</para>
+  </refsect1>
+</refentry>
diff --git a/man/org.freedesktop.resolve1.xml b/man/org.freedesktop.resolve1.xml
new file mode 100644 (file)
index 0000000..96e22c6
--- /dev/null
@@ -0,0 +1,810 @@
+<?xml version="1.0"?>
+<!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
+<!ENTITY % entities SYSTEM "custom-entities.ent" >
+%entities;
+]>
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="org.freedesktop.resolve1" conditional='ENABLE_RESOLVE'
+    xmlns:xi="http://www.w3.org/2001/XInclude">
+  <refentryinfo>
+    <title>org.freedesktop.resolve1</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>org.freedesktop.resolve1</refentrytitle>
+    <manvolnum>5</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>org.freedesktop.resolve1</refname>
+    <refpurpose>The D-Bus interface of systemd-resolved</refpurpose>
+  </refnamediv>
+
+  <refsect1>
+    <title>Introduction</title>
+
+    <para>
+    <citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    is a system service that provides hostname resolution and caching using DNS, LLMNR, and mDNS. It also
+    does DNSSEC validation. This page describes the resolve semantics and the D-Bus interface.</para>
+
+    <para>This page contains an API reference only. If you are looking for a longer explanation how to use
+    this API, please consult
+    <ulink url="https://wiki.freedesktop.org/www/Software/systemd/writing-network-configuration-managers">
+    Writing Network Configuration Managers</ulink> and
+    <ulink url="https://wiki.freedesktop.org/www/Software/systemd/writing-resolver-clients">Writing Resolver
+    Clients</ulink>.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>The Manager Object</title>
+
+    <para>The service exposes the following interfaces on the Manager object on the bus:</para>
+
+    <programlisting executable="systemd-resolved" node="/org/freedesktop/resolve1" interface="org.freedesktop.resolve1.Manager">
+node /org/freedesktop/resolve1 {
+  interface org.freedesktop.resolve1.Manager {
+    methods:
+      ResolveHostname(in  i ifindex,
+                      in  s name,
+                      in  i family,
+                      in  t flags,
+                      out a(iiay) addresses,
+                      out s canonical,
+                      out t flags);
+      ResolveAddress(in  i ifindex,
+                     in  i family,
+                     in  ay address,
+                     in  t flags,
+                     out a(is) names,
+                     out t flags);
+      ResolveRecord(in  i ifindex,
+                    in  s name,
+                    in  q class,
+                    in  q type,
+                    in  t flags,
+                    out a(iqqay) records,
+                    out t flags);
+      ResolveService(in  i ifindex,
+                     in  s name,
+                     in  s type,
+                     in  s domain,
+                     in  i family,
+                     in  t flags,
+                     out a(qqqsa(iiay)s) srv_data,
+                     out aay txt_data,
+                     out s canonical_name,
+                     out s canonical_type,
+                     out s canonical_domain,
+                     out t flags);
+      GetLink(in  i ifindex,
+              out o path);
+      SetLinkDNS(in  i ifindex,
+                 in  a(iay) addresses);
+      SetLinkDomains(in  i ifindex,
+                     in  a(sb) domains);
+      SetLinkDefaultRoute(in  i ifindex,
+                          in  b enable);
+      SetLinkLLMNR(in  i ifindex,
+                   in  s mode);
+      SetLinkMulticastDNS(in  i ifindex,
+                          in  s mode);
+      SetLinkDNSOverTLS(in  i ifindex,
+                        in  s mode);
+      SetLinkDNSSEC(in  i ifindex,
+                    in  s mode);
+      SetLinkDNSSECNegativeTrustAnchors(in  i ifindex,
+                                        in  as names);
+      RevertLink(in  i ifindex);
+      RegisterService(in  s name,
+                      in  s name_template,
+                      in  s type,
+                      in  q service_port,
+                      in  q service_priority,
+                      in  q service_weight,
+                      in  aa{say} txt_datas,
+                      out o service_path);
+      UnregisterService(in  o service_path);
+      ResetStatistics();
+      FlushCaches();
+      ResetServerFeatures();
+    properties:
+      readonly s LLMNRHostname = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly s LLMNR = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly s MulticastDNS = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly s DNSOverTLS = '...';
+      readonly a(iiay) DNS = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly a(iiay) FallbackDNS = [...];
+      readonly (iiay) CurrentDNSServer = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(isb) Domains = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly (tt) TransactionStatistics = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly (ttt) CacheStatistics = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly s DNSSEC = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly (tttt) DNSSECStatistics = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b DNSSECSupported = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly as DNSSECNegativeTrustAnchors = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly s DNSStubListener = '...';
+  };
+  interface org.freedesktop.DBus.Peer { ... };
+  interface org.freedesktop.DBus.Introspectable { ... };
+  interface org.freedesktop.DBus.Properties { ... };
+};
+    </programlisting>
+
+    <!--method SetLinkDefaultRoute is not documented!-->
+
+    <!--method SetLinkDNSOverTLS is not documented!-->
+
+    <!--method RegisterService is not documented!-->
+
+    <!--method UnregisterService is not documented!-->
+
+    <!--method FlushCaches is not documented!-->
+
+    <!--method ResetServerFeatures is not documented!-->
+
+    <!--property LLMNR is not documented!-->
+
+    <!--property MulticastDNS is not documented!-->
+
+    <!--property DNSOverTLS is not documented!-->
+
+    <!--property FallbackDNS is not documented!-->
+
+    <!--property CurrentDNSServer is not documented!-->
+
+    <!--property DNSSEC is not documented!-->
+
+    <!--property DNSSECNegativeTrustAnchors is not documented!-->
+
+    <!--property DNSStubListener is not documented!-->
+
+    <!--Autogenerated cross-references for systemd.directives, do not edit-->
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.resolve1.Manager"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.resolve1.Manager"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ResolveHostname()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ResolveAddress()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ResolveRecord()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ResolveService()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetLink()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetLinkDNS()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetLinkDomains()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetLinkDefaultRoute()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetLinkLLMNR()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetLinkMulticastDNS()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetLinkDNSOverTLS()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetLinkDNSSEC()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetLinkDNSSECNegativeTrustAnchors()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="RevertLink()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="RegisterService()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="UnregisterService()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ResetStatistics()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="FlushCaches()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ResetServerFeatures()"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LLMNRHostname"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LLMNR"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MulticastDNS"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DNSOverTLS"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DNS"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="FallbackDNS"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CurrentDNSServer"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Domains"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TransactionStatistics"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CacheStatistics"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DNSSEC"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DNSSECStatistics"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DNSSECSupported"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DNSSECNegativeTrustAnchors"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DNSStubListener"/>
+
+    <!--End of Autogenerated section-->
+
+    <refsect2>
+      <title>Methods</title>
+
+      <para><function>ResolveHostname()</function> takes a hostname and resolves it to one or more IP addresses.
+      As parameters it takes the Linux network interface index to execute the query on, or 0 if it may be
+      done on any suitable interface. The <varname>name</varname> parameter specifies the hostname to
+      resolve. Note that if required, IDNA conversion is applied to this name unless it is resolved via LLMNR or MulticastDNS. The <varname>family</varname> parameter
+      limits the results to a specific address family. It may be <constant>AF_INET</constant>,
+      <constant>AF_INET6</constant> or <constant>AF_UNSPEC</constant>. If <constant>AF_UNSPEC</constant> is specified (recommended), both kinds are retrieved, subject
+      to local network configuration (i.e. if no local, routable IPv6 address is found, no IPv6 address is
+      retrieved; and similarly for IPv4). A 64-bit <varname>flags</varname> field may be used to alter the
+      behaviour of the resolver operation (see below). The method returns an array of address records. Each
+      address record consists of the interface index the address belongs to, an address family as well as a
+      byte array with the actual IP address data (which either has 4 or 16 elements, depending on the address
+      family). The returned address family will be one of <constant>AF_INET</constant> or
+      <constant>AF_INET6</constant>. For IPv6, the returned address interface index should be used to
+      initialize the .sin6_scope_id field of a <structname>struct sockaddr_in6</structname> instance to permit
+      support for resolution to link-local IP addresses. The address array is followed by the canonical name
+      of the host, which may or may not be identical to the resolved hostname. Finally, a 64-bit
+      <varname>flags</varname> field is returned that is defined similarly to the <varname>flags</varname>
+      field that was passed in, but contains information about the resolved data (see below). If the hostname
+      passed in is an IPv4 or IPv6 address formatted as string, it is parsed, and the result is returned. In
+      this case, no network communication is done.</para>
+
+      <para><function>ResolveAddress()</function> executes the reverse operation: it takes an IP address and
+      acquires one or more hostnames for it. As parameters it takes the interface index to execute the query
+      on, or <constant>0</constant> if all suitable interfaces are OK. The <varname>family</varname>
+      parameter indicates the address family of the IP address to resolve. It may be either
+      <constant>AF_INET</constant> or <constant>AF_INET6</constant>. The <varname>address</varname> parameter
+      takes the raw IP address data (as either a 4 or 16 byte array). The <varname>flags</varname> input
+      parameter may be used to alter the resolver operation (see below). The method returns an array of name
+      records, each consisting of an interface index and a hostname.  The <varname>flags</varname> output
+      field contains additional information about the resolver operation (see below).</para>
+
+      <para><function>ResolveRecord()</function> takes a DNS resource record (RR) type, class and name, and
+      retrieves the full resource record set (RRset), including the RDATA, for it. As parameter it takes the
+      Linux network interface index to execute the query on, or <constant>0</constant> if it may be done on
+      any suitable interface. The <varname>name</varname> parameter specifies the RR domain name to look up
+      (no IDNA conversion is applied), followed by the 16-bit class and type fields (which may be
+      ANY). Finally, a <varname>flags</varname> field may be passed in to alter behaviour of the look-up (see
+      below). On completion, an array of RR items is returned. Each array entry consists of the network interface
+      index the RR was discovered on, the type and class field of the RR found, and a byte array of the raw
+      RR discovered. The raw RR data starts with the RR's domain name, in the original casing, followed
+      by the RR type, class, TTL and RDATA, in the binary format documented in
+      <ulink url="https://www.ietf.org/rfc/rfc1035.txt">RFC 1035</ulink>. For RRs that support name
+      compression in the payload (such as MX or PTR), the compression is expanded in the returned
+      data.</para>
+
+      <para>Note that currently, the class field has to be specified as IN or ANY. Specifying a different
+      class will return an error indicating that look-ups of this kind are unsupported. Similarly, some
+      special types are not supported either (AXFR, OPT, …). While <filename>systemd-resolved</filename> parses and validates resource
+      records of many types, it is crucial that clients using this API understand that the RR data originates
+      from the network and should be thoroughly validated before use.</para>
+
+      <para><function>ResolveService()</function> may be used to resolve a DNS SRV service record, as well as the
+      hostnames referenced in it, and possibly an accompanying DNS-SD TXT record containing additional
+      service metadata. The primary benefit of using this method over <function>ResolveRecord()</function>
+      specifying the SRV type is that it will resolve the SRV and TXT RRs as well as the hostnames referenced
+      in the SRV in a single operation. As parameters it takes a Linux network interface index, a service
+      name, a service type and a service domain. This method may be invoked in three different modes:</para>
+
+      <orderedlist>
+        <listitem><para>To resolve a DNS-SD service, specify the service name (e.g. <literal>Lennart's
+        Files</literal>), the service type (e.g. <literal>_webdav._tcp</literal>) and the domain to search in
+        (e.g. <literal>local</literal>) as the three service parameters. The service name must be in UTF-8
+        format, and no IDNA conversion is applied to it in this mode (as mandated by the DNS-SD
+        specifications). However, if necessary, IDNA conversion is applied to the domain parameter.</para>
+        </listitem>
+
+        <listitem><para>To resolve a plain SRV record, set the service name parameter to the empty string
+        and set the service type and domain properly. (IDNA conversion is applied to the domain, if
+        necessary.)</para></listitem>
+
+        <listitem><para>Alternatively, leave both the service name and type empty and specify the full
+        domain name of the SRV record (i.e. prefixed with the service type) in the domain parameter. (No IDNA
+        coversion is applied in this mode.)</para></listitem>
+      </orderedlist>
+
+      <para>The <varname>family</varname> parameter of the <function>ResolveService()</function> method encodes
+      the desired family of the addresses to resolve (use <constant>AF_INET</constant>,
+      <constant>AF_INET6</constant>, or <constant>AF_UNSPEC</constant>). If this is enabled (Use the
+      <constant>NO_ADDRESS</constant> flag to turn address resolution off, see below). The
+      <varname>flags</varname> parameter takes a couple of flags that may be used to alter the resolver
+      operation.</para>
+
+      <para>On completion, <function>ResolveService()</function> returns an array of SRV record structures. Each
+      items consisting of the priority, weight and port fields as well as the hostname to contact, as encoded in the SRV
+      record. Immediately following is an array of the addresses of this hostname, with each item consisting
+      of the interface index, the address family and the address data in a byte array. This address array is
+      followed by the canonicalized hostname. After this array of SRV record structures an array of byte
+      arrays follows that encodes the TXT RR strings, in case DNS-SD look-ups are enabled. The next parameters
+      are the canonical service name, type and domain. This may or may not be identical to the parameters
+      passed in. Finally, a <varname>flags</varname> field is returned that contains information about the
+      resolver operation performed.</para>
+
+      <para>The <function>ResetStatistics()</function> method resets the various statistics counters that
+      <filename>systemd-resolved</filename> maintains to zero. (For details, see the statistics properties below.)</para>
+
+      <para>The <function>GetLink()</function> method takes a network interface index and returns the object
+      path to the <interfacename>org.freedesktop.resolve1.Link</interfacename> object corresponding to it.
+      </para>
+
+      <para>The <function>SetLinkDNS()</function> method sets the DNS servers to use on a specific
+      interface. This method (and the following ones) may be used by network management software to configure
+      per-interface DNS settings. It takes a network interface index as well as an array of DNS server IP
+      address records. Each array item consists of an address family (either <constant>AF_INET</constant> or
+      <constant>AF_INET6</constant>), followed by a 4-byte or 16-byte array with the raw address data. This
+      method is a one-step shortcut for retrieving the Link object for a network interface using
+      <function>GetLink()</function> (see above) and then invoking the <function>SetDNS()</function> method
+      (see below) on it.
+      </para>
+
+      <para>Network management software integrating with <filename>systemd-resolved</filename> should
+      call this method (and the five below) after the interface appeared in the kernel (and thus after a
+      network interface index has been assigned), but before the network interfaces is activated
+      (<constant>IFF_UP</constant> set) so that all settings take effect during the full time the network
+      interface is up. It is safe to alter settings while the interface is up, however. Use
+      <function>RevertLink()</function> (described below) to reset all per-interface settings.</para>
+
+      <para>The <function>SetLinkDomains()</function> method sets the search and routing domains to use on a
+      specific network interface for DNS look-ups. It takes a network interface index and an array of domains,
+      each with a boolean parameter indicating whether the specified domain shall be used as a search domain
+      (false), or just as a routing domain (true). Search domains are used for qualifying single-label names into
+      FQDN when looking up hostnames, as well as for making routing decisions on which interface to send
+      queries ending in the domain to. Routing domains are only used for routing decisions and not used for single-label
+      name qualification. Pass the search domains in the order they should be used.</para>
+
+      <para>The <function>SetLinkLLMNR()</function> method enables or disables LLMNR support on a specific
+      network interface. It takes a network interface index as well as a string that may either be empty or one of
+      <literal>yes</literal>, <literal>no</literal> or <literal>resolve</literal>. If empty, the systemd-wide
+      default LLMNR setting is used. If <literal>yes</literal>, LLMNR is used for resolution of single-label
+      names and the local hostname is registered on all local LANs for LLMNR resolution by peers. If
+      <literal>no</literal>, LLMNR is turned off fully on this interface. If <literal>resolve</literal>, LLMNR
+      is only enabled for resolving names, but the local hostname is not registered for other peers to
+      use.</para>
+
+      <para>Similarly, the <function>SetLinkMulticastDNS()</function> method enables or disables MulticastDNS
+      support on a specific interface. It takes the same parameters as <function>SetLinkLLMNR()</function>
+      described above.</para>
+
+      <para>The <function>SetLinkDNSSEC()</function> method enables or disables DNSSEC validation on a
+      specific network interface. It takes a network interface index as well as a string that may either be
+      empty or one of <literal>yes</literal>, <literal>no</literal>, or <literal>allow-downgrade</literal>. When
+      empty, the system-wide default DNSSEC setting is used. If <literal>yes</literal>, full DNSSEC validation
+      is done for all look-ups. If the selected DNS server does not support DNSSEC, look-ups will fail if this
+      mode is used. If <literal>no</literal>, DNSSEC validation is fully disabled. If
+      <literal>allow-downgrade</literal>, DNSSEC validation is enabled, but is turned off automatically if the
+      selected server does not support it (thus opening up behaviour to downgrade attacks). Note that DNSSEC
+      only applies to traditional DNS, not to LLMNR or MulticastDNS.</para>
+
+      <para>The <function>SetLinkDNSSECNegativeTrustAnchors()</function> method may be used to configure DNSSEC
+      Negative Trust Anchors (NTAs) for a specific network interface. It takes a network interface index and a
+      list of domains as arguments.</para>
+
+      <para>The <function>RevertLink()</function> method may be used to revert all per-link settings done with
+      the six methods described above to the defaults again.</para>
+
+      <refsect3>
+        <title>The Flags Parameter</title>
+
+        <para>The four methods above accept and return a 64-bit flags value. In most cases passing 0 is sufficient
+        and recommended. However, the following flags are defined to alter the look-up:</para>
+
+        <programlisting>
+#define SD_RESOLVED_DNS           (UINT64_C(1) &lt;&lt; 0)
+#define SD_RESOLVED_LLMNR_IPV4    (UINT64_C(1) &lt;&lt; 1)
+#define SD_RESOLVED_LLMNR_IPV6    (UINT64_C(1) &lt;&lt; 2)
+#define SD_RESOLVED_MDNS_IPV4     (UINT64_C(1) &lt;&lt; 3)
+#define SD_RESOLVED_MDNS_IPV6     (UINT64_C(1) &lt;&lt; 4)
+#define SD_RESOLVED_NO_CNAME      (UINT64_C(1) &lt;&lt; 5)
+#define SD_RESOLVED_NO_TXT        (UINT64_C(1) &lt;&lt; 6)
+#define SD_RESOLVED_NO_ADDRESS    (UINT64_C(1) &lt;&lt; 7)
+#define SD_RESOLVED_NO_SEARCH     (UINT64_C(1) &lt;&lt; 8)
+#define SD_RESOLVED_AUTHENTICATED (UINT64_C(1) &lt;&lt; 9)
+        </programlisting>
+
+        <para>On input, the first five flags control the protocols to use for the look-up. They refer to
+        classic unicast DNS, LLMNR via IPv4/UDP and IPv6/UDP respectively, as well as MulticastDNS via
+        IPv4/UDP and IPv6/UDP. If all of these five bits are off on input (which is strongly recommended) the
+        look-up will be done via all suitable protocols for the specific look-up. Note that these flags
+        operate as filter only, but cannot force a look-up to be done via a protocol. Specifically, <filename>systemd-resolved</filename>
+        will only route look-ups within the .local TLD to MulticastDNS (plus some reverse look-up address
+        domains), and single-label names to LLMNR (plus some reverse address lookup domains). It will route
+        neither of these to Unicast DNS servers. Also, it will do LLMNR and Multicast DNS only on interfaces
+        suitable for multicast.</para>
+
+        <para>On output, these five flags indicate which protocol was used to execute the operation, and hence
+        where the data was found.</para>
+
+        <para>The primary use cases for these five flags are follow-up look-ups based on DNS data retrieved
+        earlier. In this case it is often a good idea to limit the follow-up look-up to the protocol that was
+        used to discover the first DNS result.</para>
+
+        <para>The NO_CNAME flag controls whether CNAME/DNAME resource records shall be followed during the
+        look-up. This flag is only available at input, none of the functions will return it on output. If a
+        CNAME/DNAME RR is discovered while resolving a hostname, an error is returned instead. By default,
+        when the flag is off, CNAME/DNAME RRs are followed.</para>
+
+        <para>The NO_TXT and NO_ADDRESS flags only influence operation of the
+        <function>ResolveService()</function> method. They are only defined for input, not output. If
+        NO_TXT set, the DNS-SD TXT RR look-up is not done in the same operation. If NO_ADDRESS is specified,
+        the hostnames discovered are not implicitly translated to their addresses.</para>
+
+        <para>The NO_SEARCH flag turns off the search domain logic. It is only defined for input in
+        <function>ResolveHostname()</function>. When specified, single-label hostnames are not qualified
+        using defined search domains, if any are configured. Note that <function>ResolveRecord()</function>
+        will never qualify single-label domain names using search domains. Also note that
+        multi-label hostnames are never subject to search list expansion.</para>
+
+        <para>The AUTHENTICATED bit is defined only in the output flags of the four functions. If set, the
+        returned data has been fully authenticated. Specifically, this bit is set for all DNSSEC-protected data
+        for which a full trust chain may be established to a trusted domain anchor. It is also set for locally
+        synthesized data, such as <literal>localhost</literal> or data from
+        <filename>/etc/hosts</filename>. Moreover, it is set for all LLMNR or mDNS RRs which originate from the
+        local host. Applications that require authenticated RR data for operation should check this flag before
+        trusting the data. Note that <filename>systemd-resolved</filename> will never return invalidated data, hence this flag
+        simply allows to discern the cases where data is known to be trustable, or where there is proof that
+        the data is "rightfully" unauthenticated (which includes cases where the underlying protocol or server
+        does not support authenticating data).</para>
+      </refsect3>
+
+  </refsect2>
+
+    <refsect2>
+      <title>Properties</title>
+
+      <para><varname>LLMNRHostname</varname> contains the hostname currently exposed on the network via
+      LLMNR. It usually follows the system hostname as may be queried via
+      <citerefentry project="man-pages"><refentrytitle>gethostname</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      but may differ if a conflict is detected on the network.</para>
+
+      <para><varname>DNS</varname> contains an array of all DNS servers currently used by
+      <filename>systemd-resolved</filename>. It contains similar information as the DNS server data written to
+      /run/systemd/resolve/resolv.conf. Each structure in the array consists of a numeric network interface
+      index, an address family, and a byte array containing the DNS server address (either 4 bytes in length
+      for IPv4 or 16 bytes in lengths for IPv6). The array contains DNS servers configured system-wide,
+      including those possibly read from a foreign <filename>/etc/resolv.conf</filename> or the
+      <varname>DNS=</varname> setting in <filename>/etc/systemd/resolved.conf</filename>, as well as
+      per-interface DNS server information either retrieved from
+      <citerefentry><refentrytitle>systemd-networkd</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+      or configured by external software via <function>SetLinkDNS()</function> (see above). The network
+      interface index will be 0 for the system-wide configured services and non-zero for the per-link
+      servers.</para>
+
+      <para>Similarly, the <varname>Domains</varname> property contains an array of all search and
+      routing domains currently used by <filename>systemd-resolved</filename>. Each entry consists of a network interface index (again, 0
+      encodes system-wide entries), the actual domain name, and whether the entry is used only for routing
+      (true) or for both routing and searching (false).</para>
+
+      <para>The <varname>TransactionStatistics</varname> property contains information about the number of
+      transactions <filename>systemd-resolved</filename> has processed. It contains a pair of unsigned 64-bit counters, the first
+      containing the number of currently ongoing transactions, the second the number of total transactions
+      <filename>systemd-resolved</filename> is processing or has processed. The latter value may be reset using the
+      <function>ResetStatistics()</function> method described above. Note that the number of transactions does
+      not directly map to the number of issued resolver bus method calls. While simple look-ups usually require a
+      single transaction only, more complex look-ups might result in more, for example when CNAMEs or DNSSEC
+      are in use.</para>
+
+      <para>The <varname>CacheStatistics</varname> property contains information about the executed cache
+      operations so far. It exposes three 64-bit counters: the first being the total number of current cache
+      entries (both positive and negative), the second the number of cache hits, and the third the number of
+      cache misses. The latter counters may be reset using <function>ResetStatistics()</function> (see
+      above). </para>
+
+      <para>The <varname>DNSSECStatistics</varname> property contains information about the DNSSEC
+      validations executed so far. It contains four 64-bit counters: the number of secure, insecure, bogus,
+      and indeterminate DNSSEC validations so far. The counters are increased for each validated RRset, and
+      each non-existance proof. The secure counter is increased for each operation that successfully verified
+      a signed reply, the insecure counter is increased for each operation that successfully verified that an
+      unsigned reply is rightfully unsigned. The bogus counter is increased for each operation where the
+      validation did not check out and the data is likely to have been tempered with. Finally the
+      indeterminate counter is increased for each operation which did not complete because the necessary keys
+      could not be acquired or the cryptographic algorithms were unknown.</para>
+
+      <para>The <varname>DNSSECSupported</varname> boolean property reports whether DNSSEC is enabled and
+      the selected DNS servers support it. It combines information about system-wide and per-link DNS
+      settings (see below), and only reports true if DNSSEC is enabled and supported on every interface for
+      which DNS is configured and for the system-wide settings if there are any. Note that <filename>systemd-resolved</filename> assumes
+      DNSSEC is supported by DNS servers until it verifies that this is not the case. Thus, the reported
+      value may initially be true, until the first transactions are executed.</para>
+
+      <para>The <varname>LogLevel</varname> property shows the (maximum) log level of the manager, with the
+      same values as the <option>--log-level=</option> option described in
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>Link Object</title>
+
+    <programlisting executable="systemd-resolved" node="/org/freedesktop/resolve1/link/_1" interface="org.freedesktop.resolve1.Link">
+node /org/freedesktop/resolve1/link/_1 {
+  interface org.freedesktop.resolve1.Link {
+    methods:
+      SetDNS(in  a(iay) addresses);
+      SetDomains(in  a(sb) domains);
+      SetDefaultRoute(in  b enable);
+      SetLLMNR(in  s mode);
+      SetMulticastDNS(in  s mode);
+      SetDNSOverTLS(in  s mode);
+      SetDNSSEC(in  s mode);
+      SetDNSSECNegativeTrustAnchors(in  as names);
+      Revert();
+    properties:
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t ScopesMask = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(iay) DNS = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly (iay) CurrentDNSServer = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(sb) Domains = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b DefaultRoute = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly s LLMNR = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly s MulticastDNS = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly s DNSOverTLS = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly s DNSSEC = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly as DNSSECNegativeTrustAnchors = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b DNSSECSupported = ...;
+  };
+  interface org.freedesktop.DBus.Peer { ... };
+  interface org.freedesktop.DBus.Introspectable { ... };
+  interface org.freedesktop.DBus.Properties { ... };
+};
+    </programlisting>
+
+    <!--method SetDomains is not documented!-->
+
+    <!--method SetDefaultRoute is not documented!-->
+
+    <!--method SetLLMNR is not documented!-->
+
+    <!--method SetMulticastDNS is not documented!-->
+
+    <!--method SetDNSOverTLS is not documented!-->
+
+    <!--method SetDNSSEC is not documented!-->
+
+    <!--method SetDNSSECNegativeTrustAnchors is not documented!-->
+
+    <!--method Revert is not documented!-->
+
+    <!--property CurrentDNSServer is not documented!-->
+
+    <!--property DefaultRoute is not documented!-->
+
+    <!--property LLMNR is not documented!-->
+
+    <!--property MulticastDNS is not documented!-->
+
+    <!--property DNSOverTLS is not documented!-->
+
+    <!--property DNSSEC is not documented!-->
+
+    <!--property DNSSECNegativeTrustAnchors is not documented!-->
+
+    <!--Autogenerated cross-references for systemd.directives, do not edit-->
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.resolve1.Link"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.resolve1.Link"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetDNS()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetDomains()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetDefaultRoute()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetLLMNR()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetMulticastDNS()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetDNSOverTLS()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetDNSSEC()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetDNSSECNegativeTrustAnchors()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Revert()"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ScopesMask"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DNS"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CurrentDNSServer"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Domains"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultRoute"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LLMNR"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MulticastDNS"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DNSOverTLS"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DNSSEC"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DNSSECNegativeTrustAnchors"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DNSSECSupported"/>
+
+    <!--End of Autogenerated section-->
+
+    <para>For each Linux network interface a "Link" object is created which exposes per-link DNS
+    configuration and state. Use <function>GetLink()</function> on the Manager interface to retrieve the
+    object path for a link object given the network interface index (see above).</para>
+
+    <refsect2>
+      <title>Methods</title>
+
+      <para>The various methods exposed by the Link interface are equivalent to their similarly named
+      counterparts on the Manager interface. e.g. <function>SetDNS()</function> on the Link object maps to
+      <function>SetLinkDNS()</function> on the Manager object, the main difference being that the later
+      expects an interface index to be specified. Invoking the methods on the Manager interface has the
+      benefit of reducing roundtrips, as it is not necessary to first request the Link object path via
+      <function>GetLink()</function> before invoking the methods. For further details on these methods see
+      the <interfacename>Manager</interfacename> documentation above.</para>
+    </refsect2>
+
+    <refsect2>
+      <title>Properties</title>
+
+      <para><varname>ScopesMask</varname> defines which resolver scopes are currently active on this
+      interface. This 64-bit unsigned integer field is a bit mask consisting of a subset of the bits of the
+      flags parameter describe above. Specifically, it may have the DNS, LLMNR and MDNS bits (the latter in
+      IPv4 and IPv6 flavours) set. Each individual bit is set when the protocol applies to a specific
+      interface and is enabled for it. It is unset otherwise. Specifically, a multicast-capable interface in
+      the "UP" state with an IP address is suitable for LLMNR or MulticastDNS, and any interface that is UP and
+      has an IP address is suitable for DNS. Note the relationship of the bits exposed here with the LLMNR
+      and MulticastDNS properties also exposed on the Link interface. The latter expose what is *configured*
+      to be used on the interface, the former expose what is actually used on the interface, taking into
+      account the abilities of the interface.</para>
+
+      <para><varname>DNSSECSupported</varname> exposes a boolean field that indicates whether DNSSEC is
+      currently configured and in use on the interface. Note that if DNSSEC is enabled on an interface, it is
+      assumed available until it is detected that the configured server does not actually support it. Thus,
+      this property may initially report that DNSSEC is supported on an interface.</para>
+
+      <para>The other properties reflect the state of the various configuration settings for the link which
+      may be set with the various methods calls such as SetDNS() or SetLLMNR().</para>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>Common Errors</title>
+
+    <para>Many bus methods <filename>systemd-resolved</filename> exposes (in particular the resolver methods such
+    as <function>ResolveHostname()</function> on the <interfacename>Manager</interfacename> interface) may return
+    some of the following errors:</para>
+
+    <variablelist>
+      <varlistentry><term><constant>org.freedesktop.resolve1.NoNameServers</constant></term>
+      <listitem><para>No suitable DNS servers were found to resolve a request.</para></listitem>
+      </varlistentry>
+
+      <varlistentry><term><constant>org.freedesktop.resolve1.InvalidReply</constant></term>
+      <listitem><para>A response from the selected DNS server was not understood.</para></listitem>
+      </varlistentry>
+
+      <varlistentry><term><constant>org.freedesktop.resolve1.NoSuchRR</constant></term>
+      <listitem><para>The requested name exists, but there is no resource record of the requested type for
+      it. (This is the DNS NODATA case).</para></listitem></varlistentry>
+
+      <varlistentry><term><constant>org.freedesktop.resolve1.CNameLoop</constant></term>
+      <listitem><para>The look-up failed because a CNAME or DNAME loop was detected.</para></listitem>
+      </varlistentry>
+
+      <varlistentry><term><constant>org.freedesktop.resolve1.Aborted</constant></term>
+      <listitem><para>The look-up was aborted because the selected protocol became unavailable while the
+      operation was ongoing.</para></listitem>
+      </varlistentry>
+
+      <varlistentry><term><constant>org.freedesktop.resolve1.NoSuchService</constant></term>
+      <listitem><para>A service look-up was successful, but the SRV record reported that the service is not
+      available.</para></listitem></varlistentry>
+
+      <varlistentry><term><constant>org.freedesktop.resolve1.DnssecFailed</constant></term>
+      <listitem><para>The acquired response did not pass DNSSEC validation.</para></listitem>
+      </varlistentry>
+
+      <varlistentry><term><constant>org.freedesktop.resolve1.NoTrustAnchor</constant></term>
+      <listitem><para>No chain of trust could be established for the response to a configured DNSSEC trust
+      anchor.</para></listitem>
+      </varlistentry>
+
+      <varlistentry><term><constant>org.freedesktop.resolve1.ResourceRecordTypeUnsupported</constant></term>
+      <listitem><para>The requested resource record type is not supported on the selected DNS servers. This
+      error is generated for example when an RRSIG record is requested from a DNS server that does not
+      support DNSSEC.</para></listitem>
+
+      </varlistentry>
+
+      <varlistentry><term><constant>org.freedesktop.resolve1.NoSuchLink</constant></term>
+      <listitem><para>No network interface with the specified network interface index exists.
+      </para></listitem></varlistentry>
+
+      <varlistentry><term><constant>org.freedesktop.resolve1.LinkBusy</constant></term>
+      <listitem><para>The requested configuration change could not be made because
+      <citerefentry><refentrytitle>systemd-networkd</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+      already took possession of the interface and supplied configuration data for it.</para></listitem>
+      </varlistentry>
+
+      <varlistentry><term><constant>org.freedesktop.resolve1.NetworkDown</constant></term>
+      <listitem><para>The requested look-up failed because the system is currently not connected to any
+      suitable network.</para></listitem></varlistentry>
+
+      <varlistentry><term><constant>org.freedesktop.resolve1.DnsError.NXDOMAIN</constant></term>
+      <term><constant>org.freedesktop.resolve1.DnsError.REFUSED</constant></term>
+      <term>...</term>
+      <listitem><para>The look-up failed with a DNS return code reporting a failure. The error names used as
+      suffixes here are defined in by IANA in
+      <ulink url="https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-6">DNS RCODEs</ulink>.
+      </para></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Examples</title>
+
+    <example>
+      <title>Introspect <interfacename>org.freedesktop.resolve1.Manager</interfacename> on the bus</title>
+
+      <programlisting>
+$ gdbus introspect --system \
+  --dest org.freedesktop.resolve1 \
+  --object-path /org/freedesktop/resolve1
+      </programlisting>
+    </example>
+
+    <example>
+      <title>Introspect <interfacename>org.freedesktop.resolve1.Link</interfacename> on the bus</title>
+
+      <programlisting>
+$ gdbus introspect --system \
+  --dest org.freedesktop.resolve1 \
+  --object-path /org/freedesktop/resolve1/link/_11
+      </programlisting>
+    </example>
+  </refsect1>
+
+  <refsect1>
+    <title>Versioning</title>
+
+    <para>These D-Bus interfaces follow <ulink url="http://0pointer.de/blog/projects/versioning-dbus.html">
+    the usual interface versioning guidelines</ulink>.</para>
+  </refsect1>
+</refentry>
diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml
new file mode 100644 (file)
index 0000000..6b16ae1
--- /dev/null
@@ -0,0 +1,9400 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" >
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="org.freedesktop.systemd1" xmlns:xi="http://www.w3.org/2001/XInclude">
+  <refentryinfo>
+    <title>org.freedesktop.systemd1</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>org.freedesktop.systemd1</refentrytitle>
+    <manvolnum>5</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>org.freedesktop.systemd1</refname>
+    <refpurpose>The D-Bus interface of systemd</refpurpose>
+  </refnamediv>
+
+  <refsect1>
+    <title>Introduction</title>
+
+    <para>
+    <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry> and its
+    auxiliary daemons expose a number of APIs over D-Bus. This page only describes the various APIs exposed by the
+    system and service manager itself. It does not cover the auxiliary daemons.
+    </para>
+
+    <para>The service manager exposes a number of objects on the bus: one
+    <interfacename>Manager</interfacename> object as a central entry point for clients along with individual objects
+    for each unit and for each queued job. The unit objects each implement a generic
+    <interfacename>Unit</interfacename> interface as well as a type-specific interface. For example, service units
+    implement both <interfacename>org.freedesktop.systemd1.Unit</interfacename> and
+    <interfacename>org.freedesktop.system1.Service</interfacename>. The manager object can list
+    unit and job objects or directly convert a unit name or job id into a bus path of the corresponding
+    D-Bus object.</para>
+
+    <para>Properties exposing time values are usually encoded in microseconds (usec) on the bus, even if
+    their corresponding settings in the unit files are in seconds.</para>
+
+    <para>In contrast to most of the other services of the systemd suite, PID 1 does not use
+    <ulink url="https://www.freedesktop.org/software/polkit/docs/latest/">polkit</ulink>
+    for controlling access to privileged operations, but relies exclusively on the low-level D-Bus policy
+    language. (This is done in order to avoid a cyclic dependency between polkit and systemd/PID 1.) This
+    means that sensitive operations exposed by PID 1 on the bus are generally not available to unprivileged
+    processes directly. However, some operations (such as shutdown/reboot/suspend) are made available through the D-Bus
+    API of logind, see
+    <citerefentry><refentrytitle>org.freedesktop.login1</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>The Manager Object</title>
+
+    <para>The main entry point object is available on the fixed
+    <constant>/org/freedesktop/systemd1</constant> object path:</para>
+
+    <programlisting executable="systemd" node="/org/freedesktop/systemd1" interface="org.freedesktop.systemd1.Manager">
+node /org/freedesktop/systemd1 {
+  interface org.freedesktop.systemd1.Manager {
+    methods:
+      GetUnit(in  s name,
+              out o unit);
+      GetUnitByPID(in  u pid,
+                   out o unit);
+      GetUnitByInvocationID(in  ay invocation_id,
+                            out o unit);
+      GetUnitByControlGroup(in  s cgroup,
+                            out o unit);
+      LoadUnit(in  s name,
+               out o unit);
+      StartUnit(in  s name,
+                in  s mode,
+                out o job);
+      StartUnitReplace(in  s old_unit,
+                       in  s new_unit,
+                       in  s mode,
+                       out o job);
+      StopUnit(in  s name,
+               in  s mode,
+               out o job);
+      ReloadUnit(in  s name,
+                 in  s mode,
+                 out o job);
+      RestartUnit(in  s name,
+                  in  s mode,
+                  out o job);
+      TryRestartUnit(in  s name,
+                     in  s mode,
+                     out o job);
+      ReloadOrRestartUnit(in  s name,
+                          in  s mode,
+                          out o job);
+      ReloadOrTryRestartUnit(in  s name,
+                             in  s mode,
+                             out o job);
+      EnqueueUnitJob(in  s name,
+                     in  s job_type,
+                     in  s job_mode,
+                     out u job_id,
+                     out o job_path,
+                     out s unit_id,
+                     out o unit_path,
+                     out s job_type,
+                     out a(uosos) affected_jobs);
+      KillUnit(in  s name,
+               in  s whom,
+               in  i signal);
+      CleanUnit(in  s name,
+                in  as mask);
+      FreezeUnit(in  s name);
+      ThawUnit(in  s name);
+      ResetFailedUnit(in  s name);
+      SetUnitProperties(in  s name,
+                        in  b runtime,
+                        in  a(sv) properties);
+      RefUnit(in  s name);
+      UnrefUnit(in  s name);
+      StartTransientUnit(in  s name,
+                         in  s mode,
+                         in  a(sv) properties,
+                         in  a(sa(sv)) aux,
+                         out o job);
+      GetUnitProcesses(in  s name,
+                       out a(sus) processes);
+      AttachProcessesToUnit(in  s unit_name,
+                            in  s subcgroup,
+                            in  au pids);
+      AbandonScope(in  s name);
+      GetJob(in  u id,
+             out o job);
+      GetJobAfter(in  u id,
+                  out a(usssoo) jobs);
+      GetJobBefore(in  u id,
+                   out a(usssoo) jobs);
+      CancelJob(in  u id);
+      ClearJobs();
+      ResetFailed();
+      ListUnits(out a(ssssssouso) units);
+      ListUnitsFiltered(in  as states,
+                        out a(ssssssouso) units);
+      ListUnitsByPatterns(in  as states,
+                          in  as patterns,
+                          out a(ssssssouso) units);
+      ListUnitsByNames(in  as names,
+                       out a(ssssssouso) units);
+      ListJobs(out a(usssoo) jobs);
+      Subscribe();
+      Unsubscribe();
+      Dump(out s output);
+      DumpByFileDescriptor(out h fd);
+      Reload();
+      Reexecute();
+      Exit();
+      Reboot();
+      PowerOff();
+      Halt();
+      KExec();
+      SwitchRoot(in  s new_root,
+                 in  s init);
+      SetEnvironment(in  as assignments);
+      UnsetEnvironment(in  as names);
+      UnsetAndSetEnvironment(in  as names,
+                             in  as assignments);
+      ListUnitFiles(out a(ss) unit_files);
+      ListUnitFilesByPatterns(in  as states,
+                              in  as patterns,
+                              out a(ss) unit_files);
+      GetUnitFileState(in  s file,
+                       out s state);
+      EnableUnitFiles(in  as files,
+                      in  b runtime,
+                      in  b force,
+                      out b carries_install_info,
+                      out a(sss) changes);
+      DisableUnitFiles(in  as files,
+                       in  b runtime,
+                       out a(sss) changes);
+      ReenableUnitFiles(in  as files,
+                        in  b runtime,
+                        in  b force,
+                        out b carries_install_info,
+                        out a(sss) changes);
+      LinkUnitFiles(in  as files,
+                    in  b runtime,
+                    in  b force,
+                    out a(sss) changes);
+      PresetUnitFiles(in  as files,
+                      in  b runtime,
+                      in  b force,
+                      out b carries_install_info,
+                      out a(sss) changes);
+      PresetUnitFilesWithMode(in  as files,
+                              in  s mode,
+                              in  b runtime,
+                              in  b force,
+                              out b carries_install_info,
+                              out a(sss) changes);
+      MaskUnitFiles(in  as files,
+                    in  b runtime,
+                    in  b force,
+                    out a(sss) changes);
+      UnmaskUnitFiles(in  as files,
+                      in  b runtime,
+                      out a(sss) changes);
+      RevertUnitFiles(in  as files,
+                      out a(sss) changes);
+      SetDefaultTarget(in  s name,
+                       in  b force,
+                       out a(sss) changes);
+      GetDefaultTarget(out s name);
+      PresetAllUnitFiles(in  s mode,
+                         in  b runtime,
+                         in  b force,
+                         out a(sss) changes);
+      AddDependencyUnitFiles(in  as files,
+                             in  s target,
+                             in  s type,
+                             in  b runtime,
+                             in  b force,
+                             out a(sss) changes);
+      GetUnitFileLinks(in  s name,
+                       in  b runtime,
+                       out as links);
+      SetExitCode(in  y number);
+      LookupDynamicUserByName(in  s name,
+                              out u uid);
+      LookupDynamicUserByUID(in  u uid,
+                             out s name);
+      GetDynamicUsers(out a(us) users);
+    signals:
+      UnitNew(s id,
+              o unit);
+      UnitRemoved(s id,
+                  o unit);
+      JobNew(u id,
+             o job,
+             s unit);
+      JobRemoved(u id,
+                 o job,
+                 s unit,
+                 s result);
+      StartupFinished(t firmware,
+                      t loader,
+                      t kernel,
+                      t initrd,
+                      t userspace,
+                      t total);
+      UnitFilesChanged();
+      Reloading(b active);
+    properties:
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s Version = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s Features = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s Virtualization = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s Architecture = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s Tainted = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t FirmwareTimestamp = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t FirmwareTimestampMonotonic = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LoaderTimestamp = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LoaderTimestampMonotonic = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t KernelTimestamp = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t KernelTimestampMonotonic = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t InitRDTimestamp = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t InitRDTimestampMonotonic = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t UserspaceTimestamp = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t UserspaceTimestampMonotonic = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t FinishTimestamp = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t FinishTimestampMonotonic = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t SecurityStartTimestamp = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t SecurityStartTimestampMonotonic = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t SecurityFinishTimestamp = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t SecurityFinishTimestampMonotonic = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t GeneratorsStartTimestamp = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t GeneratorsStartTimestampMonotonic = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t GeneratorsFinishTimestamp = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t GeneratorsFinishTimestampMonotonic = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t UnitsLoadStartTimestamp = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t UnitsLoadStartTimestampMonotonic = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t UnitsLoadFinishTimestamp = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t UnitsLoadFinishTimestampMonotonic = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t InitRDSecurityStartTimestamp = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t InitRDSecurityStartTimestampMonotonic = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t InitRDSecurityFinishTimestamp = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t InitRDSecurityFinishTimestampMonotonic = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t InitRDGeneratorsStartTimestamp = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t InitRDGeneratorsStartTimestampMonotonic = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t InitRDGeneratorsFinishTimestamp = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t InitRDGeneratorsFinishTimestampMonotonic = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t InitRDUnitsLoadStartTimestamp = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t InitRDUnitsLoadStartTimestampMonotonic = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t InitRDUnitsLoadFinishTimestamp = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t InitRDUnitsLoadFinishTimestampMonotonic = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      @org.freedesktop.systemd1.Privileged("true")
+      readwrite s LogLevel = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      @org.freedesktop.systemd1.Privileged("true")
+      readwrite s LogTarget = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly u NNames = ...;
+      readonly u NFailedUnits = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly u NJobs = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly u NInstalledJobs = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly u NFailedJobs = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly d Progress = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly as Environment = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b ConfirmSpawn = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b ShowStatus = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as UnitPath = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s DefaultStandardOutput = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s DefaultStandardError = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      @org.freedesktop.systemd1.Privileged("true")
+      readwrite t RuntimeWatchdogUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      @org.freedesktop.systemd1.Privileged("true")
+      readwrite t RebootWatchdogUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      @org.freedesktop.systemd1.Privileged("true")
+      readwrite t KExecWatchdogUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      @org.freedesktop.systemd1.Privileged("true")
+      readwrite b ServiceWatchdogs = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly s ControlGroup = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly s SystemState = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly y ExitCode = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t DefaultTimerAccuracyUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t DefaultTimeoutStartUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t DefaultTimeoutStopUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t DefaultTimeoutAbortUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t DefaultRestartUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t DefaultStartLimitIntervalUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u DefaultStartLimitBurst = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b DefaultCPUAccounting = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b DefaultBlockIOAccounting = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b DefaultMemoryAccounting = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b DefaultTasksAccounting = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t DefaultLimitCPU = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t DefaultLimitCPUSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t DefaultLimitFSIZE = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t DefaultLimitFSIZESoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t DefaultLimitDATA = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t DefaultLimitDATASoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t DefaultLimitSTACK = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t DefaultLimitSTACKSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t DefaultLimitCORE = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t DefaultLimitCORESoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t DefaultLimitRSS = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t DefaultLimitRSSSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t DefaultLimitNOFILE = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t DefaultLimitNOFILESoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t DefaultLimitAS = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t DefaultLimitASSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t DefaultLimitNPROC = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t DefaultLimitNPROCSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t DefaultLimitMEMLOCK = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t DefaultLimitMEMLOCKSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t DefaultLimitLOCKS = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t DefaultLimitLOCKSSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t DefaultLimitSIGPENDING = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t DefaultLimitSIGPENDINGSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t DefaultLimitMSGQUEUE = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t DefaultLimitMSGQUEUESoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t DefaultLimitNICE = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t DefaultLimitNICESoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t DefaultLimitRTPRIO = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t DefaultLimitRTPRIOSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t DefaultLimitRTTIME = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t DefaultLimitRTTIMESoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t DefaultTasksMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t TimerSlackNSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s DefaultOOMPolicy = '...';
+  };
+  interface org.freedesktop.DBus.Peer { ... };
+  interface org.freedesktop.DBus.Introspectable { ... };
+  interface org.freedesktop.DBus.Properties { ... };
+};
+    </programlisting>
+
+    <!--method GetUnitByInvocationID is not documented!-->
+
+    <!--method GetUnitByControlGroup is not documented!-->
+
+    <!--method EnqueueUnitJob is not documented!-->
+
+    <!--method CleanUnit is not documented!-->
+
+    <!--method FreezeUnit is not documented!-->
+
+    <!--method ThawUnit is not documented!-->
+
+    <!--method RefUnit is not documented!-->
+
+    <!--method UnrefUnit is not documented!-->
+
+    <!--method GetUnitProcesses is not documented!-->
+
+    <!--method AttachProcessesToUnit is not documented!-->
+
+    <!--method AbandonScope is not documented!-->
+
+    <!--method GetJobAfter is not documented!-->
+
+    <!--method GetJobBefore is not documented!-->
+
+    <!--method ListUnitsFiltered is not documented!-->
+
+    <!--method ListUnitsByPatterns is not documented!-->
+
+    <!--method ListUnitsByNames is not documented!-->
+
+    <!--method Dump is not documented!-->
+
+    <!--method DumpByFileDescriptor is not documented!-->
+
+    <!--method ListUnitFilesByPatterns is not documented!-->
+
+    <!--method PresetUnitFilesWithMode is not documented!-->
+
+    <!--method RevertUnitFiles is not documented!-->
+
+    <!--method PresetAllUnitFiles is not documented!-->
+
+    <!--method AddDependencyUnitFiles is not documented!-->
+
+    <!--method GetUnitFileLinks is not documented!-->
+
+    <!--method SetExitCode is not documented!-->
+
+    <!--method LookupDynamicUserByName is not documented!-->
+
+    <!--method LookupDynamicUserByUID is not documented!-->
+
+    <!--method GetDynamicUsers is not documented!-->
+
+    <!--signal UnitNew is not documented!-->
+
+    <!--signal UnitRemoved is not documented!-->
+
+    <!--signal JobNew is not documented!-->
+
+    <!--signal JobRemoved is not documented!-->
+
+    <!--signal StartupFinished is not documented!-->
+
+    <!--signal UnitFilesChanged is not documented!-->
+
+    <!--signal Reloading is not documented!-->
+
+    <!--property SecurityStartTimestampMonotonic is not documented!-->
+
+    <!--property SecurityFinishTimestamp is not documented!-->
+
+    <!--property SecurityFinishTimestampMonotonic is not documented!-->
+
+    <!--property GeneratorsStartTimestampMonotonic is not documented!-->
+
+    <!--property GeneratorsFinishTimestamp is not documented!-->
+
+    <!--property GeneratorsFinishTimestampMonotonic is not documented!-->
+
+    <!--property UnitsLoadStartTimestamp is not documented!-->
+
+    <!--property UnitsLoadStartTimestampMonotonic is not documented!-->
+
+    <!--property UnitsLoadFinishTimestamp is not documented!-->
+
+    <!--property UnitsLoadFinishTimestampMonotonic is not documented!-->
+
+    <!--property InitRDSecurityStartTimestamp is not documented!-->
+
+    <!--property InitRDSecurityStartTimestampMonotonic is not documented!-->
+
+    <!--property InitRDSecurityFinishTimestamp is not documented!-->
+
+    <!--property InitRDSecurityFinishTimestampMonotonic is not documented!-->
+
+    <!--property InitRDGeneratorsStartTimestamp is not documented!-->
+
+    <!--property InitRDGeneratorsStartTimestampMonotonic is not documented!-->
+
+    <!--property InitRDGeneratorsFinishTimestamp is not documented!-->
+
+    <!--property InitRDGeneratorsFinishTimestampMonotonic is not documented!-->
+
+    <!--property InitRDUnitsLoadStartTimestamp is not documented!-->
+
+    <!--property InitRDUnitsLoadStartTimestampMonotonic is not documented!-->
+
+    <!--property InitRDUnitsLoadFinishTimestamp is not documented!-->
+
+    <!--property InitRDUnitsLoadFinishTimestampMonotonic is not documented!-->
+
+    <!--property LogLevel is not documented!-->
+
+    <!--property LogTarget is not documented!-->
+
+    <!--property NFailedUnits is not documented!-->
+
+    <!--property ConfirmSpawn is not documented!-->
+
+    <!--property ShowStatus is not documented!-->
+
+    <!--property DefaultStandardOutput is not documented!-->
+
+    <!--property DefaultStandardError is not documented!-->
+
+    <!--property RuntimeWatchdogUSec is not documented!-->
+
+    <!--property RebootWatchdogUSec is not documented!-->
+
+    <!--property KExecWatchdogUSec is not documented!-->
+
+    <!--property ServiceWatchdogs is not documented!-->
+
+    <!--property SystemState is not documented!-->
+
+    <!--property ExitCode is not documented!-->
+
+    <!--property DefaultTimerAccuracyUSec is not documented!-->
+
+    <!--property DefaultTimeoutStartUSec is not documented!-->
+
+    <!--property DefaultTimeoutStopUSec is not documented!-->
+
+    <!--property DefaultTimeoutAbortUSec is not documented!-->
+
+    <!--property DefaultRestartUSec is not documented!-->
+
+    <!--property DefaultStartLimitIntervalUSec is not documented!-->
+
+    <!--property DefaultStartLimitBurst is not documented!-->
+
+    <!--property DefaultCPUAccounting is not documented!-->
+
+    <!--property DefaultBlockIOAccounting is not documented!-->
+
+    <!--property DefaultMemoryAccounting is not documented!-->
+
+    <!--property DefaultTasksAccounting is not documented!-->
+
+    <!--property DefaultLimitCPU is not documented!-->
+
+    <!--property DefaultLimitCPUSoft is not documented!-->
+
+    <!--property DefaultLimitFSIZE is not documented!-->
+
+    <!--property DefaultLimitFSIZESoft is not documented!-->
+
+    <!--property DefaultLimitDATA is not documented!-->
+
+    <!--property DefaultLimitDATASoft is not documented!-->
+
+    <!--property DefaultLimitSTACK is not documented!-->
+
+    <!--property DefaultLimitSTACKSoft is not documented!-->
+
+    <!--property DefaultLimitCORE is not documented!-->
+
+    <!--property DefaultLimitCORESoft is not documented!-->
+
+    <!--property DefaultLimitRSS is not documented!-->
+
+    <!--property DefaultLimitRSSSoft is not documented!-->
+
+    <!--property DefaultLimitNOFILE is not documented!-->
+
+    <!--property DefaultLimitNOFILESoft is not documented!-->
+
+    <!--property DefaultLimitAS is not documented!-->
+
+    <!--property DefaultLimitASSoft is not documented!-->
+
+    <!--property DefaultLimitNPROC is not documented!-->
+
+    <!--property DefaultLimitNPROCSoft is not documented!-->
+
+    <!--property DefaultLimitMEMLOCK is not documented!-->
+
+    <!--property DefaultLimitMEMLOCKSoft is not documented!-->
+
+    <!--property DefaultLimitLOCKS is not documented!-->
+
+    <!--property DefaultLimitLOCKSSoft is not documented!-->
+
+    <!--property DefaultLimitSIGPENDING is not documented!-->
+
+    <!--property DefaultLimitSIGPENDINGSoft is not documented!-->
+
+    <!--property DefaultLimitMSGQUEUE is not documented!-->
+
+    <!--property DefaultLimitMSGQUEUESoft is not documented!-->
+
+    <!--property DefaultLimitNICE is not documented!-->
+
+    <!--property DefaultLimitNICESoft is not documented!-->
+
+    <!--property DefaultLimitRTPRIO is not documented!-->
+
+    <!--property DefaultLimitRTPRIOSoft is not documented!-->
+
+    <!--property DefaultLimitRTTIME is not documented!-->
+
+    <!--property DefaultLimitRTTIMESoft is not documented!-->
+
+    <!--property DefaultTasksMax is not documented!-->
+
+    <!--property TimerSlackNSec is not documented!-->
+
+    <!--property DefaultOOMPolicy is not documented!-->
+
+    <!--Autogenerated cross-references for systemd.directives, do not edit-->
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Manager"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Manager"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetUnit()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetUnitByPID()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetUnitByInvocationID()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetUnitByControlGroup()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="LoadUnit()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="StartUnit()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="StartUnitReplace()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="StopUnit()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ReloadUnit()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="RestartUnit()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="TryRestartUnit()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ReloadOrRestartUnit()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ReloadOrTryRestartUnit()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="EnqueueUnitJob()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="KillUnit()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="CleanUnit()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="FreezeUnit()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ThawUnit()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ResetFailedUnit()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetUnitProperties()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="RefUnit()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="UnrefUnit()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="StartTransientUnit()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetUnitProcesses()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="AttachProcessesToUnit()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="AbandonScope()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetJob()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetJobAfter()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetJobBefore()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="CancelJob()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ClearJobs()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ResetFailed()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ListUnits()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ListUnitsFiltered()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ListUnitsByPatterns()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ListUnitsByNames()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ListJobs()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Subscribe()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Unsubscribe()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Dump()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="DumpByFileDescriptor()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Reload()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Reexecute()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Exit()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Reboot()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="PowerOff()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Halt()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="KExec()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SwitchRoot()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetEnvironment()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="UnsetEnvironment()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="UnsetAndSetEnvironment()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ListUnitFiles()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ListUnitFilesByPatterns()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetUnitFileState()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="EnableUnitFiles()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="DisableUnitFiles()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ReenableUnitFiles()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="LinkUnitFiles()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="PresetUnitFiles()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="PresetUnitFilesWithMode()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="MaskUnitFiles()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="UnmaskUnitFiles()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="RevertUnitFiles()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetDefaultTarget()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetDefaultTarget()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="PresetAllUnitFiles()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="AddDependencyUnitFiles()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetUnitFileLinks()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetExitCode()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="LookupDynamicUserByName()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="LookupDynamicUserByUID()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetDynamicUsers()"/>
+
+    <variablelist class="dbus-signal" generated="True" extra-ref="UnitNew"/>
+
+    <variablelist class="dbus-signal" generated="True" extra-ref="UnitRemoved"/>
+
+    <variablelist class="dbus-signal" generated="True" extra-ref="JobNew"/>
+
+    <variablelist class="dbus-signal" generated="True" extra-ref="JobRemoved"/>
+
+    <variablelist class="dbus-signal" generated="True" extra-ref="StartupFinished"/>
+
+    <variablelist class="dbus-signal" generated="True" extra-ref="UnitFilesChanged"/>
+
+    <variablelist class="dbus-signal" generated="True" extra-ref="Reloading"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Version"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Features"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Virtualization"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Architecture"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Tainted"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="FirmwareTimestamp"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="FirmwareTimestampMonotonic"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LoaderTimestamp"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LoaderTimestampMonotonic"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="KernelTimestamp"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="KernelTimestampMonotonic"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="InitRDTimestamp"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="InitRDTimestampMonotonic"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="UserspaceTimestamp"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="UserspaceTimestampMonotonic"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="FinishTimestamp"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="FinishTimestampMonotonic"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SecurityStartTimestamp"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SecurityStartTimestampMonotonic"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SecurityFinishTimestamp"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SecurityFinishTimestampMonotonic"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="GeneratorsStartTimestamp"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="GeneratorsStartTimestampMonotonic"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="GeneratorsFinishTimestamp"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="GeneratorsFinishTimestampMonotonic"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="UnitsLoadStartTimestamp"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="UnitsLoadStartTimestampMonotonic"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="UnitsLoadFinishTimestamp"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="UnitsLoadFinishTimestampMonotonic"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="InitRDSecurityStartTimestamp"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="InitRDSecurityStartTimestampMonotonic"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="InitRDSecurityFinishTimestamp"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="InitRDSecurityFinishTimestampMonotonic"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="InitRDGeneratorsStartTimestamp"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="InitRDGeneratorsStartTimestampMonotonic"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="InitRDGeneratorsFinishTimestamp"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="InitRDGeneratorsFinishTimestampMonotonic"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="InitRDUnitsLoadStartTimestamp"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="InitRDUnitsLoadStartTimestampMonotonic"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="InitRDUnitsLoadFinishTimestamp"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="InitRDUnitsLoadFinishTimestampMonotonic"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LogLevel"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LogTarget"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="NNames"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="NFailedUnits"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="NJobs"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="NInstalledJobs"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="NFailedJobs"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Progress"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Environment"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ConfirmSpawn"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ShowStatus"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="UnitPath"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultStandardOutput"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultStandardError"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RuntimeWatchdogUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RebootWatchdogUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="KExecWatchdogUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ServiceWatchdogs"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ControlGroup"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SystemState"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ExitCode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultTimerAccuracyUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultTimeoutStartUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultTimeoutStopUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultTimeoutAbortUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultRestartUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultStartLimitIntervalUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultStartLimitBurst"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultCPUAccounting"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultBlockIOAccounting"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultMemoryAccounting"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultTasksAccounting"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultLimitCPU"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultLimitCPUSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultLimitFSIZE"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultLimitFSIZESoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultLimitDATA"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultLimitDATASoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultLimitSTACK"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultLimitSTACKSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultLimitCORE"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultLimitCORESoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultLimitRSS"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultLimitRSSSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultLimitNOFILE"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultLimitNOFILESoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultLimitAS"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultLimitASSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultLimitNPROC"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultLimitNPROCSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultLimitMEMLOCK"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultLimitMEMLOCKSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultLimitLOCKS"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultLimitLOCKSSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultLimitSIGPENDING"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultLimitSIGPENDINGSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultLimitMSGQUEUE"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultLimitMSGQUEUESoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultLimitNICE"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultLimitNICESoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultLimitRTPRIO"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultLimitRTPRIOSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultLimitRTTIME"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultLimitRTTIMESoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultTasksMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TimerSlackNSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultOOMPolicy"/>
+
+    <!--End of Autogenerated section-->
+
+    <refsect2>
+      <title>Methods</title>
+
+      <para>Note that many of the methods exist twice: once on the <interfacename>Manager</interfacename>
+      object and once on the respective unit objects. This is to optimize access times so that methods that
+      belong to unit objects do not have to be called with a resolved unit path, but can be called with only
+      the unit id, too.</para>
+
+      <para><function>GetUnit()</function> may be used to get the unit object path for a unit name. It takes
+      the unit name and returns the object path. If a unit has not been loaded yet by this name this method
+      will fail.</para>
+
+      <para><function>GetUnitByPID()</function> may be used to get the unit object path of the unit a process
+      ID belongs to. It takes a UNIX PID and returns the object path. The PID must refer to an existing system process.</para>
+
+      <para><function>LoadUnit()</function> is similar to <function>GetUnit()</function> but will load the
+      unit from disk if possible.</para>
+
+      <para><function>StartUnit()</function> enqueues a start job and possibly depending jobs. It takes the unit
+      to activate and a mode string as arguments. The mode needs to be one of <literal>replace</literal>,
+      <literal>fail</literal>, <literal>isolate</literal>, <literal>ignore-dependencies</literal>, or
+      <literal>ignore-requirements</literal>. If <literal>replace</literal>, the method will start the unit and
+      its dependencies, possibly replacing already queued jobs that conflict with it. If
+      <literal>fail</literal>, the method will start the unit and its dependencies, but will fail if this would
+      change an already queued job. If <literal>isolate</literal>, the method will start the unit in question
+      and terminate all units that aren't dependencies of it. If <literal>ignore-dependencies</literal>, it
+      will start a unit but ignore all its dependencies. If <literal>ignore-requirements</literal>, it will
+      start a unit but only ignore the requirement dependencies. It is not recommended to make use of the
+      latter two options. On completion, this method returns the newly created job object.</para>
+
+      <para><function>StartUnitReplace()</function> is similar to <function>StartUnit()</function> but
+      replaces a job that is queued for one unit by a job for another unit.</para>
+
+      <para><function>StopUnit()</function> is similar to <function>StartUnit()</function> but stops the
+      specified unit rather than starting it. Note that the <literal>isolate</literal> mode is invalid for this
+      method.</para>
+
+      <para><function>ReloadUnit()</function>, <function>RestartUnit()</function>,
+      <function>TryRestartUnit()</function>, <function>ReloadOrRestartUnit()</function>, or
+      <function>ReloadOrTryRestartUnit()</function> may be used to restart and/or reload a unit. These methods take
+      similar arguments as <function>StartUnit()</function>. Reloading is done only if the unit is already
+      running and fails otherwise. If a service is restarted that isn't running, it will be started unless
+      the "Try" flavor is used in which case a service that isn't running is not affected by the restart. The
+      "ReloadOrRestart" flavors attempt a reload if the unit supports it and use a restart otherwise.</para>
+
+      <para><function>KillUnit()</function> may be used to kill (i.e. send a signal to) all processes of a
+      unit. It takes the unit <varname>name</varname>, an enum <varname>who</varname> and a UNIX
+      <varname>signal</varname> number to send. The <varname>who</varname> enum is one of
+      <literal>main</literal>, <literal>control</literal> or <literal>all</literal>. If
+      <literal>main</literal>, only the main process of the unit is killed. If <literal>control</literal>, only
+      the control process of the unit is killed. If <literal>all</literal>, all processes are killed. A
+      <literal>control</literal> process is for example a process that is configured via
+      <varname>ExecStop=</varname> and is spawned in parallel to the main daemon process in order to shut it
+      down.</para>
+
+      <para><function>GetJob()</function> returns the job object path for a specific job, identified by its
+      id.</para>
+
+      <para><function>CancelJob()</function> cancels a specific job identified by its numeric ID. This
+      operation is also available in the <function>Cancel()</function> method of Job objects (see below) and
+      exists primarily to reduce the necessary round trips to execute this operation. Note that this will not
+      have any effect on jobs whose execution has already begun.</para>
+
+      <para><function>ClearJobs()</function> flushes the job queue, removing all jobs that are still
+      queued. Note that this does not have any effect on jobs whose execution has already begun. It only
+      flushes jobs that are queued and have not yet begun execution.</para>
+
+      <para><function>ResetFailedUnit()</function> resets the "failed" state of a specific unit.</para>
+
+      <para><function>ResetFailed()</function> resets the "failed" state of all units.</para>
+
+      <para><function>ListUnits()</function> returns an array of all currently loaded units. Note that
+      units may be known by multiple names at the same name, and hence there might be more unit names loaded
+      than actual units behind them. The array consists of structures with the following elements:
+      <itemizedlist>
+        <listitem><para>The primary unit name as string</para></listitem>
+
+        <listitem><para>The human readable description string</para></listitem>
+
+        <listitem><para>The load state (i.e. whether the unit file has been loaded
+        successfully)</para></listitem>
+
+        <listitem><para>The active state (i.e. whether the unit is currently started or
+        not)</para></listitem>
+
+        <listitem><para>The sub state (a more fine-grained version of the active state that is specific to
+        the unit type, which the active state is not)</para></listitem>
+
+        <listitem><para>A unit that is being followed in its state by this unit, if there is any, otherwise
+        the empty string.</para></listitem>
+
+        <listitem><para>The unit object path</para></listitem>
+
+        <listitem><para>If there is a job queued for the job unit, the numeric job id, 0
+        otherwise</para></listitem>
+
+        <listitem><para>The job type as string</para></listitem>
+
+        <listitem><para>The job object path</para></listitem>
+      </itemizedlist></para>
+
+      <para><function>ListJobs()</function> returns an array with all currently queued jobs. Returns an array
+      consisting of structures with the following elements:
+      <itemizedlist>
+        <listitem><para>The numeric job id</para></listitem>
+
+        <listitem><para>The primary unit name for this job</para></listitem>
+
+        <listitem><para>The job type as string</para></listitem>
+
+        <listitem><para>The job state as string</para></listitem>
+
+        <listitem><para>The job object path</para></listitem>
+
+        <listitem><para>The unit object path</para></listitem>
+      </itemizedlist></para>
+
+      <para><function>Subscribe()</function> enables most bus signals to be sent out. Clients which are
+      interested in signals need to call this method. Signals are only sent out if at least one client
+      invoked this method. <function>Unsubscribe()</function> reverts the signal subscription that
+      <function>Subscribe()</function> implements. It is not necessary to invoke
+      <function>Unsubscribe()</function> as clients are tracked. Signals are no longer sent out as soon as
+      all clients which previously asked for <function>Subscribe()</function> either closed their connection
+      to the bus or invoked <function>Unsubscribe()</function>.</para>
+
+      <para><function>Reload()</function> may be invoked to reload all unit files.</para>
+
+      <para><function>Reexecute()</function> may be invoked to reexecute the main manager process. It will
+      serialize its state, reexecute, and deserizalize the state again. This is useful for upgrades and is a
+      more comprehensive version of <function>Reload()</function>.</para>
+
+      <para><function>Exit()</function> may be invoked to ask the manager to exit. This is not available for
+      the system manager and is useful only for user session managers.</para>
+
+      <para><function>Reboot()</function>, <function>PowerOff()</function>, <function>Halt()</function>, or
+      <function>KExec()</function> may be used to ask for immediate reboot, powering down, halt or kexec
+      based reboot of the system. Note that this does not shut down any services and immediately transitions
+      into the reboot process. These functions are normally only called as the last step of shutdown and should
+      not be called directly. To shut down the machine, it is generally a better idea to invoke
+      <function>Reboot()</function> or <function>PowerOff()</function> on the
+      <filename>systemd-logind</filename> manager object; see
+      <citerefentry><refentrytitle>org.freedesktop.login1</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+      for more information.</para>
+
+      <para><function>SwitchRoot()</function> may be used to transition to a new root directory. This is
+      intended to be used by initial RAM disks. The method takes two arguments: the new root directory (which
+      needs to be specified) and an init binary path (which may be left empty, in which case it is
+      automatically searched for). The state of the system manager will be serialized before the
+      transition. After the transition, the manager binary on the main system is invoked and replaces the old
+      PID 1. All state will then be deserialized.</para>
+
+      <para><function>SetEnvironment()</function> may be used to alter the environment block that is passed
+      to all spawned processes. It takes a string array of environment variable assignments. Any previously set
+      environment variables will be overridden.</para>
+
+      <para><function>UnsetEnvironment()</function> may be used to unset environment variables. It takes a
+      string array of environment variable names. All variables specified will be unset (if they have been
+      set previously) and no longer be passed to all spawned processes. This method has no effect for variables
+      that were previously not set, but will not fail in that case.</para>
+
+      <para><function>UnsetAndSetEnvironment()</function> is a combination of
+      <function>UnsetEnvironment()</function> and <function>SetEnvironment()</function>. It takes two
+      lists. The first list contains variables to unset, the second one contains assignments to set. If a
+      variable is listed in both, the variable is set after this method returns, i.e. the set list overrides the
+      unset list.</para>
+
+      <para><function>ListUnitFiles()</function> returns an array of unit names and their enablement
+      status. Note that <function>ListUnit()</function> returns a list of units currently loaded into memory,
+      while <function>ListUnitFiles()</function> returns a list of unit <emphasis>files</emphasis> that were
+      found on disk. Note that while most units are read directly from a unit file with the same name, some
+      units are not backed by files and some files (templates) cannot directly be loaded as units but need
+      to be instantiated instead.</para>
+
+      <para><function>GetUnitFileState()</function> returns the current enablement status of a specific unit
+      file.</para>
+
+      <para><function>EnableUnitFiles()</function> may be used to enable one or more units in the system (by
+      creating symlinks to them in <filename>/etc</filename> or <filename>/run</filename>). It takes a list
+      of unit files to enable (either just file names or full absolute paths if the unit files are residing
+      outside the usual unit search paths) and two booleans: the first controls whether the unit shall be
+      enabled for runtime only (true, <filename>/run</filename>), or persistently (false,
+      <filename>/etc</filename>). The second one controls whether symlinks pointing to other units shall be
+      replaced if necessary. This method returns one boolean and an array of the changes made. The boolean
+      signals whether the unit files contained any enablement information (i.e. an [Install]) section. The
+      changes array consists of structures with three strings: the type of the change (one of
+      <literal>symlink</literal> or <literal>unlink</literal>), the file name of the symlink and the
+      destination of the symlink. Note that most of the following calls return a changes list in the same
+      format.</para>
+
+      <para>Similarly, <function>DisableUnitFiles()</function> disables one or more units in the system,
+      i.e. removes all symlinks to them in <filename>/etc</filename> and <filename>/run</filename>.</para>
+
+      <para>Similarly, <function>ReenableUnitFiles()</function> applies the changes to one or more units that
+      would result from disabling and enabling the unit quickly one after the other in an atomic
+      fashion. This is useful to apply updated [Install] information contained in unit files.</para>
+
+      <para>Similarly, <function>LinkUnitFiles()</function> links unit files (that are located outside of the
+      usual unit search paths) into the unit search path.</para>
+
+      <para>Similarly, <function>PresetUnitFiles()</function> enables/disables one or more unit files
+      according to the preset policy. See
+      <citerefentry><refentrytitle>systemd.preset</refentrytitle><manvolnum>7</manvolnum></citerefentry> for more
+      information.</para>
+
+      <para>Similarly, <function>MaskUnitFiles()</function> masks unit files and
+      <function>UnmaskUnitFiles()</function> unmasks them again.</para>
+
+      <para><function>SetDefaultTarget()</function> changes the <filename>default.target</filename> link. See
+      <citerefentry><refentrytitle>bootup</refentrytitle><manvolnum>7</manvolnum></citerefentry> for more
+      information.</para>
+
+      <para><function>GetDefaultTarget()</function> retrieves the name of the unit to which
+      <filename>default.target</filename> is aliased.</para>
+
+      <para><function>SetUnitProperties()</function> may be used to modify certain unit properties at
+      runtime. Not all properties may be changed at runtime, but many resource management settings (primarily
+      those listed in
+      <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>)
+      may. The changes are applied instantly and stored on disk for future boots, unless
+      <varname>runtime</varname> is true, in which case the settings only apply until the next
+      reboot. <varname>name</varname> is the name of the unit to modify. <varname>properties</varname> are
+      the settings to set, encoded as an array of property name and value pairs. Note that this is not a
+      dictionary! Also note that when setting array properties with this method usually results in appending to
+      the pre-configured array. To reset the configured arrays, set the property to an empty array first and
+      then append to it.</para>
+
+      <para><function>StartTransientUnit()</function> may be used to create and start a transient unit which
+      will be released as soon as it is not running or referenced anymore or the system is
+      rebooted. <varname>name</varname> is the unit name including its suffix and must be
+      unique. <varname>mode</varname> is the same as in <function>StartUnit()</function>,
+      <varname>properties</varname> contains properties of the unit, specified like in
+      <function>SetUnitProperties()</function>. <varname>aux</varname> is currently unused and should be
+      passed as an empty array. See the
+      <ulink url="http://www.freedesktop.org/wiki/Software/systemd/ControlGroupInterface/">New Control Group
+      Interface</ulink> for more information how to make use of this functionality for resource control
+      purposes.</para>
+    </refsect2>
+
+    <refsect2>
+      <title>Signals</title>
+
+      <para>Note that most signals are sent out only after <function>Subscribe()</function> has been invoked
+      by at least one client. Make sure to invoke this method when subscribing to these signals!</para>
+
+      <para><function>UnitNew()</function> and <function>UnitRemoved()</function> are sent out each time a
+      new unit is loaded or unloaded. Note that this has little to do with whether a unit is available on
+      disk or not, and simply reflects the units that are currently loaded into memory. The signals take two
+      parameters: the primary unit name and the object path.</para>
+
+      <para><function>JobNew()</function> and <function>JobRemoved()</function> are sent out each time a new
+      job is queued or dequeued. Both signals take the numeric job ID, the bus path and the primary unit name
+      for this job as arguments. <function>JobRemoved()</function> also includes a result string which is one
+      of <literal>done</literal>, <literal>canceled</literal>, <literal>timeout</literal>,
+      <literal>failed</literal>, <literal>dependency</literal>, or
+      <literal>skipped</literal>. <literal>done</literal> indicates successful execution of a
+      job. <literal>canceled</literal> indicates that a job has been canceled (via
+      <function>CancelJob()</function> above) before it finished execution (this doesn't necessarily mean
+      though that the job operation is actually cancelled too, see above). <literal>timeout</literal>
+      indicates that the job timeout was reached. <literal>failed</literal> indicates that the job
+      failed. <literal>dependency</literal> indicates that a job this job depended on failed and the job hence
+      was removed as well. <literal>skipped</literal> indicates that a job was skipped because
+      it didn't apply to the unit's current state.</para>
+
+      <para><function>StartupFinished()</function> is sent out when startup finishes. It carries six
+      microsecond timespan values, each indicating how much boot time has been spent in the firmware (if
+      known), in the boot loader (if known), in the kernel initialization phase, in the initrd (if known), in
+      userspace and in total. These values may also be calculated from the
+      <varname>FirmwareTimestampMonotonic</varname>, <varname>LoaderTimestampMonotonic</varname>,
+      <varname>InitRDTimestampMonotonic</varname>, <varname>UserspaceTimestampMonotonic</varname>, and
+      <varname>FinishTimestampMonotonic</varname> properties (see below).</para>
+
+      <para><function>UnitFilesChanged()</function> is sent out each time the list of enabled or masked unit
+      files on disk have changed.</para>
+
+      <para><function>Reloading()</function> is sent out immediately before a daemon reload is done (with the
+      boolean parameter set to True) and after a daemon reload is completed (with the boolean parameter set
+      to False). This may be used by UIs to optimize UI updates.</para>
+    </refsect2>
+
+    <refsect2>
+      <title>Properties</title>
+
+      <para>Most properties simply reflect the respective options in
+      <filename>/etc/systemd/system.conf</filename> and the kernel command line.</para>
+
+      <para>The others:</para>
+
+      <para><varname>Version</varname> encodes the version string of the running systemd instance. Note that
+      the version string is purely informational. It should not be parsed and one may not assume the version to
+      be formatted in any particular way. We take the liberty to change the versioning scheme at any time and
+      it is not part of the public API.</para>
+
+      <para><varname>Features</varname> encodes the features that have been enabled and disabled for this
+      build. Enabled options are prefixed with +, disabled options with -.</para>
+
+      <para><varname>Tainted</varname> encodes a couple of taint flags as a colon-separated list. When
+      systemd detects it is running on a system with certain problems, it will set an appropriate taint
+      flag. Taints may be used to lower the chance of bogus bug reports. The following taints are currently
+      known: <literal>split-usr</literal>, <literal>mtab-not-symlink</literal>,
+      <literal>cgroups-missing</literal>, <literal>local-hwclock</literal>. <literal>split-usr</literal> is
+      set if <filename>/usr</filename> is not pre-mounted when systemd is first invoked. See
+      <ulink url="http://freedesktop.org/wiki/Software/systemd/separate-usr-is-broken">
+      Booting Without /usr is Broken</ulink>
+      for details why this is bad. <literal>mtab-not-symlink</literal> indicates that
+      <filename>/etc/mtab</filename> is not a symlink to <filename>/proc/self/mounts</filename> as
+      required. <literal>cgroups-missing</literal> indicates that control groups have not been enabled in the
+      kernel. <literal>local-hwclock</literal> indicates that the local RTC is configured to be in local time
+      rather than UTC.</para>
+
+      <para><varname>FirmwareTimestamp</varname>, <varname>FirmwareTimestampMonotonic</varname>,
+      <varname>LoaderTimestamp</varname>, <varname>LoaderTimestampMonotonic</varname>,
+      <varname>KernelTimestamp</varname>, <varname>KernelTimestampMonotonic</varname>,
+      <varname>InitRDTimestamp</varname>, <varname>InitRDTimestampMonotonic</varname>,
+      <varname>UserspaceTimestamp</varname>, <varname>UserspaceTimestampMonotonic</varname>,
+      <varname>FinishTimestamp</varname>, and <varname>FinishTimestampMonotonic</varname> encode
+      <constant>CLOCK_REALTIME</constant> and <constant>CLOCK_MONOTONIC</constant> microsecond timestamps
+      taken when the firmware first began execution, when the boot loader first began execution, when the
+      kernel first began execution, when the initrd first began execution, when the main systemd instance
+      began execution and finally, when all queued startup jobs finished execution. These values are useful
+      for determining boot-time performance. Note that as monotonic time begins with the kernel startup, the
+      <varname>KernelTimestampMonotonic</varname> timestamp will always be 0 and
+      <varname>FirmwareTimestampMonotonic</varname> and <varname>LoaderTimestampMonotonic</varname> are to
+      be read as negative values. Also, not all fields are always available, depending on the used firmware,
+      boot loader or initrd implementation. In these cases the respective pairs of timestamps are both 0,
+      indicating that no data is available.</para>
+
+      <para>Similarly, the <varname>SecurityStartTimestamp</varname>,
+      <varname>GeneratorsStartTimestamp</varname> and <varname>LoadUnitTimestamp</varname> (as well as their
+      monotonic and stop counterparts) expose performance data for uploading the security policies to the
+      kernel (such as the SELinux, IMA, or SMACK policies), for running the generator tools and for loading
+      the unit files.</para>
+
+      <para><varname>NNames</varname> encodes how many unit names are currently known. This only includes
+      names of units that are currently loaded and can be more than the amount of actually loaded units since
+      units may have more than one name.</para>
+
+      <para><varname>NJobs</varname> encodes how many jobs are currently queued.</para>
+
+      <para><varname>NInstalledJobs</varname> encodes how many jobs have ever been queued in total.</para>
+
+      <para><varname>NFailedJobs</varname> encodes how many jobs have ever failed in total.</para>
+
+      <para><varname>Progress</varname> encodes boot progress as a floating point value between 0.0 and
+      1.0. This value begins at 0.0 at early-boot and ends at 1.0 when boot is finished and is based on the
+      number of executed and queued jobs. After startup, this field is always 1.0 indicating a finished
+      boot.</para>
+
+      <para><varname>Environment</varname> encodes the environment block passed to all executed services. It
+      may be altered with bus calls such as <function>SetEnvironment()</function> (see above).</para>
+
+      <para><varname>UnitPath</varname> encodes the currently active unit file search path. It is an array of
+      file system paths encoded as strings.</para>
+
+      <para><varname>Virtualization</varname> contains a short ID string describing the virtualization
+      technology the system runs in. On bare-metal hardware this is the empty string. Otherwise, it contains
+      an identifier such as <literal>kvm</literal>, <literal>vmware</literal> and so on. For a full list of
+      IDs see
+      <citerefentry><refentrytitle>systemd-detect-virt</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
+      Note that only the "innermost" virtualization technology is exported here. This detects both
+      full-machine virtualizations (VMs) and shared-kernel virtualization (containers).</para>
+
+      <para><varname>Architecture</varname> contains a short ID string describing the architecture the
+      systemd instance is running on. This follows the same vocabulary as
+      <varname>ConditionArchitectures=</varname>.</para>
+
+      <para><varname>ControlGroup</varname> contains the root control group path of this system manager. Note
+      that the root path is encoded as the empty string here (not as <literal>/</literal>!), so that it can be
+      appended to <filename>/sys/fs/cgroup/systemd</filename> easily. This value will be set to the empty
+      string for the host instance and some other string for container instances.</para>
+    </refsect2>
+
+    <refsect2>
+      <title>Security</title>
+
+      <para>Read access is generally granted to all clients. Additionally, for unprivileged clients, some
+      operations are allowed through the polkit privilege system. Operations which modify unit state
+      (<function>StartUnit()</function>, <function>StopUnit()</function>, <function>KillUnit()</function>,
+      <function>RestartUnit()</function> and similar, <function>SetProperty()</function>) require
+      <interfacename>org.freedesktop.systemd1.manage-units</interfacename>. Operations which modify unit file
+      enablement state (<function>EnableUnitFiles()</function>, <function>DisableUnitFiles()</function>,
+      <function>ReenableUnitFiles()</function>, <function>LinkUnitFiles()</function>,
+      <function>PresetUnitFiles</function>, <function>MaskUnitFiles</function>, and similar) require
+      <interfacename>org.freedesktop.systemd1.manage-unit-files</interfacename>. Operations which modify the
+      exported environment (<function>SetEnvironment()</function>, <function>UnsetEnvironment()</function>,
+      <function>UnsetAndSetEnvironment()</function>) require
+      <interfacename>org.freedesktop.systemd1.set-environment</interfacename>. <function>Reload()</function>
+      and <function>Reexecute()</function> require
+      <interfacename>org.freedesktop.systemd1.reload-daemon</interfacename>.
+      </para>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>Unit Objects</title>
+
+    <programlisting executable="systemd" node="/org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice" interface="org.freedesktop.systemd1.Unit">
+node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
+  interface org.freedesktop.systemd1.Unit {
+    methods:
+      Start(in  s mode,
+            out o job);
+      Stop(in  s mode,
+           out o job);
+      Reload(in  s mode,
+             out o job);
+      Restart(in  s mode,
+              out o job);
+      TryRestart(in  s mode,
+                 out o job);
+      ReloadOrRestart(in  s mode,
+                      out o job);
+      ReloadOrTryRestart(in  s mode,
+                         out o job);
+      EnqueueJob(in  s job_type,
+                 in  s job_mode,
+                 out u job_id,
+                 out o job_path,
+                 out s unit_id,
+                 out o unit_path,
+                 out s job_type,
+                 out a(uosos) affected_jobs);
+      Kill(in  s whom,
+           in  i signal);
+      ResetFailed();
+      SetProperties(in  b runtime,
+                    in  a(sv) properties);
+      Ref();
+      Unref();
+      Clean(in  as mask);
+      Freeze();
+      Thaw();
+    properties:
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s Id = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as Names = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly s Following = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as Requires = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as Requisite = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as Wants = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as BindsTo = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as PartOf = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as RequiredBy = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as RequisiteOf = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as WantedBy = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as BoundBy = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as ConsistsOf = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as Conflicts = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as ConflictedBy = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as Before = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as After = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as OnFailure = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as Triggers = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as TriggeredBy = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as PropagatesReloadTo = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as ReloadPropagatedFrom = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as JoinsNamespaceOf = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as RequiresMountsFor = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as Documentation = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s Description = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s LoadState = '...';
+      readonly s ActiveState = '...';
+      readonly s FreezerState = '...';
+      readonly s SubState = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s FragmentPath = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s SourcePath = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as DropInPaths = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly s UnitFileState = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly s UnitFilePreset = '...';
+      readonly t StateChangeTimestamp = ...;
+      readonly t StateChangeTimestampMonotonic = ...;
+      readonly t InactiveExitTimestamp = ...;
+      readonly t InactiveExitTimestampMonotonic = ...;
+      readonly t ActiveEnterTimestamp = ...;
+      readonly t ActiveEnterTimestampMonotonic = ...;
+      readonly t ActiveExitTimestamp = ...;
+      readonly t ActiveExitTimestampMonotonic = ...;
+      readonly t InactiveEnterTimestamp = ...;
+      readonly t InactiveEnterTimestampMonotonic = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b CanStart = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b CanStop = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b CanReload = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b CanIsolate = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as CanClean = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b CanFreeze = ...;
+      readonly (uo) Job = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b StopWhenUnneeded = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b RefuseManualStart = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b RefuseManualStop = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b AllowIsolate = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b DefaultDependencies = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s OnFailureJobMode = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b IgnoreOnIsolate = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b NeedDaemonReload = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t JobTimeoutUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t JobRunningTimeoutUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s JobTimeoutAction = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s JobTimeoutRebootArgument = '...';
+      readonly b ConditionResult = ...;
+      readonly b AssertResult = ...;
+      readonly t ConditionTimestamp = ...;
+      readonly t ConditionTimestampMonotonic = ...;
+      readonly t AssertTimestamp = ...;
+      readonly t AssertTimestampMonotonic = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates")
+      readonly a(sbbsi) Conditions = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates")
+      readonly a(sbbsi) Asserts = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly (ss) LoadError = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b Transient = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b Perpetual = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t StartLimitIntervalUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u StartLimitBurst = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s StartLimitAction = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s FailureAction = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i FailureActionExitStatus = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s SuccessAction = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i SuccessActionExitStatus = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s RebootArgument = '...';
+      readonly ay InvocationID = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s CollectMode = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly as Refs = ['...', ...];
+  };
+  interface org.freedesktop.DBus.Peer { ... };
+  interface org.freedesktop.DBus.Introspectable { ... };
+  interface org.freedesktop.DBus.Properties { ... };
+};
+    </programlisting>
+
+    <!--method EnqueueJob is not documented!-->
+
+    <!--method Ref is not documented!-->
+
+    <!--method Unref is not documented!-->
+
+    <!--method Clean is not documented!-->
+
+    <!--method Freeze is not documented!-->
+
+    <!--method Thaw is not documented!-->
+
+    <!--property PartOf is not documented!-->
+
+    <!--property RequisiteOf is not documented!-->
+
+    <!--property ConsistsOf is not documented!-->
+
+    <!--property ReloadPropagatedFrom is not documented!-->
+
+    <!--property JoinsNamespaceOf is not documented!-->
+
+    <!--property FreezerState is not documented!-->
+
+    <!--property DropInPaths is not documented!-->
+
+    <!--property UnitFilePreset is not documented!-->
+
+    <!--property StateChangeTimestamp is not documented!-->
+
+    <!--property StateChangeTimestampMonotonic is not documented!-->
+
+    <!--property CanClean is not documented!-->
+
+    <!--property CanFreeze is not documented!-->
+
+    <!--property OnFailureJobMode is not documented!-->
+
+    <!--property JobRunningTimeoutUSec is not documented!-->
+
+    <!--property JobTimeoutAction is not documented!-->
+
+    <!--property JobTimeoutRebootArgument is not documented!-->
+
+    <!--property AssertResult is not documented!-->
+
+    <!--property AssertTimestamp is not documented!-->
+
+    <!--property AssertTimestampMonotonic is not documented!-->
+
+    <!--property Asserts is not documented!-->
+
+    <!--property Perpetual is not documented!-->
+
+    <!--property StartLimitIntervalUSec is not documented!-->
+
+    <!--property StartLimitAction is not documented!-->
+
+    <!--property FailureAction is not documented!-->
+
+    <!--property FailureActionExitStatus is not documented!-->
+
+    <!--property SuccessAction is not documented!-->
+
+    <!--property SuccessActionExitStatus is not documented!-->
+
+    <!--property RebootArgument is not documented!-->
+
+    <!--property InvocationID is not documented!-->
+
+    <!--property CollectMode is not documented!-->
+
+    <!--property Refs is not documented!-->
+
+    <!--Autogenerated cross-references for systemd.directives, do not edit-->
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Unit"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Unit"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Start()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Stop()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Reload()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Restart()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="TryRestart()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ReloadOrRestart()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ReloadOrTryRestart()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="EnqueueJob()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Kill()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ResetFailed()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetProperties()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Ref()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Unref()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Clean()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Freeze()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Thaw()"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Id"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Names"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Following"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Requires"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Requisite"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Wants"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BindsTo"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="PartOf"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RequiredBy"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RequisiteOf"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="WantedBy"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BoundBy"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ConsistsOf"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Conflicts"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ConflictedBy"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Before"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="After"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="OnFailure"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Triggers"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TriggeredBy"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="PropagatesReloadTo"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ReloadPropagatedFrom"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="JoinsNamespaceOf"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RequiresMountsFor"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Documentation"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Description"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LoadState"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ActiveState"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="FreezerState"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SubState"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="FragmentPath"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SourcePath"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DropInPaths"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="UnitFileState"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="UnitFilePreset"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StateChangeTimestamp"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StateChangeTimestampMonotonic"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="InactiveExitTimestamp"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="InactiveExitTimestampMonotonic"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ActiveEnterTimestamp"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ActiveEnterTimestampMonotonic"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ActiveExitTimestamp"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ActiveExitTimestampMonotonic"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="InactiveEnterTimestamp"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="InactiveEnterTimestampMonotonic"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CanStart"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CanStop"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CanReload"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CanIsolate"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CanClean"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CanFreeze"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Job"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StopWhenUnneeded"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RefuseManualStart"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RefuseManualStop"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="AllowIsolate"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultDependencies"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="OnFailureJobMode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IgnoreOnIsolate"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="NeedDaemonReload"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="JobTimeoutUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="JobRunningTimeoutUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="JobTimeoutAction"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="JobTimeoutRebootArgument"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ConditionResult"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="AssertResult"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ConditionTimestamp"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ConditionTimestampMonotonic"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="AssertTimestamp"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="AssertTimestampMonotonic"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Conditions"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Asserts"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LoadError"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Transient"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Perpetual"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StartLimitIntervalUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StartLimitBurst"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StartLimitAction"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="FailureAction"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="FailureActionExitStatus"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SuccessAction"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SuccessActionExitStatus"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RebootArgument"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="InvocationID"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CollectMode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Refs"/>
+
+    <!--End of Autogenerated section-->
+
+    <refsect2>
+      <title>Methods</title>
+
+      <para><function>Start()</function>, <function>Stop()</function>, <function>Reload()</function>,
+      <function>Restart()</function>, <function>TryRestart()</function>,
+      <function>ReloadOrRestart()</function>, <function>ReloadOrTryRestart()</function>,
+      <function>Kill()</function>, <function>ResetFailed()</function>, and
+      <function>SetProperties()</function> implement the same operation as the respective methods on the
+      <interfacename>Manager</interfacename> object (see above). However, these methods operate on the unit
+      object and hence do not take a unit name parameter. Invoking the methods directly on the Manager
+      object has the advantage of not requiring a <function>GetUnit()</function> call to get the unit object
+      for a specific unit name. Calling the methods on the Manager object is hence a round trip
+      optimization.</para>
+    </refsect2>
+
+    <refsect2>
+      <title>Properties</title>
+
+      <para><varname>Id</varname> contains the primary name of the unit.</para>
+
+      <para><varname>Names</varname> contains all names of the unit, including the primary name that is also
+      exposed in <varname>Id</varname>.</para>
+
+      <para><varname>Following</varname> either contains the empty string or contains the name of another
+      unit that this unit follows in state. This is used for some device units which reflect the unit state
+      machine of another unit, and which other unit this is might possibly change.</para>
+
+      <para><varname>Requires</varname>, <varname>RequiresOverridable</varname>,
+      <varname>Requisite</varname>, <varname>RequisiteOverridable</varname>, <varname>Wants</varname>,
+      <varname>BindsTo</varname>, <varname>RequiredBy</varname>, <varname>RequiredByOverridable</varname>,
+      <varname>WantedBy</varname>, <varname>BoundBy</varname>, <varname>Conflicts</varname>,
+      <varname>ConflictedBy</varname>, <varname>Before</varname>, <varname>After</varname>,
+      <varname>OnFailure</varname>, <varname>Triggers</varname>, <varname>TriggeredBy</varname>,
+      <varname>PropagatesReloadTo</varname>, and <varname>RequiresMountsFor</varname> contain arrays which encode
+      the dependencies and their inverse dependencies (where this applies) as configured in the unit file or
+      determined automatically.</para>
+
+      <para><varname>Description</varname> contains the human readable description string for the
+      unit.</para>
+
+      <para><varname>SourcePath</varname> contains the path to a configuration file this unit is
+      automatically generated from in case it is not a native unit (in which case it contains the empty
+      string). For example, all mount units generated from <filename>/etc/fstab</filename> have this field
+      set to <filename>/etc/fstab</filename>.</para>
+
+      <para><varname>Documentation</varname> contains a string array with URLs of documentation for this
+      unit.</para>
+
+      <para><varname>LoadState</varname> contains a state value that reflects whether the configuration file
+      of this unit has been loaded. The following states are currently defined: <literal>loaded</literal>,
+      <literal>error</literal>, and <literal>masked</literal>. <literal>loaded</literal> indicates that the
+      configuration was successfully loaded. <literal>error</literal> indicates that the configuration failed
+      to load. The <varname>LoadError</varname> field (see below) contains information about the cause of
+      this failure. <literal>masked</literal> indicates that the unit is currently masked out (i.e. symlinked
+      to <filename>/dev/null</filename> or empty). Note that the <varname>LoadState</varname> is fully
+      orthogonal to the <varname>ActiveState</varname> (see below) as units without valid loaded
+      configuration might be active (because configuration might have been reloaded at a time where a unit
+      was already active).</para>
+
+      <para><varname>ActiveState</varname> contains a state value that reflects whether the unit is currently
+      active or not. The following states are currently defined: <literal>active</literal>,
+      <literal>reloading</literal>, <literal>inactive</literal>, <literal>failed</literal>,
+      <literal>activating</literal>, and <literal>deactivating</literal>. <literal>active</literal> indicates
+      that unit is active (obviously...). <literal>reloading</literal> indicates that the unit is active and
+      currently reloading its configuration. <literal>inactive</literal> indicates that it is inactive and
+      the previous run was successful or no previous run has taken place yet. <literal>failed</literal>
+      indicates that it is inactive and the previous run was not successful (more information about the
+      reason for this is available on the unit type specific interfaces, for example for services in the
+      <varname>Result</varname> property, see below). <literal>activating</literal> indicates that the unit
+      has previously been inactive but is currently in the process of entering an active state. Conversely
+      <literal>deactivating</literal> indicates that the unit is currently in the process of
+      deactivation.</para>
+
+      <para><varname>SubState</varname> encodes states of the same state machine that
+      <varname>ActiveState</varname> covers, but knows more fine-grained states that are
+      unit-type-specific. Where <varname>ActiveState</varname> only covers six high-level states,
+      <varname>SubState</varname> covers possibly many more low-level unit-type-specific states that are
+      mapped to the six high-level states. Note that multiple low-level states might map to the same
+      high-level state, but not vice versa. Not all high-level states have low-level counterparts on all unit
+      types. At this point the low-level states are not documented here, and are more likely to be extended
+      later on than the common high-level states explained above.</para>
+
+      <para><varname>FragmentPath</varname> contains the unit file path this unit was read from, if there is
+      one (if not, it contains the empty string).</para>
+
+      <para><varname>UnitFileState</varname> encodes the install state of the unit file of
+      <varname>FragmentPath</varname>. It currently knows the following states: <literal>enabled</literal>,
+      <literal>enabled-runtime</literal>, <literal>linked</literal>, <literal>linked-runtime</literal>,
+      <literal>masked</literal>, <literal>masked-runtime</literal>, <literal>static</literal>,
+      <literal>disabled</literal>, and <literal>invalid</literal>. <literal>enabled</literal> indicates that a
+      unit file is permanently enabled. <literal>enable-runtime</literal> indicates the unit file is only
+      temporarily enabled and will no longer be enabled after a reboot (that means, it is enabled via
+      <filename>/run</filename> symlinks, rather than <filename>/etc</filename>). <literal>linked</literal>
+      indicates that a unit is linked into <filename>/etc</filename> permanently. <literal>linked-runtime</literal>
+      indicates that a unit is linked into <filename>/run</filename> temporarily (until the next
+      reboot). <literal>masked</literal> indicates that the unit file is masked permanently.
+      <literal>masked-runtime</literal> indicates that it is masked in <filename>/run</filename> temporarily
+      (until the next reboot). <literal>static</literal> indicates that the unit is statically enabled, i.e.
+      always enabled and doesn't need to be enabled explicitly. <literal>invalid</literal> indicates that it
+      could not be determined whether the unit file is enabled.</para>
+
+      <para><varname>InactiveExitTimestamp</varname>, <varname>InactiveExitTimestampMonotonic</varname>,
+      <varname>ActiveEnterTimestamp</varname>, <varname>ActiveEnterTimestampMonotonic</varname>,
+      <varname>ActiveExitTimestamp</varname>, <varname>ActiveExitTimestampMonotonic</varname>,
+      <varname>InactiveEnterTimestamp</varname>, and <varname>InactiveEnterTimestampMonotonic</varname>
+      contain <constant>CLOCK_REALTIME</constant> and <constant>CLOCK_MONOTONIC</constant> 64-bit microsecond
+      timestamps of the last time a unit left the inactive state, entered the active state, exited the active
+      state, or entered an inactive state. These are the points in time where the unit transitioned
+      <literal>inactive</literal>/<literal>failed</literal> → <literal>activating</literal>,
+      <literal>activating</literal> → <literal>active</literal>, <literal>active</literal> →
+      <literal>deactivating</literal>, and finally <literal>deactivating</literal> →
+      <literal>inactive</literal>/<literal>failed</literal>. The fields are 0 in case such a transition has
+      not yet been recorded on this boot.</para>
+
+      <para><varname>CanStart</varname>, <varname>CanStop</varname>, and <varname>CanReload</varname> encode
+      as booleans whether the unit supports the start, stop or reload operations. Even if a unit supports
+      such an operation, the client might not necessary have the necessary privileges to execute them.</para>
+
+      <para><varname>CanIsolate</varname> encodes as a boolean whether the unit may be started in isolation
+      mode.</para>
+
+      <para><varname>Job</varname> encodes the job ID and job object path of the job currently scheduled or
+      executed for this unit, if there is any. If no job is scheduled or executed, the job id field will be
+      0.</para>
+
+      <para><varname>StopWhenUnneeded</varname>, <varname>RefuseManualStart</varname>,
+      <varname>RefuseManualStop</varname>, <varname>AllowIsolate</varname>,
+      <varname>DefaultDependencies</varname>, <varname>OnFailureIsolate</varname>,
+      <varname>IgnoreOnIsolate</varname>, <varname>IgnoreOnSnapshot</varname> map directly to the
+      corresponding configuration booleans in the unit file.</para>
+
+      <para><varname>DefaultControlGroup</varname> contains the main control group of this unit as a
+      string. This refers to a group in systemd's own <literal>name=systemd</literal> hierarchy, which
+      systemd uses to watch and manipulate the unit and all its processes.</para>
+
+      <para><varname>NeedDaemonReload</varname> is a boolean that indicates whether the configuration file
+      this unit is loaded from (i.e. <varname>FragmentPath</varname> or <varname>SourcePath</varname>) has
+      changed since the configuration was read and hence whether a configuration reload is
+      recommended.</para>
+
+      <para><varname>JobTimeoutUSec</varname> maps directly to the corresponding configuration setting in the
+      unit file.</para>
+
+      <para><varname>ConditionTimestamp</varname> and <varname>ConditionTimestampMonotonic</varname> contain
+      the <constant>CLOCK_REALTIME</constant>/<constant>CLOCK_MONOTONIC</constant> microsecond timestamps of
+      the last time the configured conditions of the unit have been checked or 0 if they have never been
+      checked. Conditions are checked when a unit is requested to start.</para>
+
+      <para><varname>ConditionResult</varname> contains the condition result of the last time the configured
+      conditions of this unit were checked. </para>
+
+      <para><varname>Conditions</varname> contains all configured conditions of the unit. For each condition,
+      five fields are given: condition type (e.g. <varname>ConditionPathExists</varname>), whether the
+      condition is a trigger condition, whether the condition is reversed, the right hand side of the
+      condition (e.g. the path in case of <varname>ConditionPathExists</varname>), and the status. The status
+      can be 0, in which case the condition hasn't been checked yet, a positive value, in which case the
+      condition passed, or a negative value, in which case the condition failed. Currently only 0, +1, and -1
+      are used, but additional values may be used in the future, retaining the meaning of
+      zero/positive/negative values.</para>
+
+      <para><varname>LoadError</varname> contains a pair of strings. If the unit failed to load (as encoded
+      in <varname>LoadState</varname>, see above), then this will include a D-Bus error pair consisting of
+      the error ID and an explanatory human readable string of what happened. If it loaded successfully, this
+      will be a pair of empty strings.</para>
+
+      <para><varname>Transient</varname> contains a boolean that indicates whether the unit was created as a
+      transient unit (i.e. via <function>CreateTransientUnit()</function> on the manager object).</para>
+    </refsect2>
+
+    <refsect2>
+      <title>Security</title>
+
+      <para>Similarly to methods on the <interfacename>Manager</interfacename> object, read-only access is
+      allowed for everyone. All operations are allowed for clients with the
+      <constant>CAP_SYS_ADMIN</constant> capability or when the
+      <interfacename>org.freedesktop.systemd1.manage-units</interfacename> privilege is granted by
+      polkit.</para>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>Service Unit Objects</title>
+
+    <para>All service unit objects implement the
+    <interfacename>org.freedesktop.systemd1.Service</interfacename> interface (described here) in addition to
+    the generic <interfacename>org.freedesktop.systemd1.Unit</interfacename> interface (see above).</para>
+
+    <programlisting executable="systemd" node="/org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice" interface="org.freedesktop.systemd1.Service">
+node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
+  interface org.freedesktop.systemd1.Service {
+    methods:
+      GetProcesses(out a(sus) processes);
+      AttachProcesses(in  s subcgroup,
+                      in  au pids);
+    properties:
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s Type = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s Restart = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s PIDFile = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s NotifyAccess = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t RestartUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t TimeoutStartUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t TimeoutStopUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t TimeoutAbortUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t RuntimeMaxUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t WatchdogUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t WatchdogTimestamp = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t WatchdogTimestampMonotonic = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b RootDirectoryStartOnly = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b RemainAfterExit = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b GuessMainPID = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly (aiai) RestartPreventExitStatus = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly (aiai) RestartForceExitStatus = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly (aiai) SuccessExitStatus = ...;
+      readonly u MainPID = ...;
+      readonly u ControlPID = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s BusName = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u FileDescriptorStoreMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly u NFileDescriptorStore = ...;
+      readonly s StatusText = '...';
+      readonly i StatusErrno = ...;
+      readonly s Result = '...';
+      readonly s ReloadResult = '...';
+      readonly s CleanResult = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s USBFunctionDescriptors = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s USBFunctionStrings = '...';
+      readonly u UID = ...;
+      readonly u GID = ...;
+      readonly u NRestarts = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s OOMPolicy = '...';
+      readonly t ExecMainStartTimestamp = ...;
+      readonly t ExecMainStartTimestampMonotonic = ...;
+      readonly t ExecMainExitTimestamp = ...;
+      readonly t ExecMainExitTimestampMonotonic = ...;
+      readonly u ExecMainPID = ...;
+      readonly i ExecMainCode = ...;
+      readonly i ExecMainStatus = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates")
+      readonly a(sasbttttuii) ExecCondition = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates")
+      readonly a(sasasttttuii) ExecConditionEx = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates")
+      readonly a(sasbttttuii) ExecStartPre = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates")
+      readonly a(sasasttttuii) ExecStartPreEx = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates")
+      readonly a(sasbttttuii) ExecStart = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates")
+      readonly a(sasasttttuii) ExecStartEx = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates")
+      readonly a(sasbttttuii) ExecStartPost = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates")
+      readonly a(sasasttttuii) ExecStartPostEx = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates")
+      readonly a(sasbttttuii) ExecReload = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates")
+      readonly a(sasasttttuii) ExecReloadEx = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates")
+      readonly a(sasbttttuii) ExecStop = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates")
+      readonly a(sasasttttuii) ExecStopEx = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates")
+      readonly a(sasbttttuii) ExecStopPost = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates")
+      readonly a(sasasttttuii) ExecStopPostEx = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly s Slice = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly s ControlGroup = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryCurrent = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t CPUUsageNSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly ay EffectiveCPUs = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly ay EffectiveMemoryNodes = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t TasksCurrent = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IPIngressBytes = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IPIngressPackets = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IPEgressBytes = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IPEgressPackets = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IOReadBytes = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IOReadOperations = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IOWriteBytes = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IOWriteOperations = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b Delegate = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly as DelegateControllers = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b CPUAccounting = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t CPUWeight = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t StartupCPUWeight = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t CPUShares = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t StartupCPUShares = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t CPUQuotaPerSecUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t CPUQuotaPeriodUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly ay AllowedCPUs = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly ay AllowedMemoryNodes = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b IOAccounting = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IOWeight = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t StartupIOWeight = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) IODeviceWeight = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) IOReadBandwidthMax = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) IOWriteBandwidthMax = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) IOReadIOPSMax = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) IOWriteIOPSMax = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) IODeviceLatencyTargetUSec = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b BlockIOAccounting = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t BlockIOWeight = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t StartupBlockIOWeight = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) BlockIODeviceWeight = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) BlockIOReadBandwidth = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) BlockIOWriteBandwidth = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b MemoryAccounting = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t DefaultMemoryLow = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t DefaultMemoryMin = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryMin = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryLow = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryHigh = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemorySwapMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryLimit = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly s DevicePolicy = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(ss) DeviceAllow = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b TasksAccounting = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t TasksMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b IPAccounting = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(iayu) IPAddressAllow = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(iayu) IPAddressDeny = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly as IPIngressFilterPath = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly as IPEgressFilterPath = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly as DisableControllers = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as Environment = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly a(sb) EnvironmentFiles = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as PassEnvironment = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as UnsetEnvironment = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u UMask = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitCPU = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitCPUSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitFSIZE = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitFSIZESoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitDATA = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitDATASoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitSTACK = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitSTACKSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitCORE = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitCORESoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitRSS = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitRSSSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitNOFILE = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitNOFILESoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitAS = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitASSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitNPROC = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitNPROCSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitMEMLOCK = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitMEMLOCKSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitLOCKS = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitLOCKSSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitSIGPENDING = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitSIGPENDINGSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitMSGQUEUE = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitMSGQUEUESoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitNICE = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitNICESoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitRTPRIO = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitRTPRIOSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitRTTIME = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitRTTIMESoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s WorkingDirectory = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s RootDirectory = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s RootImage = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i OOMScoreAdjust = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t CoredumpFilter = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i Nice = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i IOSchedulingClass = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i IOSchedulingPriority = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i CPUSchedulingPolicy = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i CPUSchedulingPriority = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly ay CPUAffinity = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b CPUAffinityFromNUMA = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i NUMAPolicy = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly ay NUMAMask = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t TimerSlackNSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b CPUSchedulingResetOnFork = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b NonBlocking = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s StandardInput = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s StandardInputFileDescriptorName = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly ay StandardInputData = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s StandardOutput = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s StandardOutputFileDescriptorName = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s StandardError = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s StandardErrorFileDescriptorName = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s TTYPath = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b TTYReset = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b TTYVHangup = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b TTYVTDisallocate = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i SyslogPriority = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s SyslogIdentifier = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b SyslogLevelPrefix = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i SyslogLevel = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i SyslogFacility = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i LogLevelMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LogRateLimitIntervalUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u LogRateLimitBurst = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly aay LogExtraFields = [[...], ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s LogNamespace = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i SecureBits = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t CapabilityBoundingSet = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t AmbientCapabilities = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s User = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s Group = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b DynamicUser = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b RemoveIPC = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as SupplementaryGroups = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s PAMName = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as ReadWritePaths = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as ReadOnlyPaths = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as InaccessiblePaths = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t MountFlags = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b PrivateTmp = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b PrivateDevices = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b ProtectClock = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b ProtectKernelTunables = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b ProtectKernelModules = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b ProtectKernelLogs = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b ProtectControlGroups = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b PrivateNetwork = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b PrivateUsers = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b PrivateMounts = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s ProtectHome = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s ProtectSystem = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b SameProcessGroup = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s UtmpIdentifier = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s UtmpMode = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly (bs) SELinuxContext = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly (bs) AppArmorProfile = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly (bs) SmackProcessLabel = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b IgnoreSIGPIPE = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b NoNewPrivileges = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly (bas) SystemCallFilter = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as SystemCallArchitectures = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i SystemCallErrorNumber = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s Personality = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b LockPersonality = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly (bas) RestrictAddressFamilies = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s RuntimeDirectoryPreserve = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u RuntimeDirectoryMode = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as RuntimeDirectory = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u StateDirectoryMode = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as StateDirectory = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u CacheDirectoryMode = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as CacheDirectory = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u LogsDirectoryMode = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as LogsDirectory = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u ConfigurationDirectoryMode = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as ConfigurationDirectory = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t TimeoutCleanUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b MemoryDenyWriteExecute = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b RestrictRealtime = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b RestrictSUIDSGID = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t RestrictNamespaces = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly a(ssbt) BindPaths = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly a(ssbt) BindReadOnlyPaths = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly a(ss) TemporaryFileSystem = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b MountAPIVFS = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s KeyringMode = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b ProtectHostname = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s NetworkNamespacePath = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s KillMode = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i KillSignal = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i RestartKillSignal = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i FinalKillSignal = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b SendSIGKILL = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b SendSIGHUP = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i WatchdogSignal = ...;
+  };
+  interface org.freedesktop.DBus.Peer { ... };
+  interface org.freedesktop.DBus.Introspectable { ... };
+  interface org.freedesktop.DBus.Properties { ... };
+  interface org.freedesktop.systemd1.Unit { ... };
+};
+    </programlisting>
+
+    <!--method GetProcesses is not documented!-->
+
+    <!--method AttachProcesses is not documented!-->
+
+    <!--property Type is not documented!-->
+
+    <!--property Restart is not documented!-->
+
+    <!--property PIDFile is not documented!-->
+
+    <!--property NotifyAccess is not documented!-->
+
+    <!--property RestartUSec is not documented!-->
+
+    <!--property TimeoutStartUSec is not documented!-->
+
+    <!--property TimeoutStopUSec is not documented!-->
+
+    <!--property TimeoutAbortUSec is not documented!-->
+
+    <!--property RuntimeMaxUSec is not documented!-->
+
+    <!--property WatchdogUSec is not documented!-->
+
+    <!--property RootDirectoryStartOnly is not documented!-->
+
+    <!--property RemainAfterExit is not documented!-->
+
+    <!--property GuessMainPID is not documented!-->
+
+    <!--property RestartPreventExitStatus is not documented!-->
+
+    <!--property RestartForceExitStatus is not documented!-->
+
+    <!--property SuccessExitStatus is not documented!-->
+
+    <!--property BusName is not documented!-->
+
+    <!--property FileDescriptorStoreMax is not documented!-->
+
+    <!--property NFileDescriptorStore is not documented!-->
+
+    <!--property StatusErrno is not documented!-->
+
+    <!--property ReloadResult is not documented!-->
+
+    <!--property CleanResult is not documented!-->
+
+    <!--property USBFunctionDescriptors is not documented!-->
+
+    <!--property USBFunctionStrings is not documented!-->
+
+    <!--property UID is not documented!-->
+
+    <!--property GID is not documented!-->
+
+    <!--property NRestarts is not documented!-->
+
+    <!--property OOMPolicy is not documented!-->
+
+    <!--property ExecCondition is not documented!-->
+
+    <!--property ExecConditionEx is not documented!-->
+
+    <!--property ExecStartPreEx is not documented!-->
+
+    <!--property ExecStartEx is not documented!-->
+
+    <!--property ExecStartPostEx is not documented!-->
+
+    <!--property ExecReloadEx is not documented!-->
+
+    <!--property ExecStopEx is not documented!-->
+
+    <!--property ExecStopPost is not documented!-->
+
+    <!--property ExecStopPostEx is not documented!-->
+
+    <!--property Slice is not documented!-->
+
+    <!--property MemoryCurrent is not documented!-->
+
+    <!--property CPUUsageNSec is not documented!-->
+
+    <!--property EffectiveCPUs is not documented!-->
+
+    <!--property EffectiveMemoryNodes is not documented!-->
+
+    <!--property TasksCurrent is not documented!-->
+
+    <!--property IPIngressBytes is not documented!-->
+
+    <!--property IPIngressPackets is not documented!-->
+
+    <!--property IPEgressBytes is not documented!-->
+
+    <!--property IPEgressPackets is not documented!-->
+
+    <!--property IOReadBytes is not documented!-->
+
+    <!--property IOReadOperations is not documented!-->
+
+    <!--property IOWriteBytes is not documented!-->
+
+    <!--property IOWriteOperations is not documented!-->
+
+    <!--property Delegate is not documented!-->
+
+    <!--property DelegateControllers is not documented!-->
+
+    <!--property CPUAccounting is not documented!-->
+
+    <!--property CPUWeight is not documented!-->
+
+    <!--property StartupCPUWeight is not documented!-->
+
+    <!--property CPUShares is not documented!-->
+
+    <!--property StartupCPUShares is not documented!-->
+
+    <!--property CPUQuotaPerSecUSec is not documented!-->
+
+    <!--property CPUQuotaPeriodUSec is not documented!-->
+
+    <!--property AllowedCPUs is not documented!-->
+
+    <!--property AllowedMemoryNodes is not documented!-->
+
+    <!--property IOAccounting is not documented!-->
+
+    <!--property IOWeight is not documented!-->
+
+    <!--property StartupIOWeight is not documented!-->
+
+    <!--property IODeviceWeight is not documented!-->
+
+    <!--property IOReadBandwidthMax is not documented!-->
+
+    <!--property IOWriteBandwidthMax is not documented!-->
+
+    <!--property IOReadIOPSMax is not documented!-->
+
+    <!--property IOWriteIOPSMax is not documented!-->
+
+    <!--property IODeviceLatencyTargetUSec is not documented!-->
+
+    <!--property BlockIOAccounting is not documented!-->
+
+    <!--property BlockIOWeight is not documented!-->
+
+    <!--property StartupBlockIOWeight is not documented!-->
+
+    <!--property BlockIODeviceWeight is not documented!-->
+
+    <!--property BlockIOReadBandwidth is not documented!-->
+
+    <!--property BlockIOWriteBandwidth is not documented!-->
+
+    <!--property MemoryAccounting is not documented!-->
+
+    <!--property DefaultMemoryLow is not documented!-->
+
+    <!--property DefaultMemoryMin is not documented!-->
+
+    <!--property MemoryMin is not documented!-->
+
+    <!--property MemoryLow is not documented!-->
+
+    <!--property MemoryHigh is not documented!-->
+
+    <!--property MemoryMax is not documented!-->
+
+    <!--property MemorySwapMax is not documented!-->
+
+    <!--property MemoryLimit is not documented!-->
+
+    <!--property DevicePolicy is not documented!-->
+
+    <!--property DeviceAllow is not documented!-->
+
+    <!--property TasksAccounting is not documented!-->
+
+    <!--property TasksMax is not documented!-->
+
+    <!--property IPAccounting is not documented!-->
+
+    <!--property IPAddressAllow is not documented!-->
+
+    <!--property IPAddressDeny is not documented!-->
+
+    <!--property IPIngressFilterPath is not documented!-->
+
+    <!--property IPEgressFilterPath is not documented!-->
+
+    <!--property DisableControllers is not documented!-->
+
+    <!--property EnvironmentFiles is not documented!-->
+
+    <!--property PassEnvironment is not documented!-->
+
+    <!--property UnsetEnvironment is not documented!-->
+
+    <!--property UMask is not documented!-->
+
+    <!--property LimitCPUSoft is not documented!-->
+
+    <!--property LimitFSIZE is not documented!-->
+
+    <!--property LimitFSIZESoft is not documented!-->
+
+    <!--property LimitDATA is not documented!-->
+
+    <!--property LimitDATASoft is not documented!-->
+
+    <!--property LimitSTACK is not documented!-->
+
+    <!--property LimitSTACKSoft is not documented!-->
+
+    <!--property LimitCORE is not documented!-->
+
+    <!--property LimitCORESoft is not documented!-->
+
+    <!--property LimitRSS is not documented!-->
+
+    <!--property LimitRSSSoft is not documented!-->
+
+    <!--property LimitNOFILE is not documented!-->
+
+    <!--property LimitNOFILESoft is not documented!-->
+
+    <!--property LimitAS is not documented!-->
+
+    <!--property LimitASSoft is not documented!-->
+
+    <!--property LimitNPROC is not documented!-->
+
+    <!--property LimitNPROCSoft is not documented!-->
+
+    <!--property LimitMEMLOCK is not documented!-->
+
+    <!--property LimitMEMLOCKSoft is not documented!-->
+
+    <!--property LimitLOCKS is not documented!-->
+
+    <!--property LimitLOCKSSoft is not documented!-->
+
+    <!--property LimitSIGPENDING is not documented!-->
+
+    <!--property LimitSIGPENDINGSoft is not documented!-->
+
+    <!--property LimitMSGQUEUE is not documented!-->
+
+    <!--property LimitMSGQUEUESoft is not documented!-->
+
+    <!--property LimitNICE is not documented!-->
+
+    <!--property LimitNICESoft is not documented!-->
+
+    <!--property LimitRTPRIO is not documented!-->
+
+    <!--property LimitRTPRIOSoft is not documented!-->
+
+    <!--property LimitRTTIME is not documented!-->
+
+    <!--property LimitRTTIMESoft is not documented!-->
+
+    <!--property WorkingDirectory is not documented!-->
+
+    <!--property RootDirectory is not documented!-->
+
+    <!--property RootImage is not documented!-->
+
+    <!--property OOMScoreAdjust is not documented!-->
+
+    <!--property CoredumpFilter is not documented!-->
+
+    <!--property Nice is not documented!-->
+
+    <!--property IOSchedulingClass is not documented!-->
+
+    <!--property IOSchedulingPriority is not documented!-->
+
+    <!--property CPUSchedulingPolicy is not documented!-->
+
+    <!--property CPUSchedulingPriority is not documented!-->
+
+    <!--property CPUAffinity is not documented!-->
+
+    <!--property CPUAffinityFromNUMA is not documented!-->
+
+    <!--property NUMAPolicy is not documented!-->
+
+    <!--property NUMAMask is not documented!-->
+
+    <!--property TimerSlackNSec is not documented!-->
+
+    <!--property CPUSchedulingResetOnFork is not documented!-->
+
+    <!--property NonBlocking is not documented!-->
+
+    <!--property StandardInput is not documented!-->
+
+    <!--property StandardInputFileDescriptorName is not documented!-->
+
+    <!--property StandardInputData is not documented!-->
+
+    <!--property StandardOutput is not documented!-->
+
+    <!--property StandardOutputFileDescriptorName is not documented!-->
+
+    <!--property StandardError is not documented!-->
+
+    <!--property StandardErrorFileDescriptorName is not documented!-->
+
+    <!--property TTYPath is not documented!-->
+
+    <!--property TTYReset is not documented!-->
+
+    <!--property TTYVHangup is not documented!-->
+
+    <!--property TTYVTDisallocate is not documented!-->
+
+    <!--property SyslogPriority is not documented!-->
+
+    <!--property SyslogIdentifier is not documented!-->
+
+    <!--property SyslogLevelPrefix is not documented!-->
+
+    <!--property SyslogLevel is not documented!-->
+
+    <!--property SyslogFacility is not documented!-->
+
+    <!--property LogLevelMax is not documented!-->
+
+    <!--property LogRateLimitIntervalUSec is not documented!-->
+
+    <!--property LogRateLimitBurst is not documented!-->
+
+    <!--property LogExtraFields is not documented!-->
+
+    <!--property LogNamespace is not documented!-->
+
+    <!--property AmbientCapabilities is not documented!-->
+
+    <!--property User is not documented!-->
+
+    <!--property Group is not documented!-->
+
+    <!--property DynamicUser is not documented!-->
+
+    <!--property RemoveIPC is not documented!-->
+
+    <!--property SupplementaryGroups is not documented!-->
+
+    <!--property PAMName is not documented!-->
+
+    <!--property ReadWritePaths is not documented!-->
+
+    <!--property ReadOnlyPaths is not documented!-->
+
+    <!--property InaccessiblePaths is not documented!-->
+
+    <!--property PrivateTmp is not documented!-->
+
+    <!--property PrivateDevices is not documented!-->
+
+    <!--property ProtectClock is not documented!-->
+
+    <!--property ProtectKernelTunables is not documented!-->
+
+    <!--property ProtectKernelModules is not documented!-->
+
+    <!--property ProtectKernelLogs is not documented!-->
+
+    <!--property ProtectControlGroups is not documented!-->
+
+    <!--property PrivateNetwork is not documented!-->
+
+    <!--property PrivateUsers is not documented!-->
+
+    <!--property PrivateMounts is not documented!-->
+
+    <!--property ProtectHome is not documented!-->
+
+    <!--property ProtectSystem is not documented!-->
+
+    <!--property SameProcessGroup is not documented!-->
+
+    <!--property UtmpIdentifier is not documented!-->
+
+    <!--property UtmpMode is not documented!-->
+
+    <!--property SELinuxContext is not documented!-->
+
+    <!--property AppArmorProfile is not documented!-->
+
+    <!--property SmackProcessLabel is not documented!-->
+
+    <!--property IgnoreSIGPIPE is not documented!-->
+
+    <!--property NoNewPrivileges is not documented!-->
+
+    <!--property SystemCallFilter is not documented!-->
+
+    <!--property SystemCallArchitectures is not documented!-->
+
+    <!--property SystemCallErrorNumber is not documented!-->
+
+    <!--property Personality is not documented!-->
+
+    <!--property LockPersonality is not documented!-->
+
+    <!--property RestrictAddressFamilies is not documented!-->
+
+    <!--property RuntimeDirectoryPreserve is not documented!-->
+
+    <!--property RuntimeDirectoryMode is not documented!-->
+
+    <!--property RuntimeDirectory is not documented!-->
+
+    <!--property StateDirectoryMode is not documented!-->
+
+    <!--property StateDirectory is not documented!-->
+
+    <!--property CacheDirectoryMode is not documented!-->
+
+    <!--property CacheDirectory is not documented!-->
+
+    <!--property LogsDirectoryMode is not documented!-->
+
+    <!--property LogsDirectory is not documented!-->
+
+    <!--property ConfigurationDirectoryMode is not documented!-->
+
+    <!--property ConfigurationDirectory is not documented!-->
+
+    <!--property TimeoutCleanUSec is not documented!-->
+
+    <!--property MemoryDenyWriteExecute is not documented!-->
+
+    <!--property RestrictRealtime is not documented!-->
+
+    <!--property RestrictSUIDSGID is not documented!-->
+
+    <!--property RestrictNamespaces is not documented!-->
+
+    <!--property BindPaths is not documented!-->
+
+    <!--property BindReadOnlyPaths is not documented!-->
+
+    <!--property TemporaryFileSystem is not documented!-->
+
+    <!--property MountAPIVFS is not documented!-->
+
+    <!--property KeyringMode is not documented!-->
+
+    <!--property ProtectHostname is not documented!-->
+
+    <!--property NetworkNamespacePath is not documented!-->
+
+    <!--property KillMode is not documented!-->
+
+    <!--property KillSignal is not documented!-->
+
+    <!--property RestartKillSignal is not documented!-->
+
+    <!--property FinalKillSignal is not documented!-->
+
+    <!--property SendSIGKILL is not documented!-->
+
+    <!--property SendSIGHUP is not documented!-->
+
+    <!--property WatchdogSignal is not documented!-->
+
+    <!--Autogenerated cross-references for systemd.directives, do not edit-->
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Unit"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Service"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Unit"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Service"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetProcesses()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="AttachProcesses()"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Type"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Restart"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="PIDFile"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="NotifyAccess"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RestartUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TimeoutStartUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TimeoutStopUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TimeoutAbortUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RuntimeMaxUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="WatchdogUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="WatchdogTimestamp"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="WatchdogTimestampMonotonic"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RootDirectoryStartOnly"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RemainAfterExit"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="GuessMainPID"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RestartPreventExitStatus"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RestartForceExitStatus"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SuccessExitStatus"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MainPID"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ControlPID"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BusName"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="FileDescriptorStoreMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="NFileDescriptorStore"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StatusText"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StatusErrno"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Result"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ReloadResult"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CleanResult"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="USBFunctionDescriptors"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="USBFunctionStrings"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="UID"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="GID"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="NRestarts"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="OOMPolicy"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ExecMainStartTimestamp"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ExecMainStartTimestampMonotonic"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ExecMainExitTimestamp"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ExecMainExitTimestampMonotonic"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ExecMainPID"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ExecMainCode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ExecMainStatus"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ExecCondition"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ExecConditionEx"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ExecStartPre"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ExecStartPreEx"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ExecStart"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ExecStartEx"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ExecStartPost"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ExecStartPostEx"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ExecReload"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ExecReloadEx"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ExecStop"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ExecStopEx"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ExecStopPost"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ExecStopPostEx"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Slice"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ControlGroup"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryCurrent"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryNodes"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TasksCurrent"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPIngressBytes"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPIngressPackets"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPEgressBytes"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPEgressPackets"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOReadBytes"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOReadOperations"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOWriteBytes"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOWriteOperations"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Delegate"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DelegateControllers"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUAccounting"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StartupCPUWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUShares"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StartupCPUShares"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUQuotaPerSecUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUQuotaPeriodUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="AllowedCPUs"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="AllowedMemoryNodes"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOAccounting"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StartupIOWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IODeviceWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOReadBandwidthMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOWriteBandwidthMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOReadIOPSMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOWriteIOPSMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IODeviceLatencyTargetUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BlockIOAccounting"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BlockIOWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StartupBlockIOWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BlockIODeviceWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BlockIOReadBandwidth"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BlockIOWriteBandwidth"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryAccounting"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultMemoryLow"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultMemoryMin"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryMin"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryLow"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryHigh"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemorySwapMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryLimit"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DevicePolicy"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DeviceAllow"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TasksAccounting"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TasksMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPAccounting"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPAddressAllow"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPAddressDeny"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPIngressFilterPath"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPEgressFilterPath"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DisableControllers"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Environment"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="EnvironmentFiles"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="PassEnvironment"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="UnsetEnvironment"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="UMask"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitCPU"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitCPUSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitFSIZE"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitFSIZESoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitDATA"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitDATASoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitSTACK"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitSTACKSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitCORE"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitCORESoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitRSS"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitRSSSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitNOFILE"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitNOFILESoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitAS"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitASSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitNPROC"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitNPROCSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitMEMLOCK"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitMEMLOCKSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitLOCKS"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitLOCKSSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitSIGPENDING"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitSIGPENDINGSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitMSGQUEUE"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitMSGQUEUESoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitNICE"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitNICESoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitRTPRIO"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitRTPRIOSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitRTTIME"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitRTTIMESoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="WorkingDirectory"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RootDirectory"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RootImage"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="OOMScoreAdjust"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CoredumpFilter"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Nice"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOSchedulingClass"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOSchedulingPriority"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUSchedulingPolicy"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUSchedulingPriority"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUAffinity"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUAffinityFromNUMA"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="NUMAPolicy"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="NUMAMask"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TimerSlackNSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUSchedulingResetOnFork"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="NonBlocking"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StandardInput"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StandardInputFileDescriptorName"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StandardInputData"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StandardOutput"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StandardOutputFileDescriptorName"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StandardError"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StandardErrorFileDescriptorName"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TTYPath"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TTYReset"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TTYVHangup"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TTYVTDisallocate"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SyslogPriority"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SyslogIdentifier"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SyslogLevelPrefix"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SyslogLevel"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SyslogFacility"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LogLevelMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LogRateLimitIntervalUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LogRateLimitBurst"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LogExtraFields"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LogNamespace"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SecureBits"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CapabilityBoundingSet"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="AmbientCapabilities"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="User"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Group"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DynamicUser"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RemoveIPC"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SupplementaryGroups"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="PAMName"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ReadWritePaths"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ReadOnlyPaths"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="InaccessiblePaths"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MountFlags"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="PrivateTmp"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="PrivateDevices"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ProtectClock"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ProtectKernelTunables"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ProtectKernelModules"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ProtectKernelLogs"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ProtectControlGroups"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="PrivateNetwork"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="PrivateUsers"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="PrivateMounts"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ProtectHome"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ProtectSystem"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SameProcessGroup"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="UtmpIdentifier"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="UtmpMode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SELinuxContext"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="AppArmorProfile"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SmackProcessLabel"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IgnoreSIGPIPE"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="NoNewPrivileges"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SystemCallFilter"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SystemCallArchitectures"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SystemCallErrorNumber"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Personality"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LockPersonality"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RestrictAddressFamilies"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RuntimeDirectoryPreserve"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RuntimeDirectoryMode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RuntimeDirectory"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StateDirectoryMode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StateDirectory"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CacheDirectoryMode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CacheDirectory"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LogsDirectoryMode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LogsDirectory"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ConfigurationDirectoryMode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ConfigurationDirectory"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TimeoutCleanUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryDenyWriteExecute"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RestrictRealtime"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RestrictSUIDSGID"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RestrictNamespaces"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BindPaths"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BindReadOnlyPaths"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TemporaryFileSystem"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MountAPIVFS"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="KeyringMode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ProtectHostname"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="NetworkNamespacePath"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="KillMode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="KillSignal"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RestartKillSignal"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="FinalKillSignal"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SendSIGKILL"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SendSIGHUP"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="WatchdogSignal"/>
+
+    <!--End of Autogenerated section-->
+
+    <refsect2>
+      <title>Properties</title>
+
+      <para>Most properties of the Service interface map directly to the corresponding settings in service
+      unit files. For the sake of brevity, here's a list of all exceptions only:</para>
+
+      <para><varname>WatchdogTimestamp</varname> and <varname>WatchdogTimestampMonotonic</varname> contain
+      <constant>CLOCK_REALTIME</constant>/<constant>CLOCK_MONOTONIC</constant> microsecond timestamps of the
+      last watchdog ping received from the service, or 0 if none was ever received.</para>
+
+      <para><varname>ExecStartPre</varname>, <varname>ExecStart</varname>, <varname>ExecStartPost</varname>,
+      <varname>ExecReload</varname>, <varname>ExecStop</varname>, and <varname>ExecStop</varname> are arrays
+      of structures where each struct contains: the binary path to execute; an array with all arguments to
+      pass to the executed command, starting with argument 0; a boolean whether it should be considered a
+      failure if the process exits uncleanly; two pairs of
+      <constant>CLOCK_REALTIME</constant>/<constant>CLOCK_MONOTONIC</constant> microsecond timestamps when
+      the process began and finished running the last time, or 0 if it never ran or never finished running;
+      the PID of the process, or 0 if it has not run yet; the exit code and status of the last run. This
+      field hence maps more or less to the corresponding setting in the service unit file but is augmented
+      with runtime data.</para>
+
+      <para><varname>LimitCPU</varname> (and related properties) map more or less directly to the
+      corresponding settings in the service unit files except that if they aren't set, their value is
+      18446744073709551615 (i.e. -1).</para>
+
+      <para><varname>Capabilities</varname> contains the configured capabilities, as formatted with
+      <citerefentry project="man-pages"><refentrytitle>cap_to_text</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+      </para>
+
+      <para><varname>SecureBits</varname>, <varname>CapabilityBoundingSet</varname>,
+      <varname>MountFlags</varname> also correspond to the configured settings of the unit files, but
+      instead of being formatted as strings, they are encoded as the actual binary flags they are.
+      </para>
+
+      <para><varname>ExecMainStartTimestamp</varname>, <varname>ExecMainStartTimestampMonotonic</varname>,
+      <varname>ExecMainExitTimestamp</varname>, <varname>ExecMainExitTimestampMonotonic</varname>,
+      <varname>ExecMainPID</varname>, <varname>ExecMainCode</varname>, <varname>ExecMainStatus</varname>
+      contain information about the main process of the service as far as it is known. This is often the same
+      runtime information that is stored in <varname>ExecStart</varname>. However, it deviates for
+      <varname>Type=forking</varname> services where the main process of the service is not forked off
+      systemd directly. These fields either contain information of the last run of the process or of the
+      current running process.</para>
+
+      <para><varname>MainPID</varname> and <varname>ControlPID</varname> contain the main and control PID of
+      the service. The main PID is the current main PID of the service and is 0 when the service currently
+      has no main PID. The control PID is the PID of the current start/stop/reload process running and is 0
+      if no such process is currently running. That means that <varname>ExecMainPID</varname> and
+      <varname>MainPID</varname> differ in the way that the latter immediately reflects whether a main
+      process is currently running while the latter possible contains information collected from the last run
+      even if the process is no longer around.</para>
+
+      <para><varname>StatusText</varname> contains the status text passed to the service manager via a call
+      to
+      <citerefentry><refentrytitle>sd_notify</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+      This may be used by services to inform the service manager about its internal state with a nice
+      explanatory string.</para>
+
+      <para><varname>Result</varname> encodes the execution result of the last run of the service. It is
+      useful to determine the reason a service failed if it is in the <literal>failed</literal> state (see
+      <varname>ActiveState</varname> above). The following values are currently known:
+      <literal>success</literal> is set if the unit didn't fail. <literal>resources</literal> indicates that
+      not enough resources were available to fork off and execute the service
+      processes. <literal>timeout</literal> indicates that a timeout occurred while executing a service
+      operation. <literal>exit-code</literal> indicates that a service process exited with an unclean exit
+      code. <literal>signal</literal> indicates that a service process exited with an uncaught
+      signal. <literal>core-dump</literal> indicates that a service process exited uncleanly and dumped
+      core. <literal>watchdog</literal> indicates that a service did not send out watchdog ping messages
+      often enough. <literal>start-limit</literal> indicates that a service has been started too frequently
+      in a specific time frame (as configured in <varname>StartLimitInterval</varname>,
+      <varname>StartLimitBurst</varname>).</para>
+
+      <para><varname>ControlGroup</varname> indicates the control group path the processes of this service
+      unit are placed in.</para>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>Socket Unit Objects</title>
+
+    <programlisting executable="systemd" node="/org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket" interface="org.freedesktop.systemd1.Socket">
+node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
+  interface org.freedesktop.systemd1.Socket {
+    methods:
+      GetProcesses(out a(sus) processes);
+      AttachProcesses(in  s subcgroup,
+                      in  au pids);
+    properties:
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s BindIPv6Only = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u Backlog = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t TimeoutUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s BindToDevice = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s SocketUser = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s SocketGroup = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u SocketMode = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u DirectoryMode = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b Accept = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b Writable = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b KeepAlive = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t KeepAliveTimeUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t KeepAliveIntervalUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u KeepAliveProbes = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t DeferAcceptUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b NoDelay = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i Priority = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t ReceiveBuffer = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t SendBuffer = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i IPTOS = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i IPTTL = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t PipeSize = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b FreeBind = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b Transparent = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b Broadcast = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b PassCredentials = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b PassSecurity = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b RemoveOnStop = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly a(ss) Listen = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as Symlinks = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i Mark = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u MaxConnections = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u MaxConnectionsPerSource = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly x MessageQueueMaxMessages = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly x MessageQueueMessageSize = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s TCPCongestion = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b ReusePort = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s SmackLabel = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s SmackLabelIPIn = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s SmackLabelIPOut = '...';
+      readonly u ControlPID = ...;
+      readonly s Result = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly u NConnections = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly u NAccepted = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly u NRefused = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly s FileDescriptorName = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i SocketProtocol = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t TriggerLimitIntervalUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u TriggerLimitBurst = ...;
+      readonly u UID = ...;
+      readonly u GID = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates")
+      readonly a(sasbttttuii) ExecStartPre = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates")
+      readonly a(sasbttttuii) ExecStartPost = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates")
+      readonly a(sasbttttuii) ExecStopPre = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates")
+      readonly a(sasbttttuii) ExecStopPost = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly s Slice = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly s ControlGroup = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryCurrent = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t CPUUsageNSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly ay EffectiveCPUs = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly ay EffectiveMemoryNodes = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t TasksCurrent = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IPIngressBytes = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IPIngressPackets = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IPEgressBytes = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IPEgressPackets = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IOReadBytes = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IOReadOperations = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IOWriteBytes = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IOWriteOperations = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b Delegate = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly as DelegateControllers = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b CPUAccounting = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t CPUWeight = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t StartupCPUWeight = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t CPUShares = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t StartupCPUShares = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t CPUQuotaPerSecUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t CPUQuotaPeriodUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly ay AllowedCPUs = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly ay AllowedMemoryNodes = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b IOAccounting = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IOWeight = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t StartupIOWeight = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) IODeviceWeight = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) IOReadBandwidthMax = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) IOWriteBandwidthMax = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) IOReadIOPSMax = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) IOWriteIOPSMax = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) IODeviceLatencyTargetUSec = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b BlockIOAccounting = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t BlockIOWeight = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t StartupBlockIOWeight = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) BlockIODeviceWeight = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) BlockIOReadBandwidth = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) BlockIOWriteBandwidth = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b MemoryAccounting = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t DefaultMemoryLow = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t DefaultMemoryMin = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryMin = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryLow = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryHigh = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemorySwapMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryLimit = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly s DevicePolicy = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(ss) DeviceAllow = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b TasksAccounting = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t TasksMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b IPAccounting = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(iayu) IPAddressAllow = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(iayu) IPAddressDeny = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly as IPIngressFilterPath = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly as IPEgressFilterPath = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly as DisableControllers = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as Environment = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly a(sb) EnvironmentFiles = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as PassEnvironment = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as UnsetEnvironment = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u UMask = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitCPU = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitCPUSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitFSIZE = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitFSIZESoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitDATA = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitDATASoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitSTACK = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitSTACKSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitCORE = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitCORESoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitRSS = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitRSSSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitNOFILE = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitNOFILESoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitAS = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitASSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitNPROC = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitNPROCSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitMEMLOCK = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitMEMLOCKSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitLOCKS = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitLOCKSSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitSIGPENDING = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitSIGPENDINGSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitMSGQUEUE = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitMSGQUEUESoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitNICE = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitNICESoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitRTPRIO = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitRTPRIOSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitRTTIME = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitRTTIMESoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s WorkingDirectory = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s RootDirectory = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s RootImage = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i OOMScoreAdjust = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t CoredumpFilter = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i Nice = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i IOSchedulingClass = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i IOSchedulingPriority = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i CPUSchedulingPolicy = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i CPUSchedulingPriority = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly ay CPUAffinity = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b CPUAffinityFromNUMA = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i NUMAPolicy = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly ay NUMAMask = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t TimerSlackNSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b CPUSchedulingResetOnFork = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b NonBlocking = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s StandardInput = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s StandardInputFileDescriptorName = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly ay StandardInputData = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s StandardOutput = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s StandardOutputFileDescriptorName = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s StandardError = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s StandardErrorFileDescriptorName = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s TTYPath = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b TTYReset = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b TTYVHangup = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b TTYVTDisallocate = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i SyslogPriority = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s SyslogIdentifier = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b SyslogLevelPrefix = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i SyslogLevel = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i SyslogFacility = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i LogLevelMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LogRateLimitIntervalUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u LogRateLimitBurst = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly aay LogExtraFields = [[...], ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s LogNamespace = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i SecureBits = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t CapabilityBoundingSet = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t AmbientCapabilities = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s User = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s Group = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b DynamicUser = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b RemoveIPC = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as SupplementaryGroups = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s PAMName = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as ReadWritePaths = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as ReadOnlyPaths = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as InaccessiblePaths = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t MountFlags = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b PrivateTmp = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b PrivateDevices = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b ProtectClock = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b ProtectKernelTunables = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b ProtectKernelModules = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b ProtectKernelLogs = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b ProtectControlGroups = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b PrivateNetwork = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b PrivateUsers = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b PrivateMounts = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s ProtectHome = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s ProtectSystem = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b SameProcessGroup = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s UtmpIdentifier = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s UtmpMode = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly (bs) SELinuxContext = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly (bs) AppArmorProfile = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly (bs) SmackProcessLabel = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b IgnoreSIGPIPE = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b NoNewPrivileges = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly (bas) SystemCallFilter = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as SystemCallArchitectures = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i SystemCallErrorNumber = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s Personality = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b LockPersonality = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly (bas) RestrictAddressFamilies = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s RuntimeDirectoryPreserve = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u RuntimeDirectoryMode = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as RuntimeDirectory = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u StateDirectoryMode = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as StateDirectory = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u CacheDirectoryMode = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as CacheDirectory = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u LogsDirectoryMode = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as LogsDirectory = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u ConfigurationDirectoryMode = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as ConfigurationDirectory = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t TimeoutCleanUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b MemoryDenyWriteExecute = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b RestrictRealtime = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b RestrictSUIDSGID = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t RestrictNamespaces = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly a(ssbt) BindPaths = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly a(ssbt) BindReadOnlyPaths = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly a(ss) TemporaryFileSystem = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b MountAPIVFS = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s KeyringMode = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b ProtectHostname = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s NetworkNamespacePath = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s KillMode = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i KillSignal = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i RestartKillSignal = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i FinalKillSignal = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b SendSIGKILL = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b SendSIGHUP = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i WatchdogSignal = ...;
+  };
+  interface org.freedesktop.DBus.Peer { ... };
+  interface org.freedesktop.DBus.Introspectable { ... };
+  interface org.freedesktop.DBus.Properties { ... };
+  interface org.freedesktop.systemd1.Unit { ... };
+};
+    </programlisting>
+
+    <!--method GetProcesses is not documented!-->
+
+    <!--method AttachProcesses is not documented!-->
+
+    <!--property BindIPv6Only is not documented!-->
+
+    <!--property Backlog is not documented!-->
+
+    <!--property TimeoutUSec is not documented!-->
+
+    <!--property BindToDevice is not documented!-->
+
+    <!--property SocketUser is not documented!-->
+
+    <!--property SocketGroup is not documented!-->
+
+    <!--property SocketMode is not documented!-->
+
+    <!--property DirectoryMode is not documented!-->
+
+    <!--property Writable is not documented!-->
+
+    <!--property KeepAlive is not documented!-->
+
+    <!--property KeepAliveTimeUSec is not documented!-->
+
+    <!--property KeepAliveIntervalUSec is not documented!-->
+
+    <!--property KeepAliveProbes is not documented!-->
+
+    <!--property DeferAcceptUSec is not documented!-->
+
+    <!--property NoDelay is not documented!-->
+
+    <!--property Priority is not documented!-->
+
+    <!--property ReceiveBuffer is not documented!-->
+
+    <!--property SendBuffer is not documented!-->
+
+    <!--property IPTOS is not documented!-->
+
+    <!--property IPTTL is not documented!-->
+
+    <!--property PipeSize is not documented!-->
+
+    <!--property FreeBind is not documented!-->
+
+    <!--property Transparent is not documented!-->
+
+    <!--property Broadcast is not documented!-->
+
+    <!--property PassCredentials is not documented!-->
+
+    <!--property PassSecurity is not documented!-->
+
+    <!--property RemoveOnStop is not documented!-->
+
+    <!--property Listen is not documented!-->
+
+    <!--property Symlinks is not documented!-->
+
+    <!--property Mark is not documented!-->
+
+    <!--property MaxConnections is not documented!-->
+
+    <!--property MaxConnectionsPerSource is not documented!-->
+
+    <!--property MessageQueueMaxMessages is not documented!-->
+
+    <!--property MessageQueueMessageSize is not documented!-->
+
+    <!--property TCPCongestion is not documented!-->
+
+    <!--property ReusePort is not documented!-->
+
+    <!--property SmackLabel is not documented!-->
+
+    <!--property SmackLabelIPIn is not documented!-->
+
+    <!--property SmackLabelIPOut is not documented!-->
+
+    <!--property NRefused is not documented!-->
+
+    <!--property FileDescriptorName is not documented!-->
+
+    <!--property SocketProtocol is not documented!-->
+
+    <!--property TriggerLimitIntervalUSec is not documented!-->
+
+    <!--property TriggerLimitBurst is not documented!-->
+
+    <!--property UID is not documented!-->
+
+    <!--property GID is not documented!-->
+
+    <!--property ExecStopPre is not documented!-->
+
+    <!--property ExecStopPost is not documented!-->
+
+    <!--property Slice is not documented!-->
+
+    <!--property MemoryCurrent is not documented!-->
+
+    <!--property CPUUsageNSec is not documented!-->
+
+    <!--property EffectiveCPUs is not documented!-->
+
+    <!--property EffectiveMemoryNodes is not documented!-->
+
+    <!--property TasksCurrent is not documented!-->
+
+    <!--property IPIngressBytes is not documented!-->
+
+    <!--property IPIngressPackets is not documented!-->
+
+    <!--property IPEgressBytes is not documented!-->
+
+    <!--property IPEgressPackets is not documented!-->
+
+    <!--property IOReadBytes is not documented!-->
+
+    <!--property IOReadOperations is not documented!-->
+
+    <!--property IOWriteBytes is not documented!-->
+
+    <!--property IOWriteOperations is not documented!-->
+
+    <!--property Delegate is not documented!-->
+
+    <!--property DelegateControllers is not documented!-->
+
+    <!--property CPUAccounting is not documented!-->
+
+    <!--property CPUWeight is not documented!-->
+
+    <!--property StartupCPUWeight is not documented!-->
+
+    <!--property CPUShares is not documented!-->
+
+    <!--property StartupCPUShares is not documented!-->
+
+    <!--property CPUQuotaPerSecUSec is not documented!-->
+
+    <!--property CPUQuotaPeriodUSec is not documented!-->
+
+    <!--property AllowedCPUs is not documented!-->
+
+    <!--property AllowedMemoryNodes is not documented!-->
+
+    <!--property IOAccounting is not documented!-->
+
+    <!--property IOWeight is not documented!-->
+
+    <!--property StartupIOWeight is not documented!-->
+
+    <!--property IODeviceWeight is not documented!-->
+
+    <!--property IOReadBandwidthMax is not documented!-->
+
+    <!--property IOWriteBandwidthMax is not documented!-->
+
+    <!--property IOReadIOPSMax is not documented!-->
+
+    <!--property IOWriteIOPSMax is not documented!-->
+
+    <!--property IODeviceLatencyTargetUSec is not documented!-->
+
+    <!--property BlockIOAccounting is not documented!-->
+
+    <!--property BlockIOWeight is not documented!-->
+
+    <!--property StartupBlockIOWeight is not documented!-->
+
+    <!--property BlockIODeviceWeight is not documented!-->
+
+    <!--property BlockIOReadBandwidth is not documented!-->
+
+    <!--property BlockIOWriteBandwidth is not documented!-->
+
+    <!--property MemoryAccounting is not documented!-->
+
+    <!--property DefaultMemoryLow is not documented!-->
+
+    <!--property DefaultMemoryMin is not documented!-->
+
+    <!--property MemoryMin is not documented!-->
+
+    <!--property MemoryLow is not documented!-->
+
+    <!--property MemoryHigh is not documented!-->
+
+    <!--property MemoryMax is not documented!-->
+
+    <!--property MemorySwapMax is not documented!-->
+
+    <!--property MemoryLimit is not documented!-->
+
+    <!--property DevicePolicy is not documented!-->
+
+    <!--property DeviceAllow is not documented!-->
+
+    <!--property TasksAccounting is not documented!-->
+
+    <!--property TasksMax is not documented!-->
+
+    <!--property IPAccounting is not documented!-->
+
+    <!--property IPAddressAllow is not documented!-->
+
+    <!--property IPAddressDeny is not documented!-->
+
+    <!--property IPIngressFilterPath is not documented!-->
+
+    <!--property IPEgressFilterPath is not documented!-->
+
+    <!--property DisableControllers is not documented!-->
+
+    <!--property EnvironmentFiles is not documented!-->
+
+    <!--property PassEnvironment is not documented!-->
+
+    <!--property UnsetEnvironment is not documented!-->
+
+    <!--property UMask is not documented!-->
+
+    <!--property LimitCPUSoft is not documented!-->
+
+    <!--property LimitFSIZE is not documented!-->
+
+    <!--property LimitFSIZESoft is not documented!-->
+
+    <!--property LimitDATA is not documented!-->
+
+    <!--property LimitDATASoft is not documented!-->
+
+    <!--property LimitSTACK is not documented!-->
+
+    <!--property LimitSTACKSoft is not documented!-->
+
+    <!--property LimitCORE is not documented!-->
+
+    <!--property LimitCORESoft is not documented!-->
+
+    <!--property LimitRSS is not documented!-->
+
+    <!--property LimitRSSSoft is not documented!-->
+
+    <!--property LimitNOFILE is not documented!-->
+
+    <!--property LimitNOFILESoft is not documented!-->
+
+    <!--property LimitAS is not documented!-->
+
+    <!--property LimitASSoft is not documented!-->
+
+    <!--property LimitNPROC is not documented!-->
+
+    <!--property LimitNPROCSoft is not documented!-->
+
+    <!--property LimitMEMLOCK is not documented!-->
+
+    <!--property LimitMEMLOCKSoft is not documented!-->
+
+    <!--property LimitLOCKS is not documented!-->
+
+    <!--property LimitLOCKSSoft is not documented!-->
+
+    <!--property LimitSIGPENDING is not documented!-->
+
+    <!--property LimitSIGPENDINGSoft is not documented!-->
+
+    <!--property LimitMSGQUEUE is not documented!-->
+
+    <!--property LimitMSGQUEUESoft is not documented!-->
+
+    <!--property LimitNICE is not documented!-->
+
+    <!--property LimitNICESoft is not documented!-->
+
+    <!--property LimitRTPRIO is not documented!-->
+
+    <!--property LimitRTPRIOSoft is not documented!-->
+
+    <!--property LimitRTTIME is not documented!-->
+
+    <!--property LimitRTTIMESoft is not documented!-->
+
+    <!--property WorkingDirectory is not documented!-->
+
+    <!--property RootDirectory is not documented!-->
+
+    <!--property RootImage is not documented!-->
+
+    <!--property OOMScoreAdjust is not documented!-->
+
+    <!--property CoredumpFilter is not documented!-->
+
+    <!--property Nice is not documented!-->
+
+    <!--property IOSchedulingClass is not documented!-->
+
+    <!--property IOSchedulingPriority is not documented!-->
+
+    <!--property CPUSchedulingPolicy is not documented!-->
+
+    <!--property CPUSchedulingPriority is not documented!-->
+
+    <!--property CPUAffinity is not documented!-->
+
+    <!--property CPUAffinityFromNUMA is not documented!-->
+
+    <!--property NUMAPolicy is not documented!-->
+
+    <!--property NUMAMask is not documented!-->
+
+    <!--property TimerSlackNSec is not documented!-->
+
+    <!--property CPUSchedulingResetOnFork is not documented!-->
+
+    <!--property NonBlocking is not documented!-->
+
+    <!--property StandardInput is not documented!-->
+
+    <!--property StandardInputFileDescriptorName is not documented!-->
+
+    <!--property StandardInputData is not documented!-->
+
+    <!--property StandardOutput is not documented!-->
+
+    <!--property StandardOutputFileDescriptorName is not documented!-->
+
+    <!--property StandardError is not documented!-->
+
+    <!--property StandardErrorFileDescriptorName is not documented!-->
+
+    <!--property TTYPath is not documented!-->
+
+    <!--property TTYReset is not documented!-->
+
+    <!--property TTYVHangup is not documented!-->
+
+    <!--property TTYVTDisallocate is not documented!-->
+
+    <!--property SyslogPriority is not documented!-->
+
+    <!--property SyslogIdentifier is not documented!-->
+
+    <!--property SyslogLevelPrefix is not documented!-->
+
+    <!--property SyslogLevel is not documented!-->
+
+    <!--property SyslogFacility is not documented!-->
+
+    <!--property LogLevelMax is not documented!-->
+
+    <!--property LogRateLimitIntervalUSec is not documented!-->
+
+    <!--property LogRateLimitBurst is not documented!-->
+
+    <!--property LogExtraFields is not documented!-->
+
+    <!--property LogNamespace is not documented!-->
+
+    <!--property AmbientCapabilities is not documented!-->
+
+    <!--property User is not documented!-->
+
+    <!--property Group is not documented!-->
+
+    <!--property DynamicUser is not documented!-->
+
+    <!--property RemoveIPC is not documented!-->
+
+    <!--property SupplementaryGroups is not documented!-->
+
+    <!--property PAMName is not documented!-->
+
+    <!--property ReadWritePaths is not documented!-->
+
+    <!--property ReadOnlyPaths is not documented!-->
+
+    <!--property InaccessiblePaths is not documented!-->
+
+    <!--property PrivateTmp is not documented!-->
+
+    <!--property PrivateDevices is not documented!-->
+
+    <!--property ProtectClock is not documented!-->
+
+    <!--property ProtectKernelTunables is not documented!-->
+
+    <!--property ProtectKernelModules is not documented!-->
+
+    <!--property ProtectKernelLogs is not documented!-->
+
+    <!--property ProtectControlGroups is not documented!-->
+
+    <!--property PrivateNetwork is not documented!-->
+
+    <!--property PrivateUsers is not documented!-->
+
+    <!--property PrivateMounts is not documented!-->
+
+    <!--property ProtectHome is not documented!-->
+
+    <!--property ProtectSystem is not documented!-->
+
+    <!--property SameProcessGroup is not documented!-->
+
+    <!--property UtmpIdentifier is not documented!-->
+
+    <!--property UtmpMode is not documented!-->
+
+    <!--property SELinuxContext is not documented!-->
+
+    <!--property AppArmorProfile is not documented!-->
+
+    <!--property SmackProcessLabel is not documented!-->
+
+    <!--property IgnoreSIGPIPE is not documented!-->
+
+    <!--property NoNewPrivileges is not documented!-->
+
+    <!--property SystemCallFilter is not documented!-->
+
+    <!--property SystemCallArchitectures is not documented!-->
+
+    <!--property SystemCallErrorNumber is not documented!-->
+
+    <!--property Personality is not documented!-->
+
+    <!--property LockPersonality is not documented!-->
+
+    <!--property RestrictAddressFamilies is not documented!-->
+
+    <!--property RuntimeDirectoryPreserve is not documented!-->
+
+    <!--property RuntimeDirectoryMode is not documented!-->
+
+    <!--property RuntimeDirectory is not documented!-->
+
+    <!--property StateDirectoryMode is not documented!-->
+
+    <!--property StateDirectory is not documented!-->
+
+    <!--property CacheDirectoryMode is not documented!-->
+
+    <!--property CacheDirectory is not documented!-->
+
+    <!--property LogsDirectoryMode is not documented!-->
+
+    <!--property LogsDirectory is not documented!-->
+
+    <!--property ConfigurationDirectoryMode is not documented!-->
+
+    <!--property ConfigurationDirectory is not documented!-->
+
+    <!--property TimeoutCleanUSec is not documented!-->
+
+    <!--property MemoryDenyWriteExecute is not documented!-->
+
+    <!--property RestrictRealtime is not documented!-->
+
+    <!--property RestrictSUIDSGID is not documented!-->
+
+    <!--property RestrictNamespaces is not documented!-->
+
+    <!--property BindPaths is not documented!-->
+
+    <!--property BindReadOnlyPaths is not documented!-->
+
+    <!--property TemporaryFileSystem is not documented!-->
+
+    <!--property MountAPIVFS is not documented!-->
+
+    <!--property KeyringMode is not documented!-->
+
+    <!--property ProtectHostname is not documented!-->
+
+    <!--property NetworkNamespacePath is not documented!-->
+
+    <!--property KillMode is not documented!-->
+
+    <!--property KillSignal is not documented!-->
+
+    <!--property RestartKillSignal is not documented!-->
+
+    <!--property FinalKillSignal is not documented!-->
+
+    <!--property SendSIGKILL is not documented!-->
+
+    <!--property SendSIGHUP is not documented!-->
+
+    <!--property WatchdogSignal is not documented!-->
+
+    <!--Autogenerated cross-references for systemd.directives, do not edit-->
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Unit"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Socket"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Unit"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Socket"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetProcesses()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="AttachProcesses()"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BindIPv6Only"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Backlog"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TimeoutUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BindToDevice"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SocketUser"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SocketGroup"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SocketMode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DirectoryMode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Accept"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Writable"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="KeepAlive"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="KeepAliveTimeUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="KeepAliveIntervalUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="KeepAliveProbes"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DeferAcceptUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="NoDelay"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Priority"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ReceiveBuffer"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SendBuffer"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPTOS"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPTTL"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="PipeSize"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="FreeBind"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Transparent"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Broadcast"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="PassCredentials"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="PassSecurity"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RemoveOnStop"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Listen"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Symlinks"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Mark"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MaxConnections"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MaxConnectionsPerSource"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MessageQueueMaxMessages"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MessageQueueMessageSize"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TCPCongestion"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ReusePort"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SmackLabel"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SmackLabelIPIn"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SmackLabelIPOut"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ControlPID"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Result"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="NConnections"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="NAccepted"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="NRefused"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="FileDescriptorName"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SocketProtocol"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TriggerLimitIntervalUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TriggerLimitBurst"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="UID"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="GID"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ExecStartPre"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ExecStartPost"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ExecStopPre"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ExecStopPost"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Slice"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ControlGroup"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryCurrent"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryNodes"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TasksCurrent"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPIngressBytes"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPIngressPackets"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPEgressBytes"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPEgressPackets"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOReadBytes"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOReadOperations"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOWriteBytes"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOWriteOperations"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Delegate"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DelegateControllers"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUAccounting"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StartupCPUWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUShares"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StartupCPUShares"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUQuotaPerSecUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUQuotaPeriodUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="AllowedCPUs"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="AllowedMemoryNodes"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOAccounting"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StartupIOWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IODeviceWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOReadBandwidthMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOWriteBandwidthMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOReadIOPSMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOWriteIOPSMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IODeviceLatencyTargetUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BlockIOAccounting"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BlockIOWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StartupBlockIOWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BlockIODeviceWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BlockIOReadBandwidth"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BlockIOWriteBandwidth"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryAccounting"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultMemoryLow"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultMemoryMin"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryMin"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryLow"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryHigh"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemorySwapMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryLimit"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DevicePolicy"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DeviceAllow"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TasksAccounting"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TasksMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPAccounting"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPAddressAllow"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPAddressDeny"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPIngressFilterPath"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPEgressFilterPath"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DisableControllers"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Environment"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="EnvironmentFiles"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="PassEnvironment"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="UnsetEnvironment"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="UMask"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitCPU"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitCPUSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitFSIZE"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitFSIZESoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitDATA"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitDATASoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitSTACK"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitSTACKSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitCORE"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitCORESoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitRSS"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitRSSSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitNOFILE"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitNOFILESoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitAS"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitASSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitNPROC"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitNPROCSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitMEMLOCK"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitMEMLOCKSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitLOCKS"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitLOCKSSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitSIGPENDING"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitSIGPENDINGSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitMSGQUEUE"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitMSGQUEUESoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitNICE"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitNICESoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitRTPRIO"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitRTPRIOSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitRTTIME"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitRTTIMESoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="WorkingDirectory"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RootDirectory"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RootImage"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="OOMScoreAdjust"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CoredumpFilter"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Nice"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOSchedulingClass"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOSchedulingPriority"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUSchedulingPolicy"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUSchedulingPriority"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUAffinity"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUAffinityFromNUMA"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="NUMAPolicy"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="NUMAMask"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TimerSlackNSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUSchedulingResetOnFork"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="NonBlocking"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StandardInput"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StandardInputFileDescriptorName"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StandardInputData"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StandardOutput"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StandardOutputFileDescriptorName"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StandardError"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StandardErrorFileDescriptorName"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TTYPath"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TTYReset"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TTYVHangup"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TTYVTDisallocate"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SyslogPriority"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SyslogIdentifier"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SyslogLevelPrefix"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SyslogLevel"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SyslogFacility"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LogLevelMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LogRateLimitIntervalUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LogRateLimitBurst"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LogExtraFields"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LogNamespace"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SecureBits"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CapabilityBoundingSet"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="AmbientCapabilities"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="User"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Group"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DynamicUser"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RemoveIPC"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SupplementaryGroups"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="PAMName"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ReadWritePaths"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ReadOnlyPaths"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="InaccessiblePaths"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MountFlags"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="PrivateTmp"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="PrivateDevices"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ProtectClock"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ProtectKernelTunables"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ProtectKernelModules"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ProtectKernelLogs"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ProtectControlGroups"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="PrivateNetwork"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="PrivateUsers"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="PrivateMounts"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ProtectHome"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ProtectSystem"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SameProcessGroup"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="UtmpIdentifier"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="UtmpMode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SELinuxContext"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="AppArmorProfile"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SmackProcessLabel"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IgnoreSIGPIPE"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="NoNewPrivileges"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SystemCallFilter"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SystemCallArchitectures"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SystemCallErrorNumber"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Personality"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LockPersonality"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RestrictAddressFamilies"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RuntimeDirectoryPreserve"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RuntimeDirectoryMode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RuntimeDirectory"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StateDirectoryMode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StateDirectory"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CacheDirectoryMode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CacheDirectory"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LogsDirectoryMode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LogsDirectory"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ConfigurationDirectoryMode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ConfigurationDirectory"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TimeoutCleanUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryDenyWriteExecute"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RestrictRealtime"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RestrictSUIDSGID"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RestrictNamespaces"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BindPaths"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BindReadOnlyPaths"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TemporaryFileSystem"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MountAPIVFS"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="KeyringMode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ProtectHostname"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="NetworkNamespacePath"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="KillMode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="KillSignal"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RestartKillSignal"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="FinalKillSignal"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SendSIGKILL"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SendSIGHUP"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="WatchdogSignal"/>
+
+    <!--End of Autogenerated section-->
+
+    <refsect2>
+      <title>Properties</title>
+
+      <para>Most of the properties map directly to the corresponding settings in socket unit files. As socket
+      units can include <varname>ExecStartPre</varname> (and similar) fields which contain information about
+      processes to execute. They also share most of the fields related to the execution context that Service
+      objects expose (see above).</para>
+
+      <para>In addition to these properties there are the following:</para>
+
+      <para><varname>NAccepted</varname> contains the accumulated number of connections ever accepted on this
+      socket. This only applies to sockets with <varname>Accept</varname> set to <literal>true</literal>,
+      i.e. those where systemd is responsible for accepted connections. </para>
+
+      <para>Similarly <varname>NConnections</varname> contains the number of currently open connections on
+      this socket. It only applies only to socket units with <varname>Accept</varname> set to
+      <literal>true</literal>.</para>
+
+      <para><varname>Result</varname> encodes the reason why a socket unit failed if it is in the
+      <literal>failed</literal> state (see <varname>ActiveState</varname> above). The values
+      <literal>success</literal>, <literal>resources</literal>, <literal>timeout</literal>,
+      <literal>exit-code</literal>, <literal>signal</literal> and <literal>core-dump</literal> have the same
+      meaning as they have for the corresponding field of service units (see above). In addition to that,
+      the value <literal>service-failed-permanent</literal> indicates that the service of this socket failed
+      continuously.</para>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>Target Unit Objects</title>
+
+    <programlisting executable="systemd" node="/org/freedesktop/systemd1/unit/basic_2etarget" interface="org.freedesktop.systemd1.Target">
+node /org/freedesktop/systemd1/unit/basic_2etarget {
+  interface org.freedesktop.systemd1.Target {
+  };
+  interface org.freedesktop.DBus.Peer { ... };
+  interface org.freedesktop.DBus.Introspectable { ... };
+  interface org.freedesktop.DBus.Properties { ... };
+  interface org.freedesktop.systemd1.Unit { ... };
+};
+    </programlisting>
+
+    <para>Target units have neither type-specific methods nor properties.</para>
+  </refsect1>
+
+
+  <refsect1>
+    <title>Device Unit Objects</title>
+
+    <para>All device unit objects implement the <interfacename>org.freedesktop.systemd1.Device</interfacename> interface (described here)
+    in addition to the generic <interfacename>org.freedesktop.systemd1.Unit</interfacename> interface (see above).</para>
+
+    <programlisting executable="systemd" node="/org/freedesktop/systemd1/unit/dev_2dttyS0_2edevice" interface="org.freedesktop.systemd1.Device">
+node /org/freedesktop/systemd1/unit/dev_2dttyS0_2edevice {
+  interface org.freedesktop.systemd1.Device {
+    properties:
+      readonly s SysFSPath = '...';
+  };
+  interface org.freedesktop.DBus.Peer { ... };
+  interface org.freedesktop.DBus.Introspectable { ... };
+  interface org.freedesktop.DBus.Properties { ... };
+  interface org.freedesktop.systemd1.Unit { ... };
+};
+    </programlisting>
+
+    <!--Autogenerated cross-references for systemd.directives, do not edit-->
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Unit"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Device"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Unit"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Device"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SysFSPath"/>
+
+    <!--End of Autogenerated section-->
+
+    <refsect2>
+      <title>Properties</title>
+
+      <para>Device units only expose a single type-specific property:</para>
+
+      <para><varname>SysFSPath</varname> contains the sysfs path of the kernel device this object corresponds
+      to.</para>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>Mount Unit Objects</title>
+
+    <para>All mount unit objects implement the <interfacename>org.freedesktop.systemd1.Mount</interfacename>
+    interface (described here) in addition to the generic
+    <interfacename>org.freedesktop.systemd1.Unit</interfacename> interface (see above).</para>
+
+    <programlisting executable="systemd" node="/org/freedesktop/systemd1/unit/home_2emount" interface="org.freedesktop.systemd1.Mount">
+node /org/freedesktop/systemd1/unit/home_2emount {
+  interface org.freedesktop.systemd1.Mount {
+    methods:
+      GetProcesses(out a(sus) processes);
+      AttachProcesses(in  s subcgroup,
+                      in  au pids);
+    properties:
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s Where = '...';
+      readonly s What = '...';
+      readonly s Options = '...';
+      readonly s Type = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t TimeoutUSec = ...;
+      readonly u ControlPID = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u DirectoryMode = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b SloppyOptions = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b LazyUnmount = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b ForceUnmount = ...;
+      readonly s Result = '...';
+      readonly u UID = ...;
+      readonly u GID = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates")
+      readonly a(sasbttttuii) ExecMount = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates")
+      readonly a(sasbttttuii) ExecUnmount = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates")
+      readonly a(sasbttttuii) ExecRemount = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly s Slice = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly s ControlGroup = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryCurrent = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t CPUUsageNSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly ay EffectiveCPUs = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly ay EffectiveMemoryNodes = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t TasksCurrent = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IPIngressBytes = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IPIngressPackets = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IPEgressBytes = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IPEgressPackets = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IOReadBytes = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IOReadOperations = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IOWriteBytes = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IOWriteOperations = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b Delegate = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly as DelegateControllers = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b CPUAccounting = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t CPUWeight = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t StartupCPUWeight = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t CPUShares = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t StartupCPUShares = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t CPUQuotaPerSecUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t CPUQuotaPeriodUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly ay AllowedCPUs = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly ay AllowedMemoryNodes = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b IOAccounting = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IOWeight = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t StartupIOWeight = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) IODeviceWeight = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) IOReadBandwidthMax = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) IOWriteBandwidthMax = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) IOReadIOPSMax = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) IOWriteIOPSMax = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) IODeviceLatencyTargetUSec = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b BlockIOAccounting = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t BlockIOWeight = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t StartupBlockIOWeight = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) BlockIODeviceWeight = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) BlockIOReadBandwidth = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) BlockIOWriteBandwidth = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b MemoryAccounting = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t DefaultMemoryLow = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t DefaultMemoryMin = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryMin = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryLow = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryHigh = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemorySwapMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryLimit = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly s DevicePolicy = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(ss) DeviceAllow = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b TasksAccounting = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t TasksMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b IPAccounting = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(iayu) IPAddressAllow = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(iayu) IPAddressDeny = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly as IPIngressFilterPath = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly as IPEgressFilterPath = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly as DisableControllers = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as Environment = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly a(sb) EnvironmentFiles = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as PassEnvironment = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as UnsetEnvironment = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u UMask = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitCPU = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitCPUSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitFSIZE = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitFSIZESoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitDATA = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitDATASoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitSTACK = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitSTACKSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitCORE = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitCORESoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitRSS = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitRSSSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitNOFILE = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitNOFILESoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitAS = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitASSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitNPROC = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitNPROCSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitMEMLOCK = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitMEMLOCKSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitLOCKS = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitLOCKSSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitSIGPENDING = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitSIGPENDINGSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitMSGQUEUE = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitMSGQUEUESoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitNICE = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitNICESoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitRTPRIO = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitRTPRIOSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitRTTIME = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitRTTIMESoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s WorkingDirectory = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s RootDirectory = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s RootImage = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i OOMScoreAdjust = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t CoredumpFilter = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i Nice = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i IOSchedulingClass = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i IOSchedulingPriority = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i CPUSchedulingPolicy = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i CPUSchedulingPriority = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly ay CPUAffinity = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b CPUAffinityFromNUMA = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i NUMAPolicy = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly ay NUMAMask = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t TimerSlackNSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b CPUSchedulingResetOnFork = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b NonBlocking = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s StandardInput = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s StandardInputFileDescriptorName = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly ay StandardInputData = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s StandardOutput = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s StandardOutputFileDescriptorName = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s StandardError = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s StandardErrorFileDescriptorName = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s TTYPath = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b TTYReset = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b TTYVHangup = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b TTYVTDisallocate = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i SyslogPriority = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s SyslogIdentifier = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b SyslogLevelPrefix = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i SyslogLevel = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i SyslogFacility = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i LogLevelMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LogRateLimitIntervalUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u LogRateLimitBurst = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly aay LogExtraFields = [[...], ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s LogNamespace = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i SecureBits = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t CapabilityBoundingSet = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t AmbientCapabilities = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s User = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s Group = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b DynamicUser = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b RemoveIPC = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as SupplementaryGroups = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s PAMName = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as ReadWritePaths = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as ReadOnlyPaths = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as InaccessiblePaths = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t MountFlags = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b PrivateTmp = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b PrivateDevices = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b ProtectClock = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b ProtectKernelTunables = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b ProtectKernelModules = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b ProtectKernelLogs = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b ProtectControlGroups = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b PrivateNetwork = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b PrivateUsers = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b PrivateMounts = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s ProtectHome = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s ProtectSystem = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b SameProcessGroup = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s UtmpIdentifier = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s UtmpMode = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly (bs) SELinuxContext = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly (bs) AppArmorProfile = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly (bs) SmackProcessLabel = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b IgnoreSIGPIPE = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b NoNewPrivileges = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly (bas) SystemCallFilter = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as SystemCallArchitectures = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i SystemCallErrorNumber = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s Personality = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b LockPersonality = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly (bas) RestrictAddressFamilies = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s RuntimeDirectoryPreserve = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u RuntimeDirectoryMode = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as RuntimeDirectory = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u StateDirectoryMode = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as StateDirectory = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u CacheDirectoryMode = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as CacheDirectory = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u LogsDirectoryMode = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as LogsDirectory = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u ConfigurationDirectoryMode = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as ConfigurationDirectory = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t TimeoutCleanUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b MemoryDenyWriteExecute = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b RestrictRealtime = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b RestrictSUIDSGID = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t RestrictNamespaces = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly a(ssbt) BindPaths = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly a(ssbt) BindReadOnlyPaths = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly a(ss) TemporaryFileSystem = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b MountAPIVFS = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s KeyringMode = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b ProtectHostname = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s NetworkNamespacePath = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s KillMode = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i KillSignal = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i RestartKillSignal = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i FinalKillSignal = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b SendSIGKILL = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b SendSIGHUP = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i WatchdogSignal = ...;
+  };
+  interface org.freedesktop.DBus.Peer { ... };
+  interface org.freedesktop.DBus.Introspectable { ... };
+  interface org.freedesktop.DBus.Properties { ... };
+  interface org.freedesktop.systemd1.Unit { ... };
+};
+    </programlisting>
+
+    <!--method GetProcesses is not documented!-->
+
+    <!--method AttachProcesses is not documented!-->
+
+    <!--property Where is not documented!-->
+
+    <!--property What is not documented!-->
+
+    <!--property Options is not documented!-->
+
+    <!--property Type is not documented!-->
+
+    <!--property TimeoutUSec is not documented!-->
+
+    <!--property DirectoryMode is not documented!-->
+
+    <!--property SloppyOptions is not documented!-->
+
+    <!--property LazyUnmount is not documented!-->
+
+    <!--property ForceUnmount is not documented!-->
+
+    <!--property UID is not documented!-->
+
+    <!--property GID is not documented!-->
+
+    <!--property ExecUnmount is not documented!-->
+
+    <!--property ExecRemount is not documented!-->
+
+    <!--property Slice is not documented!-->
+
+    <!--property MemoryCurrent is not documented!-->
+
+    <!--property CPUUsageNSec is not documented!-->
+
+    <!--property EffectiveCPUs is not documented!-->
+
+    <!--property EffectiveMemoryNodes is not documented!-->
+
+    <!--property TasksCurrent is not documented!-->
+
+    <!--property IPIngressBytes is not documented!-->
+
+    <!--property IPIngressPackets is not documented!-->
+
+    <!--property IPEgressBytes is not documented!-->
+
+    <!--property IPEgressPackets is not documented!-->
+
+    <!--property IOReadBytes is not documented!-->
+
+    <!--property IOReadOperations is not documented!-->
+
+    <!--property IOWriteBytes is not documented!-->
+
+    <!--property IOWriteOperations is not documented!-->
+
+    <!--property Delegate is not documented!-->
+
+    <!--property DelegateControllers is not documented!-->
+
+    <!--property CPUAccounting is not documented!-->
+
+    <!--property CPUWeight is not documented!-->
+
+    <!--property StartupCPUWeight is not documented!-->
+
+    <!--property CPUShares is not documented!-->
+
+    <!--property StartupCPUShares is not documented!-->
+
+    <!--property CPUQuotaPerSecUSec is not documented!-->
+
+    <!--property CPUQuotaPeriodUSec is not documented!-->
+
+    <!--property AllowedCPUs is not documented!-->
+
+    <!--property AllowedMemoryNodes is not documented!-->
+
+    <!--property IOAccounting is not documented!-->
+
+    <!--property IOWeight is not documented!-->
+
+    <!--property StartupIOWeight is not documented!-->
+
+    <!--property IODeviceWeight is not documented!-->
+
+    <!--property IOReadBandwidthMax is not documented!-->
+
+    <!--property IOWriteBandwidthMax is not documented!-->
+
+    <!--property IOReadIOPSMax is not documented!-->
+
+    <!--property IOWriteIOPSMax is not documented!-->
+
+    <!--property IODeviceLatencyTargetUSec is not documented!-->
+
+    <!--property BlockIOAccounting is not documented!-->
+
+    <!--property BlockIOWeight is not documented!-->
+
+    <!--property StartupBlockIOWeight is not documented!-->
+
+    <!--property BlockIODeviceWeight is not documented!-->
+
+    <!--property BlockIOReadBandwidth is not documented!-->
+
+    <!--property BlockIOWriteBandwidth is not documented!-->
+
+    <!--property MemoryAccounting is not documented!-->
+
+    <!--property DefaultMemoryLow is not documented!-->
+
+    <!--property DefaultMemoryMin is not documented!-->
+
+    <!--property MemoryMin is not documented!-->
+
+    <!--property MemoryLow is not documented!-->
+
+    <!--property MemoryHigh is not documented!-->
+
+    <!--property MemoryMax is not documented!-->
+
+    <!--property MemorySwapMax is not documented!-->
+
+    <!--property MemoryLimit is not documented!-->
+
+    <!--property DevicePolicy is not documented!-->
+
+    <!--property DeviceAllow is not documented!-->
+
+    <!--property TasksAccounting is not documented!-->
+
+    <!--property TasksMax is not documented!-->
+
+    <!--property IPAccounting is not documented!-->
+
+    <!--property IPAddressAllow is not documented!-->
+
+    <!--property IPAddressDeny is not documented!-->
+
+    <!--property IPIngressFilterPath is not documented!-->
+
+    <!--property IPEgressFilterPath is not documented!-->
+
+    <!--property DisableControllers is not documented!-->
+
+    <!--property EnvironmentFiles is not documented!-->
+
+    <!--property PassEnvironment is not documented!-->
+
+    <!--property UnsetEnvironment is not documented!-->
+
+    <!--property UMask is not documented!-->
+
+    <!--property LimitCPUSoft is not documented!-->
+
+    <!--property LimitFSIZE is not documented!-->
+
+    <!--property LimitFSIZESoft is not documented!-->
+
+    <!--property LimitDATA is not documented!-->
+
+    <!--property LimitDATASoft is not documented!-->
+
+    <!--property LimitSTACK is not documented!-->
+
+    <!--property LimitSTACKSoft is not documented!-->
+
+    <!--property LimitCORE is not documented!-->
+
+    <!--property LimitCORESoft is not documented!-->
+
+    <!--property LimitRSS is not documented!-->
+
+    <!--property LimitRSSSoft is not documented!-->
+
+    <!--property LimitNOFILE is not documented!-->
+
+    <!--property LimitNOFILESoft is not documented!-->
+
+    <!--property LimitAS is not documented!-->
+
+    <!--property LimitASSoft is not documented!-->
+
+    <!--property LimitNPROC is not documented!-->
+
+    <!--property LimitNPROCSoft is not documented!-->
+
+    <!--property LimitMEMLOCK is not documented!-->
+
+    <!--property LimitMEMLOCKSoft is not documented!-->
+
+    <!--property LimitLOCKS is not documented!-->
+
+    <!--property LimitLOCKSSoft is not documented!-->
+
+    <!--property LimitSIGPENDING is not documented!-->
+
+    <!--property LimitSIGPENDINGSoft is not documented!-->
+
+    <!--property LimitMSGQUEUE is not documented!-->
+
+    <!--property LimitMSGQUEUESoft is not documented!-->
+
+    <!--property LimitNICE is not documented!-->
+
+    <!--property LimitNICESoft is not documented!-->
+
+    <!--property LimitRTPRIO is not documented!-->
+
+    <!--property LimitRTPRIOSoft is not documented!-->
+
+    <!--property LimitRTTIME is not documented!-->
+
+    <!--property LimitRTTIMESoft is not documented!-->
+
+    <!--property WorkingDirectory is not documented!-->
+
+    <!--property RootDirectory is not documented!-->
+
+    <!--property RootImage is not documented!-->
+
+    <!--property OOMScoreAdjust is not documented!-->
+
+    <!--property CoredumpFilter is not documented!-->
+
+    <!--property Nice is not documented!-->
+
+    <!--property IOSchedulingClass is not documented!-->
+
+    <!--property IOSchedulingPriority is not documented!-->
+
+    <!--property CPUSchedulingPolicy is not documented!-->
+
+    <!--property CPUSchedulingPriority is not documented!-->
+
+    <!--property CPUAffinity is not documented!-->
+
+    <!--property CPUAffinityFromNUMA is not documented!-->
+
+    <!--property NUMAPolicy is not documented!-->
+
+    <!--property NUMAMask is not documented!-->
+
+    <!--property TimerSlackNSec is not documented!-->
+
+    <!--property CPUSchedulingResetOnFork is not documented!-->
+
+    <!--property NonBlocking is not documented!-->
+
+    <!--property StandardInput is not documented!-->
+
+    <!--property StandardInputFileDescriptorName is not documented!-->
+
+    <!--property StandardInputData is not documented!-->
+
+    <!--property StandardOutput is not documented!-->
+
+    <!--property StandardOutputFileDescriptorName is not documented!-->
+
+    <!--property StandardError is not documented!-->
+
+    <!--property StandardErrorFileDescriptorName is not documented!-->
+
+    <!--property TTYPath is not documented!-->
+
+    <!--property TTYReset is not documented!-->
+
+    <!--property TTYVHangup is not documented!-->
+
+    <!--property TTYVTDisallocate is not documented!-->
+
+    <!--property SyslogPriority is not documented!-->
+
+    <!--property SyslogIdentifier is not documented!-->
+
+    <!--property SyslogLevelPrefix is not documented!-->
+
+    <!--property SyslogLevel is not documented!-->
+
+    <!--property SyslogFacility is not documented!-->
+
+    <!--property LogLevelMax is not documented!-->
+
+    <!--property LogRateLimitIntervalUSec is not documented!-->
+
+    <!--property LogRateLimitBurst is not documented!-->
+
+    <!--property LogExtraFields is not documented!-->
+
+    <!--property LogNamespace is not documented!-->
+
+    <!--property AmbientCapabilities is not documented!-->
+
+    <!--property User is not documented!-->
+
+    <!--property Group is not documented!-->
+
+    <!--property DynamicUser is not documented!-->
+
+    <!--property RemoveIPC is not documented!-->
+
+    <!--property SupplementaryGroups is not documented!-->
+
+    <!--property PAMName is not documented!-->
+
+    <!--property ReadWritePaths is not documented!-->
+
+    <!--property ReadOnlyPaths is not documented!-->
+
+    <!--property InaccessiblePaths is not documented!-->
+
+    <!--property PrivateTmp is not documented!-->
+
+    <!--property PrivateDevices is not documented!-->
+
+    <!--property ProtectClock is not documented!-->
+
+    <!--property ProtectKernelTunables is not documented!-->
+
+    <!--property ProtectKernelModules is not documented!-->
+
+    <!--property ProtectKernelLogs is not documented!-->
+
+    <!--property ProtectControlGroups is not documented!-->
+
+    <!--property PrivateNetwork is not documented!-->
+
+    <!--property PrivateUsers is not documented!-->
+
+    <!--property PrivateMounts is not documented!-->
+
+    <!--property ProtectHome is not documented!-->
+
+    <!--property ProtectSystem is not documented!-->
+
+    <!--property SameProcessGroup is not documented!-->
+
+    <!--property UtmpIdentifier is not documented!-->
+
+    <!--property UtmpMode is not documented!-->
+
+    <!--property SELinuxContext is not documented!-->
+
+    <!--property AppArmorProfile is not documented!-->
+
+    <!--property SmackProcessLabel is not documented!-->
+
+    <!--property IgnoreSIGPIPE is not documented!-->
+
+    <!--property NoNewPrivileges is not documented!-->
+
+    <!--property SystemCallFilter is not documented!-->
+
+    <!--property SystemCallArchitectures is not documented!-->
+
+    <!--property SystemCallErrorNumber is not documented!-->
+
+    <!--property Personality is not documented!-->
+
+    <!--property LockPersonality is not documented!-->
+
+    <!--property RestrictAddressFamilies is not documented!-->
+
+    <!--property RuntimeDirectoryPreserve is not documented!-->
+
+    <!--property RuntimeDirectoryMode is not documented!-->
+
+    <!--property RuntimeDirectory is not documented!-->
+
+    <!--property StateDirectoryMode is not documented!-->
+
+    <!--property StateDirectory is not documented!-->
+
+    <!--property CacheDirectoryMode is not documented!-->
+
+    <!--property CacheDirectory is not documented!-->
+
+    <!--property LogsDirectoryMode is not documented!-->
+
+    <!--property LogsDirectory is not documented!-->
+
+    <!--property ConfigurationDirectoryMode is not documented!-->
+
+    <!--property ConfigurationDirectory is not documented!-->
+
+    <!--property TimeoutCleanUSec is not documented!-->
+
+    <!--property MemoryDenyWriteExecute is not documented!-->
+
+    <!--property RestrictRealtime is not documented!-->
+
+    <!--property RestrictSUIDSGID is not documented!-->
+
+    <!--property RestrictNamespaces is not documented!-->
+
+    <!--property BindPaths is not documented!-->
+
+    <!--property BindReadOnlyPaths is not documented!-->
+
+    <!--property TemporaryFileSystem is not documented!-->
+
+    <!--property MountAPIVFS is not documented!-->
+
+    <!--property KeyringMode is not documented!-->
+
+    <!--property ProtectHostname is not documented!-->
+
+    <!--property NetworkNamespacePath is not documented!-->
+
+    <!--property KillMode is not documented!-->
+
+    <!--property KillSignal is not documented!-->
+
+    <!--property RestartKillSignal is not documented!-->
+
+    <!--property FinalKillSignal is not documented!-->
+
+    <!--property SendSIGKILL is not documented!-->
+
+    <!--property SendSIGHUP is not documented!-->
+
+    <!--property WatchdogSignal is not documented!-->
+
+    <!--Autogenerated cross-references for systemd.directives, do not edit-->
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Unit"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Mount"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Unit"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Mount"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetProcesses()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="AttachProcesses()"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Where"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="What"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Options"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Type"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TimeoutUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ControlPID"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DirectoryMode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SloppyOptions"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LazyUnmount"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ForceUnmount"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Result"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="UID"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="GID"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ExecMount"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ExecUnmount"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ExecRemount"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Slice"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ControlGroup"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryCurrent"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryNodes"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TasksCurrent"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPIngressBytes"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPIngressPackets"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPEgressBytes"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPEgressPackets"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOReadBytes"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOReadOperations"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOWriteBytes"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOWriteOperations"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Delegate"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DelegateControllers"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUAccounting"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StartupCPUWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUShares"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StartupCPUShares"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUQuotaPerSecUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUQuotaPeriodUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="AllowedCPUs"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="AllowedMemoryNodes"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOAccounting"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StartupIOWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IODeviceWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOReadBandwidthMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOWriteBandwidthMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOReadIOPSMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOWriteIOPSMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IODeviceLatencyTargetUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BlockIOAccounting"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BlockIOWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StartupBlockIOWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BlockIODeviceWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BlockIOReadBandwidth"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BlockIOWriteBandwidth"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryAccounting"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultMemoryLow"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultMemoryMin"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryMin"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryLow"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryHigh"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemorySwapMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryLimit"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DevicePolicy"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DeviceAllow"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TasksAccounting"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TasksMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPAccounting"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPAddressAllow"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPAddressDeny"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPIngressFilterPath"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPEgressFilterPath"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DisableControllers"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Environment"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="EnvironmentFiles"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="PassEnvironment"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="UnsetEnvironment"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="UMask"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitCPU"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitCPUSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitFSIZE"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitFSIZESoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitDATA"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitDATASoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitSTACK"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitSTACKSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitCORE"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitCORESoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitRSS"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitRSSSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitNOFILE"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitNOFILESoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitAS"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitASSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitNPROC"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitNPROCSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitMEMLOCK"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitMEMLOCKSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitLOCKS"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitLOCKSSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitSIGPENDING"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitSIGPENDINGSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitMSGQUEUE"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitMSGQUEUESoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitNICE"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitNICESoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitRTPRIO"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitRTPRIOSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitRTTIME"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitRTTIMESoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="WorkingDirectory"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RootDirectory"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RootImage"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="OOMScoreAdjust"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CoredumpFilter"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Nice"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOSchedulingClass"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOSchedulingPriority"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUSchedulingPolicy"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUSchedulingPriority"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUAffinity"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUAffinityFromNUMA"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="NUMAPolicy"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="NUMAMask"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TimerSlackNSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUSchedulingResetOnFork"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="NonBlocking"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StandardInput"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StandardInputFileDescriptorName"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StandardInputData"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StandardOutput"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StandardOutputFileDescriptorName"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StandardError"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StandardErrorFileDescriptorName"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TTYPath"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TTYReset"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TTYVHangup"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TTYVTDisallocate"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SyslogPriority"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SyslogIdentifier"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SyslogLevelPrefix"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SyslogLevel"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SyslogFacility"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LogLevelMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LogRateLimitIntervalUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LogRateLimitBurst"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LogExtraFields"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LogNamespace"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SecureBits"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CapabilityBoundingSet"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="AmbientCapabilities"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="User"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Group"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DynamicUser"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RemoveIPC"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SupplementaryGroups"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="PAMName"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ReadWritePaths"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ReadOnlyPaths"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="InaccessiblePaths"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MountFlags"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="PrivateTmp"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="PrivateDevices"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ProtectClock"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ProtectKernelTunables"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ProtectKernelModules"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ProtectKernelLogs"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ProtectControlGroups"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="PrivateNetwork"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="PrivateUsers"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="PrivateMounts"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ProtectHome"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ProtectSystem"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SameProcessGroup"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="UtmpIdentifier"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="UtmpMode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SELinuxContext"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="AppArmorProfile"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SmackProcessLabel"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IgnoreSIGPIPE"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="NoNewPrivileges"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SystemCallFilter"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SystemCallArchitectures"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SystemCallErrorNumber"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Personality"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LockPersonality"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RestrictAddressFamilies"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RuntimeDirectoryPreserve"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RuntimeDirectoryMode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RuntimeDirectory"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StateDirectoryMode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StateDirectory"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CacheDirectoryMode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CacheDirectory"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LogsDirectoryMode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LogsDirectory"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ConfigurationDirectoryMode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ConfigurationDirectory"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TimeoutCleanUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryDenyWriteExecute"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RestrictRealtime"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RestrictSUIDSGID"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RestrictNamespaces"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BindPaths"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BindReadOnlyPaths"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TemporaryFileSystem"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MountAPIVFS"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="KeyringMode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ProtectHostname"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="NetworkNamespacePath"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="KillMode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="KillSignal"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RestartKillSignal"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="FinalKillSignal"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SendSIGKILL"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SendSIGHUP"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="WatchdogSignal"/>
+
+    <!--End of Autogenerated section-->
+
+    <refsect2>
+      <title>Properties</title>
+
+      <para>Most of the properties map directly to the corresponding settings in mount unit files. As mount
+      units invoke the <filename>/usr/bin/mount</filename> command, their bus objects include implicit
+      <varname>ExecMount</varname> (and similar) fields which contain information about processes to
+      execute. They also share most of the fields related to the execution context that Service objects
+      expose (see above). In addition to these properties there are the following:</para>
+
+      <para><varname>ControlPID</varname> contains the PID of the currently running
+      <filename>/usr/bin/mount</filename> or <filename>/usr/bin/umount</filename> command if there is one
+      running, otherwise 0.</para>
+
+      <para><varname>Result</varname> contains a value explaining why a mount unit failed if it failed. It
+      can take the values <literal>success</literal>, <literal>resources</literal>,
+      <literal>timeout</literal>, <literal>exit-code</literal>, <literal>signal</literal>, or
+      <literal>core-dump</literal> which have the identical meaning as the corresponding values of the
+      corresponding field of service unit objects (see above).</para>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>Automount Unit Objects</title>
+
+    <para>All automount unit objects implement the
+    <interfacename>org.freedesktop.systemd1.Automount</interfacename> interface (described here) in addition
+    to the generic <interfacename>org.freedesktop.systemd1.Unit</interfacename> interface (see above).</para>
+
+    <programlisting executable="systemd" node="/org/freedesktop/systemd1/unit/proc_2dsys_2dfs_2dbinfmt_5fmisc_2eautomount" interface="org.freedesktop.systemd1.Automount">
+node /org/freedesktop/systemd1/unit/proc_2dsys_2dfs_2dbinfmt_5fmisc_2eautomount {
+  interface org.freedesktop.systemd1.Automount {
+    properties:
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s Where = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u DirectoryMode = ...;
+      readonly s Result = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t TimeoutIdleUSec = ...;
+  };
+  interface org.freedesktop.DBus.Peer { ... };
+  interface org.freedesktop.DBus.Introspectable { ... };
+  interface org.freedesktop.DBus.Properties { ... };
+  interface org.freedesktop.systemd1.Unit { ... };
+};
+    </programlisting>
+
+    <!--property Where is not documented!-->
+
+    <!--property DirectoryMode is not documented!-->
+
+    <!--property TimeoutIdleUSec is not documented!-->
+
+    <!--Autogenerated cross-references for systemd.directives, do not edit-->
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Unit"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Automount"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Unit"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Automount"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Where"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DirectoryMode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Result"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TimeoutIdleUSec"/>
+
+    <!--End of Autogenerated section-->
+
+    <refsect2>
+      <title>Properties</title>
+
+      <para>Most of the properties map directly to the corresponding settings in the automount unit
+      files.</para>
+
+      <para><varname>Result</varname> knows the values <literal>success</literal> and
+      <literal>resources</literal> at this time. They have the same meanings as the corresponding values of
+      the corresponding field of the Service object.</para>
+    </refsect2>
+  </refsect1>
+
+
+  <refsect1>
+    <title>Timer Unit Objects</title>
+
+    <para>All timer unit objects implement the <interfacename>org.freedesktop.systemd1.Timer</interfacename>
+    interface (described here) in addition to the generic
+    <interfacename>org.freedesktop.systemd1.Unit</interfacename> interface (see above).</para>
+
+    <programlisting executable="systemd" node="/org/freedesktop/systemd1/unit/systemd_2dtmpfiles_2dclean_2etimer" interface="org.freedesktop.systemd1.Timer">
+node /org/freedesktop/systemd1/unit/systemd_2dtmpfiles_2dclean_2etimer {
+  interface org.freedesktop.systemd1.Timer {
+    properties:
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s Unit = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates")
+      readonly a(stt) TimersMonotonic = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates")
+      readonly a(sst) TimersCalendar = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b OnClockChange = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b OnTimezoneChange = ...;
+      readonly t NextElapseUSecRealtime = ...;
+      readonly t NextElapseUSecMonotonic = ...;
+      readonly t LastTriggerUSec = ...;
+      readonly t LastTriggerUSecMonotonic = ...;
+      readonly s Result = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t AccuracyUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t RandomizedDelayUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b Persistent = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b WakeSystem = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b RemainAfterElapse = ...;
+  };
+  interface org.freedesktop.DBus.Peer { ... };
+  interface org.freedesktop.DBus.Introspectable { ... };
+  interface org.freedesktop.DBus.Properties { ... };
+  interface org.freedesktop.systemd1.Unit { ... };
+};
+    </programlisting>
+
+    <!--property OnClockChange is not documented!-->
+
+    <!--property OnTimezoneChange is not documented!-->
+
+    <!--property LastTriggerUSec is not documented!-->
+
+    <!--property LastTriggerUSecMonotonic is not documented!-->
+
+    <!--property AccuracyUSec is not documented!-->
+
+    <!--property RandomizedDelayUSec is not documented!-->
+
+    <!--property Persistent is not documented!-->
+
+    <!--property WakeSystem is not documented!-->
+
+    <!--property RemainAfterElapse is not documented!-->
+
+    <!--Autogenerated cross-references for systemd.directives, do not edit-->
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Unit"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Timer"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Unit"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Timer"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Unit"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TimersMonotonic"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TimersCalendar"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="OnClockChange"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="OnTimezoneChange"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="NextElapseUSecRealtime"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="NextElapseUSecMonotonic"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LastTriggerUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LastTriggerUSecMonotonic"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Result"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="AccuracyUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RandomizedDelayUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Persistent"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="WakeSystem"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RemainAfterElapse"/>
+
+    <!--End of Autogenerated section-->
+
+    <refsect2>
+      <title>Properties</title>
+
+      <para><varname>Unit</varname> contains the name of the unit to activate when the timer elapses.</para>
+
+      <para><varname>TimersMonotonic</varname> contains an array of structs that contain information about
+      all monotonic timers of this timer unit. The structs contain a string identifying the timer base, which
+      is one of <literal>OnActiveUSec</literal>, <literal>OnBootUSec</literal>,
+      <literal>OnStartupUSec</literal>, <literal>OnUnitActiveUSec</literal>, or
+      <literal>OnUnitInactiveUSec</literal> which correspond to the settings of the same names in the timer
+      unit files; the microsecond offset from this timer base in monotonic time; the next elapsation point on
+      the <constant>CLOCK_MONOTONIC</constant> clock, relative to its epoch.</para>
+
+      <para><varname>TimersCalendar</varname> contains an array of structs that contain information about all
+      realtime/calendar timers of this timer unit. The structs contain a string identifying the timer base,
+      which may only be <literal>OnCalendar</literal> for now; the calendar specification string; the next
+      elapsation point on the <constant>CLOCK_REALTIME</constant> clock, relative to its epoch.</para>
+
+      <para><varname>NextElapseUSecRealtime</varname> contains the next elapsation point on the
+      <constant>CLOCK_REALTIME</constant> clock in miscroseconds since the epoch, or 0 if this timer event
+      does not include at least one calendar event.</para>
+
+      <para>Similarly, <varname>NextElapseUSecMonotonic</varname> contains the next elapsation point on the
+      <constant>CLOCK_MONOTONIC</constant> clock in microseconds since the epoch, or 0 if this timer event
+      does not include at least one monotonic event.</para>
+
+      <para><varname>Result</varname> knows the values <literal>success</literal> and
+      <literal>resources</literal> with the same meanings as the matching values of the corresponding
+      property of the service interface.</para>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>Swap Unit Objects</title>
+
+    <para>All swap unit objects implement the <interfacename>org.freedesktop.systemd1.Swap</interfacename>
+    interface (described here) in addition to the generic
+    <interfacename>org.freedesktop.systemd1.Unit</interfacename> interface (see above).</para>
+
+    <programlisting executable="systemd" node="/org/freedesktop/systemd1/unit/dev_2dsda3_2eswap" interface="org.freedesktop.systemd1.Swap">
+node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
+  interface org.freedesktop.systemd1.Swap {
+    methods:
+      GetProcesses(out a(sus) processes);
+      AttachProcesses(in  s subcgroup,
+                      in  au pids);
+    properties:
+      readonly s What = '...';
+      readonly i Priority = ...;
+      readonly s Options = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t TimeoutUSec = ...;
+      readonly u ControlPID = ...;
+      readonly s Result = '...';
+      readonly u UID = ...;
+      readonly u GID = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates")
+      readonly a(sasbttttuii) ExecActivate = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("invalidates")
+      readonly a(sasbttttuii) ExecDeactivate = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly s Slice = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly s ControlGroup = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryCurrent = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t CPUUsageNSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly ay EffectiveCPUs = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly ay EffectiveMemoryNodes = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t TasksCurrent = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IPIngressBytes = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IPIngressPackets = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IPEgressBytes = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IPEgressPackets = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IOReadBytes = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IOReadOperations = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IOWriteBytes = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IOWriteOperations = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b Delegate = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly as DelegateControllers = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b CPUAccounting = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t CPUWeight = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t StartupCPUWeight = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t CPUShares = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t StartupCPUShares = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t CPUQuotaPerSecUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t CPUQuotaPeriodUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly ay AllowedCPUs = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly ay AllowedMemoryNodes = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b IOAccounting = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IOWeight = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t StartupIOWeight = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) IODeviceWeight = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) IOReadBandwidthMax = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) IOWriteBandwidthMax = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) IOReadIOPSMax = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) IOWriteIOPSMax = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) IODeviceLatencyTargetUSec = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b BlockIOAccounting = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t BlockIOWeight = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t StartupBlockIOWeight = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) BlockIODeviceWeight = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) BlockIOReadBandwidth = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) BlockIOWriteBandwidth = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b MemoryAccounting = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t DefaultMemoryLow = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t DefaultMemoryMin = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryMin = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryLow = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryHigh = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemorySwapMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryLimit = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly s DevicePolicy = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(ss) DeviceAllow = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b TasksAccounting = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t TasksMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b IPAccounting = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(iayu) IPAddressAllow = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(iayu) IPAddressDeny = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly as IPIngressFilterPath = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly as IPEgressFilterPath = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly as DisableControllers = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as Environment = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly a(sb) EnvironmentFiles = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as PassEnvironment = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as UnsetEnvironment = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u UMask = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitCPU = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitCPUSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitFSIZE = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitFSIZESoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitDATA = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitDATASoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitSTACK = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitSTACKSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitCORE = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitCORESoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitRSS = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitRSSSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitNOFILE = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitNOFILESoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitAS = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitASSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitNPROC = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitNPROCSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitMEMLOCK = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitMEMLOCKSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitLOCKS = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitLOCKSSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitSIGPENDING = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitSIGPENDINGSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitMSGQUEUE = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitMSGQUEUESoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitNICE = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitNICESoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitRTPRIO = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitRTPRIOSoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitRTTIME = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LimitRTTIMESoft = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s WorkingDirectory = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s RootDirectory = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s RootImage = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i OOMScoreAdjust = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t CoredumpFilter = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i Nice = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i IOSchedulingClass = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i IOSchedulingPriority = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i CPUSchedulingPolicy = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i CPUSchedulingPriority = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly ay CPUAffinity = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b CPUAffinityFromNUMA = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i NUMAPolicy = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly ay NUMAMask = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t TimerSlackNSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b CPUSchedulingResetOnFork = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b NonBlocking = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s StandardInput = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s StandardInputFileDescriptorName = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly ay StandardInputData = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s StandardOutput = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s StandardOutputFileDescriptorName = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s StandardError = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s StandardErrorFileDescriptorName = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s TTYPath = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b TTYReset = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b TTYVHangup = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b TTYVTDisallocate = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i SyslogPriority = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s SyslogIdentifier = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b SyslogLevelPrefix = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i SyslogLevel = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i SyslogFacility = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i LogLevelMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t LogRateLimitIntervalUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u LogRateLimitBurst = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly aay LogExtraFields = [[...], ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s LogNamespace = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i SecureBits = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t CapabilityBoundingSet = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t AmbientCapabilities = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s User = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s Group = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b DynamicUser = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b RemoveIPC = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as SupplementaryGroups = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s PAMName = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as ReadWritePaths = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as ReadOnlyPaths = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as InaccessiblePaths = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t MountFlags = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b PrivateTmp = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b PrivateDevices = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b ProtectClock = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b ProtectKernelTunables = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b ProtectKernelModules = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b ProtectKernelLogs = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b ProtectControlGroups = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b PrivateNetwork = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b PrivateUsers = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b PrivateMounts = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s ProtectHome = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s ProtectSystem = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b SameProcessGroup = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s UtmpIdentifier = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s UtmpMode = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly (bs) SELinuxContext = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly (bs) AppArmorProfile = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly (bs) SmackProcessLabel = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b IgnoreSIGPIPE = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b NoNewPrivileges = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly (bas) SystemCallFilter = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as SystemCallArchitectures = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i SystemCallErrorNumber = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s Personality = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b LockPersonality = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly (bas) RestrictAddressFamilies = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s RuntimeDirectoryPreserve = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u RuntimeDirectoryMode = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as RuntimeDirectory = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u StateDirectoryMode = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as StateDirectory = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u CacheDirectoryMode = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as CacheDirectory = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u LogsDirectoryMode = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as LogsDirectory = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u ConfigurationDirectoryMode = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly as ConfigurationDirectory = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t TimeoutCleanUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b MemoryDenyWriteExecute = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b RestrictRealtime = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b RestrictSUIDSGID = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t RestrictNamespaces = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly a(ssbt) BindPaths = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly a(ssbt) BindReadOnlyPaths = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly a(ss) TemporaryFileSystem = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b MountAPIVFS = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s KeyringMode = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b ProtectHostname = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s NetworkNamespacePath = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s KillMode = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i KillSignal = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i RestartKillSignal = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i FinalKillSignal = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b SendSIGKILL = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b SendSIGHUP = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i WatchdogSignal = ...;
+  };
+  interface org.freedesktop.DBus.Peer { ... };
+  interface org.freedesktop.DBus.Introspectable { ... };
+  interface org.freedesktop.DBus.Properties { ... };
+  interface org.freedesktop.systemd1.Unit { ... };
+};
+    </programlisting>
+
+    <!--method GetProcesses is not documented!-->
+
+    <!--method AttachProcesses is not documented!-->
+
+    <!--property What is not documented!-->
+
+    <!--property Priority is not documented!-->
+
+    <!--property Options is not documented!-->
+
+    <!--property TimeoutUSec is not documented!-->
+
+    <!--property UID is not documented!-->
+
+    <!--property GID is not documented!-->
+
+    <!--property ExecDeactivate is not documented!-->
+
+    <!--property Slice is not documented!-->
+
+    <!--property MemoryCurrent is not documented!-->
+
+    <!--property CPUUsageNSec is not documented!-->
+
+    <!--property EffectiveCPUs is not documented!-->
+
+    <!--property EffectiveMemoryNodes is not documented!-->
+
+    <!--property TasksCurrent is not documented!-->
+
+    <!--property IPIngressBytes is not documented!-->
+
+    <!--property IPIngressPackets is not documented!-->
+
+    <!--property IPEgressBytes is not documented!-->
+
+    <!--property IPEgressPackets is not documented!-->
+
+    <!--property IOReadBytes is not documented!-->
+
+    <!--property IOReadOperations is not documented!-->
+
+    <!--property IOWriteBytes is not documented!-->
+
+    <!--property IOWriteOperations is not documented!-->
+
+    <!--property Delegate is not documented!-->
+
+    <!--property DelegateControllers is not documented!-->
+
+    <!--property CPUAccounting is not documented!-->
+
+    <!--property CPUWeight is not documented!-->
+
+    <!--property StartupCPUWeight is not documented!-->
+
+    <!--property CPUShares is not documented!-->
+
+    <!--property StartupCPUShares is not documented!-->
+
+    <!--property CPUQuotaPerSecUSec is not documented!-->
+
+    <!--property CPUQuotaPeriodUSec is not documented!-->
+
+    <!--property AllowedCPUs is not documented!-->
+
+    <!--property AllowedMemoryNodes is not documented!-->
+
+    <!--property IOAccounting is not documented!-->
+
+    <!--property IOWeight is not documented!-->
+
+    <!--property StartupIOWeight is not documented!-->
+
+    <!--property IODeviceWeight is not documented!-->
+
+    <!--property IOReadBandwidthMax is not documented!-->
+
+    <!--property IOWriteBandwidthMax is not documented!-->
+
+    <!--property IOReadIOPSMax is not documented!-->
+
+    <!--property IOWriteIOPSMax is not documented!-->
+
+    <!--property IODeviceLatencyTargetUSec is not documented!-->
+
+    <!--property BlockIOAccounting is not documented!-->
+
+    <!--property BlockIOWeight is not documented!-->
+
+    <!--property StartupBlockIOWeight is not documented!-->
+
+    <!--property BlockIODeviceWeight is not documented!-->
+
+    <!--property BlockIOReadBandwidth is not documented!-->
+
+    <!--property BlockIOWriteBandwidth is not documented!-->
+
+    <!--property MemoryAccounting is not documented!-->
+
+    <!--property DefaultMemoryLow is not documented!-->
+
+    <!--property DefaultMemoryMin is not documented!-->
+
+    <!--property MemoryMin is not documented!-->
+
+    <!--property MemoryLow is not documented!-->
+
+    <!--property MemoryHigh is not documented!-->
+
+    <!--property MemoryMax is not documented!-->
+
+    <!--property MemorySwapMax is not documented!-->
+
+    <!--property MemoryLimit is not documented!-->
+
+    <!--property DevicePolicy is not documented!-->
+
+    <!--property DeviceAllow is not documented!-->
+
+    <!--property TasksAccounting is not documented!-->
+
+    <!--property TasksMax is not documented!-->
+
+    <!--property IPAccounting is not documented!-->
+
+    <!--property IPAddressAllow is not documented!-->
+
+    <!--property IPAddressDeny is not documented!-->
+
+    <!--property IPIngressFilterPath is not documented!-->
+
+    <!--property IPEgressFilterPath is not documented!-->
+
+    <!--property DisableControllers is not documented!-->
+
+    <!--property EnvironmentFiles is not documented!-->
+
+    <!--property PassEnvironment is not documented!-->
+
+    <!--property UnsetEnvironment is not documented!-->
+
+    <!--property UMask is not documented!-->
+
+    <!--property LimitCPUSoft is not documented!-->
+
+    <!--property LimitFSIZE is not documented!-->
+
+    <!--property LimitFSIZESoft is not documented!-->
+
+    <!--property LimitDATA is not documented!-->
+
+    <!--property LimitDATASoft is not documented!-->
+
+    <!--property LimitSTACK is not documented!-->
+
+    <!--property LimitSTACKSoft is not documented!-->
+
+    <!--property LimitCORE is not documented!-->
+
+    <!--property LimitCORESoft is not documented!-->
+
+    <!--property LimitRSS is not documented!-->
+
+    <!--property LimitRSSSoft is not documented!-->
+
+    <!--property LimitNOFILE is not documented!-->
+
+    <!--property LimitNOFILESoft is not documented!-->
+
+    <!--property LimitAS is not documented!-->
+
+    <!--property LimitASSoft is not documented!-->
+
+    <!--property LimitNPROC is not documented!-->
+
+    <!--property LimitNPROCSoft is not documented!-->
+
+    <!--property LimitMEMLOCK is not documented!-->
+
+    <!--property LimitMEMLOCKSoft is not documented!-->
+
+    <!--property LimitLOCKS is not documented!-->
+
+    <!--property LimitLOCKSSoft is not documented!-->
+
+    <!--property LimitSIGPENDING is not documented!-->
+
+    <!--property LimitSIGPENDINGSoft is not documented!-->
+
+    <!--property LimitMSGQUEUE is not documented!-->
+
+    <!--property LimitMSGQUEUESoft is not documented!-->
+
+    <!--property LimitNICE is not documented!-->
+
+    <!--property LimitNICESoft is not documented!-->
+
+    <!--property LimitRTPRIO is not documented!-->
+
+    <!--property LimitRTPRIOSoft is not documented!-->
+
+    <!--property LimitRTTIME is not documented!-->
+
+    <!--property LimitRTTIMESoft is not documented!-->
+
+    <!--property WorkingDirectory is not documented!-->
+
+    <!--property RootDirectory is not documented!-->
+
+    <!--property RootImage is not documented!-->
+
+    <!--property OOMScoreAdjust is not documented!-->
+
+    <!--property CoredumpFilter is not documented!-->
+
+    <!--property Nice is not documented!-->
+
+    <!--property IOSchedulingClass is not documented!-->
+
+    <!--property IOSchedulingPriority is not documented!-->
+
+    <!--property CPUSchedulingPolicy is not documented!-->
+
+    <!--property CPUSchedulingPriority is not documented!-->
+
+    <!--property CPUAffinity is not documented!-->
+
+    <!--property CPUAffinityFromNUMA is not documented!-->
+
+    <!--property NUMAPolicy is not documented!-->
+
+    <!--property NUMAMask is not documented!-->
+
+    <!--property TimerSlackNSec is not documented!-->
+
+    <!--property CPUSchedulingResetOnFork is not documented!-->
+
+    <!--property NonBlocking is not documented!-->
+
+    <!--property StandardInput is not documented!-->
+
+    <!--property StandardInputFileDescriptorName is not documented!-->
+
+    <!--property StandardInputData is not documented!-->
+
+    <!--property StandardOutput is not documented!-->
+
+    <!--property StandardOutputFileDescriptorName is not documented!-->
+
+    <!--property StandardError is not documented!-->
+
+    <!--property StandardErrorFileDescriptorName is not documented!-->
+
+    <!--property TTYPath is not documented!-->
+
+    <!--property TTYReset is not documented!-->
+
+    <!--property TTYVHangup is not documented!-->
+
+    <!--property TTYVTDisallocate is not documented!-->
+
+    <!--property SyslogPriority is not documented!-->
+
+    <!--property SyslogIdentifier is not documented!-->
+
+    <!--property SyslogLevelPrefix is not documented!-->
+
+    <!--property SyslogLevel is not documented!-->
+
+    <!--property SyslogFacility is not documented!-->
+
+    <!--property LogLevelMax is not documented!-->
+
+    <!--property LogRateLimitIntervalUSec is not documented!-->
+
+    <!--property LogRateLimitBurst is not documented!-->
+
+    <!--property LogExtraFields is not documented!-->
+
+    <!--property LogNamespace is not documented!-->
+
+    <!--property AmbientCapabilities is not documented!-->
+
+    <!--property User is not documented!-->
+
+    <!--property Group is not documented!-->
+
+    <!--property DynamicUser is not documented!-->
+
+    <!--property RemoveIPC is not documented!-->
+
+    <!--property SupplementaryGroups is not documented!-->
+
+    <!--property PAMName is not documented!-->
+
+    <!--property ReadWritePaths is not documented!-->
+
+    <!--property ReadOnlyPaths is not documented!-->
+
+    <!--property InaccessiblePaths is not documented!-->
+
+    <!--property PrivateTmp is not documented!-->
+
+    <!--property PrivateDevices is not documented!-->
+
+    <!--property ProtectClock is not documented!-->
+
+    <!--property ProtectKernelTunables is not documented!-->
+
+    <!--property ProtectKernelModules is not documented!-->
+
+    <!--property ProtectKernelLogs is not documented!-->
+
+    <!--property ProtectControlGroups is not documented!-->
+
+    <!--property PrivateNetwork is not documented!-->
+
+    <!--property PrivateUsers is not documented!-->
+
+    <!--property PrivateMounts is not documented!-->
+
+    <!--property ProtectHome is not documented!-->
+
+    <!--property ProtectSystem is not documented!-->
+
+    <!--property SameProcessGroup is not documented!-->
+
+    <!--property UtmpIdentifier is not documented!-->
+
+    <!--property UtmpMode is not documented!-->
+
+    <!--property SELinuxContext is not documented!-->
+
+    <!--property AppArmorProfile is not documented!-->
+
+    <!--property SmackProcessLabel is not documented!-->
+
+    <!--property IgnoreSIGPIPE is not documented!-->
+
+    <!--property NoNewPrivileges is not documented!-->
+
+    <!--property SystemCallFilter is not documented!-->
+
+    <!--property SystemCallArchitectures is not documented!-->
+
+    <!--property SystemCallErrorNumber is not documented!-->
+
+    <!--property Personality is not documented!-->
+
+    <!--property LockPersonality is not documented!-->
+
+    <!--property RestrictAddressFamilies is not documented!-->
+
+    <!--property RuntimeDirectoryPreserve is not documented!-->
+
+    <!--property RuntimeDirectoryMode is not documented!-->
+
+    <!--property RuntimeDirectory is not documented!-->
+
+    <!--property StateDirectoryMode is not documented!-->
+
+    <!--property StateDirectory is not documented!-->
+
+    <!--property CacheDirectoryMode is not documented!-->
+
+    <!--property CacheDirectory is not documented!-->
+
+    <!--property LogsDirectoryMode is not documented!-->
+
+    <!--property LogsDirectory is not documented!-->
+
+    <!--property ConfigurationDirectoryMode is not documented!-->
+
+    <!--property ConfigurationDirectory is not documented!-->
+
+    <!--property TimeoutCleanUSec is not documented!-->
+
+    <!--property MemoryDenyWriteExecute is not documented!-->
+
+    <!--property RestrictRealtime is not documented!-->
+
+    <!--property RestrictSUIDSGID is not documented!-->
+
+    <!--property RestrictNamespaces is not documented!-->
+
+    <!--property BindPaths is not documented!-->
+
+    <!--property BindReadOnlyPaths is not documented!-->
+
+    <!--property TemporaryFileSystem is not documented!-->
+
+    <!--property MountAPIVFS is not documented!-->
+
+    <!--property KeyringMode is not documented!-->
+
+    <!--property ProtectHostname is not documented!-->
+
+    <!--property NetworkNamespacePath is not documented!-->
+
+    <!--property KillMode is not documented!-->
+
+    <!--property KillSignal is not documented!-->
+
+    <!--property RestartKillSignal is not documented!-->
+
+    <!--property FinalKillSignal is not documented!-->
+
+    <!--property SendSIGKILL is not documented!-->
+
+    <!--property SendSIGHUP is not documented!-->
+
+    <!--property WatchdogSignal is not documented!-->
+
+    <!--Autogenerated cross-references for systemd.directives, do not edit-->
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Unit"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Swap"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Unit"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Swap"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetProcesses()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="AttachProcesses()"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="What"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Priority"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Options"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TimeoutUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ControlPID"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Result"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="UID"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="GID"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ExecActivate"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ExecDeactivate"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Slice"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ControlGroup"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryCurrent"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryNodes"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TasksCurrent"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPIngressBytes"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPIngressPackets"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPEgressBytes"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPEgressPackets"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOReadBytes"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOReadOperations"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOWriteBytes"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOWriteOperations"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Delegate"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DelegateControllers"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUAccounting"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StartupCPUWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUShares"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StartupCPUShares"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUQuotaPerSecUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUQuotaPeriodUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="AllowedCPUs"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="AllowedMemoryNodes"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOAccounting"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StartupIOWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IODeviceWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOReadBandwidthMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOWriteBandwidthMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOReadIOPSMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOWriteIOPSMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IODeviceLatencyTargetUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BlockIOAccounting"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BlockIOWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StartupBlockIOWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BlockIODeviceWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BlockIOReadBandwidth"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BlockIOWriteBandwidth"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryAccounting"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultMemoryLow"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultMemoryMin"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryMin"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryLow"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryHigh"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemorySwapMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryLimit"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DevicePolicy"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DeviceAllow"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TasksAccounting"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TasksMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPAccounting"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPAddressAllow"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPAddressDeny"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPIngressFilterPath"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPEgressFilterPath"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DisableControllers"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Environment"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="EnvironmentFiles"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="PassEnvironment"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="UnsetEnvironment"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="UMask"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitCPU"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitCPUSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitFSIZE"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitFSIZESoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitDATA"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitDATASoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitSTACK"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitSTACKSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitCORE"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitCORESoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitRSS"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitRSSSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitNOFILE"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitNOFILESoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitAS"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitASSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitNPROC"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitNPROCSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitMEMLOCK"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitMEMLOCKSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitLOCKS"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitLOCKSSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitSIGPENDING"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitSIGPENDINGSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitMSGQUEUE"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitMSGQUEUESoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitNICE"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitNICESoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitRTPRIO"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitRTPRIOSoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitRTTIME"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LimitRTTIMESoft"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="WorkingDirectory"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RootDirectory"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RootImage"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="OOMScoreAdjust"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CoredumpFilter"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Nice"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOSchedulingClass"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOSchedulingPriority"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUSchedulingPolicy"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUSchedulingPriority"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUAffinity"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUAffinityFromNUMA"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="NUMAPolicy"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="NUMAMask"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TimerSlackNSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUSchedulingResetOnFork"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="NonBlocking"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StandardInput"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StandardInputFileDescriptorName"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StandardInputData"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StandardOutput"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StandardOutputFileDescriptorName"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StandardError"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StandardErrorFileDescriptorName"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TTYPath"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TTYReset"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TTYVHangup"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TTYVTDisallocate"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SyslogPriority"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SyslogIdentifier"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SyslogLevelPrefix"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SyslogLevel"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SyslogFacility"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LogLevelMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LogRateLimitIntervalUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LogRateLimitBurst"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LogExtraFields"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LogNamespace"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SecureBits"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CapabilityBoundingSet"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="AmbientCapabilities"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="User"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Group"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DynamicUser"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RemoveIPC"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SupplementaryGroups"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="PAMName"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ReadWritePaths"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ReadOnlyPaths"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="InaccessiblePaths"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MountFlags"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="PrivateTmp"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="PrivateDevices"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ProtectClock"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ProtectKernelTunables"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ProtectKernelModules"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ProtectKernelLogs"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ProtectControlGroups"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="PrivateNetwork"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="PrivateUsers"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="PrivateMounts"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ProtectHome"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ProtectSystem"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SameProcessGroup"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="UtmpIdentifier"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="UtmpMode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SELinuxContext"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="AppArmorProfile"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SmackProcessLabel"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IgnoreSIGPIPE"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="NoNewPrivileges"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SystemCallFilter"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SystemCallArchitectures"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SystemCallErrorNumber"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Personality"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LockPersonality"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RestrictAddressFamilies"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RuntimeDirectoryPreserve"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RuntimeDirectoryMode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RuntimeDirectory"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StateDirectoryMode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StateDirectory"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CacheDirectoryMode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CacheDirectory"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LogsDirectoryMode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LogsDirectory"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ConfigurationDirectoryMode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ConfigurationDirectory"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TimeoutCleanUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryDenyWriteExecute"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RestrictRealtime"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RestrictSUIDSGID"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RestrictNamespaces"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BindPaths"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BindReadOnlyPaths"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TemporaryFileSystem"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MountAPIVFS"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="KeyringMode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ProtectHostname"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="NetworkNamespacePath"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="KillMode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="KillSignal"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RestartKillSignal"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="FinalKillSignal"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SendSIGKILL"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SendSIGHUP"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="WatchdogSignal"/>
+
+    <!--End of Autogenerated section-->
+
+    <refsect2>
+      <title>Properties</title>
+
+      <para>Most of the properties map directly to the corresponding settings in swap unit files. As mount
+      units invoke the
+      <citerefentry project='man-pages'><refentrytitle>swapon</refentrytitle><manvolnum>8</manvolnum></citerefentry> command,
+      their bus objects include implicit <varname>ExecActivate</varname> (and similar) fields which contain
+      information about processes to execute. They also share most of the fields related to the execution
+      context that Service objects expose (see above). In addition to these properties there are the
+      following:</para>
+
+      <para><varname>ControlPID</varname> contains the PID of the currently running
+      <citerefentry project='man-pages'><refentrytitle>swapon</refentrytitle><manvolnum>8</manvolnum></citerefentry> or
+      <citerefentry project='man-pages'><refentrytitle>swapoff</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+      command if there is one running, otherwise 0.</para>
+
+      <para><varname>Result</varname> contains a value explaining why a mount unit failed if it failed. It
+      can take the values <literal>success</literal>, <literal>resources</literal>,
+      <literal>timeout</literal>, <literal>exit-code</literal>, <literal>signal</literal>, or
+      <literal>core-dump</literal> which have the identical meanings as the corresponding values of the
+      corresponding field of service unit objects (see above).</para>
+    </refsect2>
+  </refsect1>
+
+
+  <refsect1>
+    <title>Path Unit Objects</title>
+
+    <programlisting executable="systemd" node="/org/freedesktop/systemd1/unit/cups_2epath" interface="org.freedesktop.systemd1.Path">
+node /org/freedesktop/systemd1/unit/cups_2epath {
+  interface org.freedesktop.systemd1.Path {
+    properties:
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s Unit = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly a(ss) Paths = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b MakeDirectory = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u DirectoryMode = ...;
+      readonly s Result = '...';
+  };
+  interface org.freedesktop.DBus.Peer { ... };
+  interface org.freedesktop.DBus.Introspectable { ... };
+  interface org.freedesktop.DBus.Properties { ... };
+  interface org.freedesktop.systemd1.Unit { ... };
+};
+    </programlisting>
+
+    <!--property MakeDirectory is not documented!-->
+
+    <!--property DirectoryMode is not documented!-->
+
+    <!--Autogenerated cross-references for systemd.directives, do not edit-->
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Unit"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Path"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Unit"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Path"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Unit"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Paths"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MakeDirectory"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DirectoryMode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Result"/>
+
+    <!--End of Autogenerated section-->
+
+    <refsect2>
+      <title>Properties</title>
+
+      <para>Most properties correspond directly with the matching settings in path unit files.</para>
+
+      <para>The others:</para>
+
+      <para><varname>Paths</varname> contains an array of structs. Each struct contains the condition to
+      watch, which can be one of <literal>PathExists</literal>, <literal>PathExistsGlob</literal>,
+      <literal>PathChanged</literal>, <literal>PathModified</literal>, or <literal>DirectoryNotEmpty</literal>
+      which correspond directly to the matching settings in the path unit files; and the path to watch,
+      possibly including glob expressions.</para>
+
+      <para><varname>Result</varname> contains a result value which can be <literal>success</literal> or
+      <literal>resources</literal> which have the same meaning as the corresponding field of the Service
+      interface.</para>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>Slice Unit Objects</title>
+
+    <para>All slice unit objects implement the <interfacename>org.freedesktop.systemd1.Slice</interfacename>
+    interface (described here) in addition to the generic
+    <interfacename>org.freedesktop.systemd1.Unit</interfacename> interface (see above).</para>
+
+    <programlisting executable="systemd" node="/org/freedesktop/systemd1/unit/system_2eslice" interface="org.freedesktop.systemd1.Slice">
+node /org/freedesktop/systemd1/unit/system_2eslice {
+  interface org.freedesktop.systemd1.Slice {
+    methods:
+      GetProcesses(out a(sus) processes);
+      AttachProcesses(in  s subcgroup,
+                      in  au pids);
+    properties:
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly s Slice = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly s ControlGroup = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryCurrent = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t CPUUsageNSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly ay EffectiveCPUs = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly ay EffectiveMemoryNodes = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t TasksCurrent = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IPIngressBytes = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IPIngressPackets = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IPEgressBytes = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IPEgressPackets = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IOReadBytes = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IOReadOperations = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IOWriteBytes = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IOWriteOperations = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b Delegate = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly as DelegateControllers = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b CPUAccounting = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t CPUWeight = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t StartupCPUWeight = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t CPUShares = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t StartupCPUShares = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t CPUQuotaPerSecUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t CPUQuotaPeriodUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly ay AllowedCPUs = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly ay AllowedMemoryNodes = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b IOAccounting = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IOWeight = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t StartupIOWeight = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) IODeviceWeight = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) IOReadBandwidthMax = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) IOWriteBandwidthMax = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) IOReadIOPSMax = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) IOWriteIOPSMax = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) IODeviceLatencyTargetUSec = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b BlockIOAccounting = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t BlockIOWeight = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t StartupBlockIOWeight = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) BlockIODeviceWeight = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) BlockIOReadBandwidth = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) BlockIOWriteBandwidth = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b MemoryAccounting = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t DefaultMemoryLow = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t DefaultMemoryMin = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryMin = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryLow = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryHigh = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemorySwapMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryLimit = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly s DevicePolicy = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(ss) DeviceAllow = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b TasksAccounting = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t TasksMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b IPAccounting = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(iayu) IPAddressAllow = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(iayu) IPAddressDeny = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly as IPIngressFilterPath = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly as IPEgressFilterPath = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly as DisableControllers = ['...', ...];
+  };
+  interface org.freedesktop.DBus.Peer { ... };
+  interface org.freedesktop.DBus.Introspectable { ... };
+  interface org.freedesktop.DBus.Properties { ... };
+  interface org.freedesktop.systemd1.Unit { ... };
+};
+    </programlisting>
+
+    <!--method GetProcesses is not documented!-->
+
+    <!--method AttachProcesses is not documented!-->
+
+    <!--property Slice is not documented!-->
+
+    <!--property MemoryCurrent is not documented!-->
+
+    <!--property CPUUsageNSec is not documented!-->
+
+    <!--property EffectiveCPUs is not documented!-->
+
+    <!--property EffectiveMemoryNodes is not documented!-->
+
+    <!--property TasksCurrent is not documented!-->
+
+    <!--property IPIngressBytes is not documented!-->
+
+    <!--property IPIngressPackets is not documented!-->
+
+    <!--property IPEgressBytes is not documented!-->
+
+    <!--property IPEgressPackets is not documented!-->
+
+    <!--property IOReadBytes is not documented!-->
+
+    <!--property IOReadOperations is not documented!-->
+
+    <!--property IOWriteBytes is not documented!-->
+
+    <!--property IOWriteOperations is not documented!-->
+
+    <!--property Delegate is not documented!-->
+
+    <!--property DelegateControllers is not documented!-->
+
+    <!--property CPUAccounting is not documented!-->
+
+    <!--property CPUWeight is not documented!-->
+
+    <!--property StartupCPUWeight is not documented!-->
+
+    <!--property CPUShares is not documented!-->
+
+    <!--property StartupCPUShares is not documented!-->
+
+    <!--property CPUQuotaPerSecUSec is not documented!-->
+
+    <!--property CPUQuotaPeriodUSec is not documented!-->
+
+    <!--property AllowedCPUs is not documented!-->
+
+    <!--property AllowedMemoryNodes is not documented!-->
+
+    <!--property IOAccounting is not documented!-->
+
+    <!--property IOWeight is not documented!-->
+
+    <!--property StartupIOWeight is not documented!-->
+
+    <!--property IODeviceWeight is not documented!-->
+
+    <!--property IOReadBandwidthMax is not documented!-->
+
+    <!--property IOWriteBandwidthMax is not documented!-->
+
+    <!--property IOReadIOPSMax is not documented!-->
+
+    <!--property IOWriteIOPSMax is not documented!-->
+
+    <!--property IODeviceLatencyTargetUSec is not documented!-->
+
+    <!--property BlockIOAccounting is not documented!-->
+
+    <!--property BlockIOWeight is not documented!-->
+
+    <!--property StartupBlockIOWeight is not documented!-->
+
+    <!--property BlockIODeviceWeight is not documented!-->
+
+    <!--property BlockIOReadBandwidth is not documented!-->
+
+    <!--property BlockIOWriteBandwidth is not documented!-->
+
+    <!--property MemoryAccounting is not documented!-->
+
+    <!--property DefaultMemoryLow is not documented!-->
+
+    <!--property DefaultMemoryMin is not documented!-->
+
+    <!--property MemoryMin is not documented!-->
+
+    <!--property MemoryLow is not documented!-->
+
+    <!--property MemoryHigh is not documented!-->
+
+    <!--property MemoryMax is not documented!-->
+
+    <!--property MemorySwapMax is not documented!-->
+
+    <!--property MemoryLimit is not documented!-->
+
+    <!--property DevicePolicy is not documented!-->
+
+    <!--property DeviceAllow is not documented!-->
+
+    <!--property TasksAccounting is not documented!-->
+
+    <!--property TasksMax is not documented!-->
+
+    <!--property IPAccounting is not documented!-->
+
+    <!--property IPAddressAllow is not documented!-->
+
+    <!--property IPAddressDeny is not documented!-->
+
+    <!--property IPIngressFilterPath is not documented!-->
+
+    <!--property IPEgressFilterPath is not documented!-->
+
+    <!--property DisableControllers is not documented!-->
+
+    <!--Autogenerated cross-references for systemd.directives, do not edit-->
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Unit"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Slice"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Unit"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Slice"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetProcesses()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="AttachProcesses()"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Slice"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ControlGroup"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryCurrent"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryNodes"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TasksCurrent"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPIngressBytes"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPIngressPackets"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPEgressBytes"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPEgressPackets"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOReadBytes"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOReadOperations"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOWriteBytes"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOWriteOperations"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Delegate"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DelegateControllers"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUAccounting"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StartupCPUWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUShares"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StartupCPUShares"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUQuotaPerSecUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUQuotaPeriodUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="AllowedCPUs"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="AllowedMemoryNodes"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOAccounting"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StartupIOWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IODeviceWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOReadBandwidthMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOWriteBandwidthMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOReadIOPSMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOWriteIOPSMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IODeviceLatencyTargetUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BlockIOAccounting"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BlockIOWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StartupBlockIOWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BlockIODeviceWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BlockIOReadBandwidth"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BlockIOWriteBandwidth"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryAccounting"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultMemoryLow"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultMemoryMin"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryMin"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryLow"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryHigh"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemorySwapMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryLimit"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DevicePolicy"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DeviceAllow"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TasksAccounting"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TasksMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPAccounting"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPAddressAllow"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPAddressDeny"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPIngressFilterPath"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPEgressFilterPath"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DisableControllers"/>
+
+    <!--End of Autogenerated section-->
+
+    <refsect2>
+      <title>Properties</title>
+
+      <para>Most properties correspond directly with the matching settings in slice unit files.</para>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>Scope Unit Objects</title>
+
+    <para>All slice unit objects implement the <interfacename>org.freedesktop.systemd1.Scope</interfacename>
+    interface (described here) in addition to the generic
+    <interfacename>org.freedesktop.systemd1.Unit</interfacename> interface (see above).</para>
+
+    <programlisting executable="systemd" node="/org/freedesktop/systemd1/unit/session_2d1_2escope" interface="org.freedesktop.systemd1.Scope">
+node /org/freedesktop/systemd1/unit/session_2d1_2escope {
+  interface org.freedesktop.systemd1.Scope {
+    methods:
+      Abandon();
+      GetProcesses(out a(sus) processes);
+      AttachProcesses(in  s subcgroup,
+                      in  au pids);
+    signals:
+      RequestStop();
+    properties:
+      readonly s Controller = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t TimeoutStopUSec = ...;
+      readonly s Result = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly t RuntimeMaxUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly s Slice = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly s ControlGroup = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryCurrent = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t CPUUsageNSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly ay EffectiveCPUs = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly ay EffectiveMemoryNodes = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t TasksCurrent = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IPIngressBytes = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IPIngressPackets = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IPEgressBytes = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IPEgressPackets = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IOReadBytes = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IOReadOperations = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IOWriteBytes = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IOWriteOperations = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b Delegate = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly as DelegateControllers = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b CPUAccounting = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t CPUWeight = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t StartupCPUWeight = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t CPUShares = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t StartupCPUShares = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t CPUQuotaPerSecUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t CPUQuotaPeriodUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly ay AllowedCPUs = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly ay AllowedMemoryNodes = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b IOAccounting = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t IOWeight = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t StartupIOWeight = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) IODeviceWeight = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) IOReadBandwidthMax = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) IOWriteBandwidthMax = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) IOReadIOPSMax = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) IOWriteIOPSMax = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) IODeviceLatencyTargetUSec = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b BlockIOAccounting = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t BlockIOWeight = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t StartupBlockIOWeight = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) BlockIODeviceWeight = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) BlockIOReadBandwidth = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(st) BlockIOWriteBandwidth = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b MemoryAccounting = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t DefaultMemoryLow = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t DefaultMemoryMin = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryMin = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryLow = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryHigh = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemorySwapMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t MemoryLimit = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly s DevicePolicy = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(ss) DeviceAllow = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b TasksAccounting = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t TasksMax = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b IPAccounting = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(iayu) IPAddressAllow = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly a(iayu) IPAddressDeny = [...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly as IPIngressFilterPath = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly as IPEgressFilterPath = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly as DisableControllers = ['...', ...];
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s KillMode = '...';
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i KillSignal = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i RestartKillSignal = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i FinalKillSignal = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b SendSIGKILL = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly b SendSIGHUP = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly i WatchdogSignal = ...;
+  };
+  interface org.freedesktop.DBus.Peer { ... };
+  interface org.freedesktop.DBus.Introspectable { ... };
+  interface org.freedesktop.DBus.Properties { ... };
+  interface org.freedesktop.systemd1.Unit { ... };
+};
+    </programlisting>
+
+    <!--method GetProcesses is not documented!-->
+
+    <!--method AttachProcesses is not documented!-->
+
+    <!--property TimeoutStopUSec is not documented!-->
+
+    <!--property RuntimeMaxUSec is not documented!-->
+
+    <!--property Slice is not documented!-->
+
+    <!--property MemoryCurrent is not documented!-->
+
+    <!--property CPUUsageNSec is not documented!-->
+
+    <!--property EffectiveCPUs is not documented!-->
+
+    <!--property EffectiveMemoryNodes is not documented!-->
+
+    <!--property TasksCurrent is not documented!-->
+
+    <!--property IPIngressBytes is not documented!-->
+
+    <!--property IPIngressPackets is not documented!-->
+
+    <!--property IPEgressBytes is not documented!-->
+
+    <!--property IPEgressPackets is not documented!-->
+
+    <!--property IOReadBytes is not documented!-->
+
+    <!--property IOReadOperations is not documented!-->
+
+    <!--property IOWriteBytes is not documented!-->
+
+    <!--property IOWriteOperations is not documented!-->
+
+    <!--property Delegate is not documented!-->
+
+    <!--property DelegateControllers is not documented!-->
+
+    <!--property CPUAccounting is not documented!-->
+
+    <!--property CPUWeight is not documented!-->
+
+    <!--property StartupCPUWeight is not documented!-->
+
+    <!--property CPUShares is not documented!-->
+
+    <!--property StartupCPUShares is not documented!-->
+
+    <!--property CPUQuotaPerSecUSec is not documented!-->
+
+    <!--property CPUQuotaPeriodUSec is not documented!-->
+
+    <!--property AllowedCPUs is not documented!-->
+
+    <!--property AllowedMemoryNodes is not documented!-->
+
+    <!--property IOAccounting is not documented!-->
+
+    <!--property IOWeight is not documented!-->
+
+    <!--property StartupIOWeight is not documented!-->
+
+    <!--property IODeviceWeight is not documented!-->
+
+    <!--property IOReadBandwidthMax is not documented!-->
+
+    <!--property IOWriteBandwidthMax is not documented!-->
+
+    <!--property IOReadIOPSMax is not documented!-->
+
+    <!--property IOWriteIOPSMax is not documented!-->
+
+    <!--property IODeviceLatencyTargetUSec is not documented!-->
+
+    <!--property BlockIOAccounting is not documented!-->
+
+    <!--property BlockIOWeight is not documented!-->
+
+    <!--property StartupBlockIOWeight is not documented!-->
+
+    <!--property BlockIODeviceWeight is not documented!-->
+
+    <!--property BlockIOReadBandwidth is not documented!-->
+
+    <!--property BlockIOWriteBandwidth is not documented!-->
+
+    <!--property MemoryAccounting is not documented!-->
+
+    <!--property DefaultMemoryLow is not documented!-->
+
+    <!--property DefaultMemoryMin is not documented!-->
+
+    <!--property MemoryMin is not documented!-->
+
+    <!--property MemoryLow is not documented!-->
+
+    <!--property MemoryHigh is not documented!-->
+
+    <!--property MemoryMax is not documented!-->
+
+    <!--property MemorySwapMax is not documented!-->
+
+    <!--property MemoryLimit is not documented!-->
+
+    <!--property DevicePolicy is not documented!-->
+
+    <!--property DeviceAllow is not documented!-->
+
+    <!--property TasksAccounting is not documented!-->
+
+    <!--property TasksMax is not documented!-->
+
+    <!--property IPAccounting is not documented!-->
+
+    <!--property IPAddressAllow is not documented!-->
+
+    <!--property IPAddressDeny is not documented!-->
+
+    <!--property IPIngressFilterPath is not documented!-->
+
+    <!--property IPEgressFilterPath is not documented!-->
+
+    <!--property DisableControllers is not documented!-->
+
+    <!--property KillMode is not documented!-->
+
+    <!--property KillSignal is not documented!-->
+
+    <!--property RestartKillSignal is not documented!-->
+
+    <!--property FinalKillSignal is not documented!-->
+
+    <!--property SendSIGKILL is not documented!-->
+
+    <!--property SendSIGHUP is not documented!-->
+
+    <!--property WatchdogSignal is not documented!-->
+
+    <!--Autogenerated cross-references for systemd.directives, do not edit-->
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Unit"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Scope"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Unit"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Scope"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Abandon()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetProcesses()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="AttachProcesses()"/>
+
+    <variablelist class="dbus-signal" generated="True" extra-ref="RequestStop"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Controller"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TimeoutStopUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Result"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RuntimeMaxUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Slice"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="ControlGroup"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryCurrent"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUUsageNSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveCPUs"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="EffectiveMemoryNodes"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TasksCurrent"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPIngressBytes"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPIngressPackets"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPEgressBytes"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPEgressPackets"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOReadBytes"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOReadOperations"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOWriteBytes"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOWriteOperations"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Delegate"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DelegateControllers"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUAccounting"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StartupCPUWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUShares"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StartupCPUShares"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUQuotaPerSecUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CPUQuotaPeriodUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="AllowedCPUs"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="AllowedMemoryNodes"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOAccounting"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StartupIOWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IODeviceWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOReadBandwidthMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOWriteBandwidthMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOReadIOPSMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IOWriteIOPSMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IODeviceLatencyTargetUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BlockIOAccounting"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BlockIOWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="StartupBlockIOWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BlockIODeviceWeight"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BlockIOReadBandwidth"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="BlockIOWriteBandwidth"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryAccounting"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultMemoryLow"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DefaultMemoryMin"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryMin"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryLow"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryHigh"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemorySwapMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="MemoryLimit"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DevicePolicy"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DeviceAllow"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TasksAccounting"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TasksMax"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPAccounting"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPAddressAllow"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPAddressDeny"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPIngressFilterPath"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="IPEgressFilterPath"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="DisableControllers"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="KillMode"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="KillSignal"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RestartKillSignal"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="FinalKillSignal"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SendSIGKILL"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="SendSIGHUP"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="WatchdogSignal"/>
+
+    <!--End of Autogenerated section-->
+
+    <refsect2>
+      <title>Methods</title>
+
+      <para><function>Abandon()</function> may be used to place a scope unit in the "abandoned" state. This
+      may be used to inform the system manager that the manager that created the scope lost interest in the
+      scope (for example, because it is terminating), without wanting to shut down the scope entirely.</para>
+    </refsect2>
+
+    <refsect2>
+      <title>Signals</title>
+
+      <para><function>RequestStop</function> is sent to the peer that is configured in the
+      <varname>Controller</varname> property when systemd is requested to terminate the scope unit. A program
+      registering a scope can use this to cleanly shut down the processes it added to the scope instead of
+      letting systemd do it with the usual <constant>SIGTERM</constant> logic.</para>
+    </refsect2>
+
+    <refsect2>
+      <title>Properties</title>
+
+      <para>All properties correspond directly with the matching properties of service units.</para>
+
+      <para><varname>Controller</varname> contains the bus name (unique or well-known) that is notified when
+      the scope unit is to be shut down via a <function>RequestStop</function> signal (see below). This is
+      set when the scope is created. If not set, the scope's processes will terminated with
+      <constant>SIGTERM</constant> directly.</para>
+    </refsect2>
+  </refsect1>
+
+
+  <refsect1>
+    <title>Job Objects</title>
+
+    <para>Job objects encapsulate scheduled or running jobs. Each unit can have none or one jobs in the
+    execution queue. Each job is attached to exactly one unit.</para>
+
+    <programlisting executable="systemd" node="/org/freedesktop/systemd1/job/666" interface="org.freedesktop.systemd1.Job">
+node /org/freedesktop/systemd1/job/666 {
+  interface org.freedesktop.systemd1.Job {
+    methods:
+      Cancel();
+      GetAfter(out a(usssoo) jobs);
+      GetBefore(out a(usssoo) jobs);
+    properties:
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly u Id = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly (so) Unit = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+      readonly s JobType = '...';
+      readonly s State = '...';
+  };
+  interface org.freedesktop.DBus.Peer { ... };
+  interface org.freedesktop.DBus.Introspectable { ... };
+  interface org.freedesktop.DBus.Properties { ... };
+};
+    </programlisting>
+
+    <!--method GetAfter is not documented!-->
+
+    <!--method GetBefore is not documented!-->
+
+    <!--Autogenerated cross-references for systemd.directives, do not edit-->
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Job"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.systemd1.Job"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="Cancel()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetAfter()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="GetBefore()"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Id"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Unit"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="JobType"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="State"/>
+
+    <!--End of Autogenerated section-->
+
+    <refsect2>
+      <title>Methods</title>
+
+      <para><function>Cancel()</function> cancels the job. Note that this will remove a job from the queue if
+      it is not yet executed but generally will not cause a job that is already in the process of being
+      executed to be aborted. This operation may also be requested via the <function>CancelJob()</function>
+      method of the Manager object (see above), which is sometimes useful to reduce roundtrips.</para>
+    </refsect2>
+
+    <refsect2>
+      <title>Properties</title>
+
+      <para><varname>Id</varname> is the numeric Id of the job. During the runtime of a systemd instance each
+      numeric ID is only assigned once.</para>
+
+      <para><varname>Unit</varname> refers to the unit this job belongs to. It is a structure consisting of
+      the name of the unit and a bus path to the unit's object.</para>
+
+      <para><varname>JobType</varname> refers to the job's type and is one of <literal>start</literal>,
+      <literal>verify-active</literal>, <literal>stop</literal>, <literal>reload</literal>,
+      <literal>restart</literal>, <literal>try-restart</literal>, or <literal>reload-or-start</literal>. Note
+      that later versions might define additional values.</para>
+
+      <para><varname>State</varname> refers to the job's state and is one of <literal>waiting</literal> and
+      <literal>running</literal>. The former indicates that a job is currently queued but has not begun to
+      execute yet. The latter indicates that a job is currently being executed.</para>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>Examples</title>
+
+    <example>
+      <title>Introspect <interfacename>org.freedesktop.systemd1.Manager</interfacename> on the bus</title>
+
+      <programlisting>
+$ gdbus introspect --system \
+        --dest org.freedesktop.systemd1 \
+        --object-path /org/freedesktop/systemd1
+      </programlisting>
+    </example>
+
+    <example>
+      <title>Introspect a unit on the bus</title>
+
+      <programlisting>
+$ busctl introspect org.freedesktop.systemd1 \
+  $(busctl call org.freedesktop.systemd1 \
+     /org/freedesktop/systemd1 \
+     org.freedesktop.systemd1.Manager \
+     GetUnit s systemd-resolved.service | cut -d'"' -f2)
+      </programlisting>
+    </example>
+
+    <example>
+      <title>Introspect <interfacename>org.freedesktop.systemd1.Job</interfacename> on the bus</title>
+
+      <programlisting>
+$ gdbus introspect --system --dest org.freedesktop.systemd1 \
+  --object-path /org/freedesktop/systemd1/job/1292
+      </programlisting>
+    </example>
+  </refsect1>
+
+  <refsect1>
+    <title>Versioning</title>
+
+    <para>These D-Bus interfaces follow <ulink url="http://0pointer.de/blog/projects/versioning-dbus.html">
+    the usual interface versioning guidelines</ulink>.</para>
+  </refsect1>
+</refentry>
diff --git a/man/org.freedesktop.timedate1.xml b/man/org.freedesktop.timedate1.xml
new file mode 100644 (file)
index 0000000..325c3ac
--- /dev/null
@@ -0,0 +1,205 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" >
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="org.freedesktop.timedate1" conditional='ENABLE_TIMEDATED'
+    xmlns:xi="http://www.w3.org/2001/XInclude">
+  <refentryinfo>
+    <title>org.freedesktop.timedate1</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>org.freedesktop.timedate1</refentrytitle>
+    <manvolnum>5</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>org.freedesktop.timedate1</refname>
+    <refpurpose>The D-Bus interface of systemd-timedated</refpurpose>
+  </refnamediv>
+
+  <refsect1>
+    <title>Introduction</title>
+
+    <para>
+    <citerefentry><refentrytitle>systemd-timedated.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    is a system service that can be used to control the system time and related settings. This page
+    describes the D-Bus interface.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>The D-Bus API</title>
+
+    <para>The service exposes the following interfaces on the bus:</para>
+
+    <programlisting executable="systemd-timedated" node="/org/freedesktop/timedate1" interface="org.freedesktop.timedate1">
+node /org/freedesktop/timedate1 {
+  interface org.freedesktop.timedate1 {
+    methods:
+      SetTime(in  x usec_utc,
+              in  b relative,
+              in  b interactive);
+      SetTimezone(in  s timezone,
+                  in  b interactive);
+      SetLocalRTC(in  b local_rtc,
+                  in  b fix_system,
+                  in  b interactive);
+      SetNTP(in  b use_ntp,
+             in  b interactive);
+      ListTimezones(out as timezones);
+    properties:
+      readonly s Timezone = '...';
+      readonly b LocalRTC = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b CanNTP = ...;
+      readonly b NTP = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly b NTPSynchronized = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t TimeUSec = ...;
+      @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+      readonly t RTCTimeUSec = ...;
+  };
+  interface org.freedesktop.DBus.Peer { ... };
+  interface org.freedesktop.DBus.Introspectable { ... };
+  interface org.freedesktop.DBus.Properties { ... };
+};
+    </programlisting>
+
+    <!--Autogenerated cross-references for systemd.directives, do not edit-->
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.timedate1"/>
+
+    <variablelist class="dbus-interface" generated="True" extra-ref="org.freedesktop.timedate1"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetTime()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetTimezone()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetLocalRTC()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="SetNTP()"/>
+
+    <variablelist class="dbus-method" generated="True" extra-ref="ListTimezones()"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="Timezone"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="LocalRTC"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="CanNTP"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="NTP"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="NTPSynchronized"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="TimeUSec"/>
+
+    <variablelist class="dbus-property" generated="True" extra-ref="RTCTimeUSec"/>
+
+    <!--End of Autogenerated section-->
+
+    <refsect2>
+      <title>Methods</title>
+
+      <para>Use <function>SetTime()</function> to change the system clock. Pass a value of microseconds since
+      the UNIX epoch (1 Jan 1970 UTC). If <varname>relative</varname> is true, the passed usec value will be
+      added to the current system time. If it is false, the current system time will be set to the passed usec
+      value. If the system time is set with this method, the RTC will be updated as well.</para>
+
+      <para>Use <function>SetTimezone()</function> to set the system timezone. Pass a value like
+      <literal>Europe/Berlin</literal> to set the timezone. Valid timezones are listed in
+      <filename>/usr/share/zoneinfo/zone.tab</filename>. If the RTC is configured to be maintained in local
+      time, it will be updated accordingly.</para>
+
+      <para>Use <function>SetLocalRTC()</function> to control whether the RTC is in local time or UTC. It is
+      strongly recommended to maintain the RTC in UTC. However, some OSes (Windows) maintain the RTC in local
+      time, which might make it necessary to enable this feature. Note that this might create various problems as
+      daylight changes could be missed. If <varname>fix_system</varname> is <literal>true</literal>,
+      the time from the RTC is read again and the system clock is adjusted according to the new setting. If
+      <varname>fix_system</varname> is <literal>false</literal>, the system time is written to the RTC
+      taking the new setting into account. Use <varname>fix_system=true</varname> in installers and livecds
+      where the RTC is probably more reliable than the system time. Use <varname>fix_system=false</varname>
+      in configuration UIs that are run during normal operation and where the system clock is probably more
+      reliable than the RTC.</para>
+
+      <para>Use <function>SetNTP()</function> to control whether the system clock is synchronized with the
+      network using <filename>systemd-timesyncd</filename>. This will enable and start or disable and stop
+      the chosen time synchronization service.</para>
+
+      <para><function>ListTimezones()</function> returns a list of time zones known on the local system as an
+      array of names (<literal>["Africa/Abidjan", "Africa/Accra", ..., "UTC"]</literal>).</para>
+    </refsect2>
+
+    <refsect2>
+      <title>Properties</title>
+
+      <para><varname>Timezone</varname> shows the currently configured time zone.
+      <varname>LocalRTC</varname> shows whether the RTC is configured to use UTC (false), or the local time
+      zone (true). <varname>CanNTP</varname> shows whether a service to perform time synchronization over the
+      network is available, and <varname>NTP</varname> shows whether such a service is enabled.</para>
+
+      <para><varname>NTPSynchronized</varname> shows whether the kernel reports the time as synchronized
+      (c.f.
+      <citerefentry project="man-pages"><refentrytitle>adjtimex</refentrytitle><manvolnum>3</manvolnum></citerefentry>).
+      <varname>TimeUSec</varname> and <varname>RTCTimeUSec</varname> show the current time on the system and
+      in the RTC. The purpose of those three properties is to allow remote clients to access this information
+      over D-Bus. Local clients can access the information directly.</para>
+
+      <para>Whenever the <varname>Timezone</varname> and <varname>LocalRTC</varname> settings are changed via
+      the daemon, <function>PropertyChanged</function> signals are sent out to which clients can subscribe.
+      </para>
+
+      <para>Note that this service will not inform you about system time changes. Use
+      <citerefentry project="man-pages"><refentrytitle>timerfd</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+      with <constant>CLOCK_REALTIME</constant> and <constant>TFD_TIMER_CANCEL_ON_SET</constant> for that.
+      </para>
+    </refsect2>
+
+    <refsect2>
+      <title>Security</title>
+
+      <para>The <varname>interactive</varname> boolean parameters can be used to control whether
+      <ulink url="https://www.freedesktop.org/software/polkit/docs/latest/">polkit</ulink>
+      should interactively ask the user for authentication credentials if required.</para>
+
+      <para>The polkit action for <function>SetTimezone()</function> is
+      <interfacename>org.freedesktop.timedate1.set-timezone</interfacename>. For
+      <function>SetLocalRTC()</function> it is
+      <interfacename>org.freedesktop.timedate1.set-local-rtc</interfacename>, for
+      <function>SetTime()</function> it is <interfacename>org.freedesktop.timedate1.set-time</interfacename>
+      and for <function>SetNTP()</function> it is
+      <interfacename>org.freedesktop.timedate1.set-ntp</interfacename>.
+      <function>ListTimezones()</function> does not require any privileges.
+      </para>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>Examples</title>
+
+    <example>
+      <title>Introspect <interfacename>org.freedesktop.timedate1</interfacename> on the bus</title>
+
+      <programlisting>
+$ gdbus introspect --system \
+  --dest org.freedesktop.timedate1 \
+  --object-path /org/freedesktop/timedate1
+      </programlisting>
+    </example>
+  </refsect1>
+
+  <refsect1>
+    <title>Versioning</title>
+
+    <para>These D-Bus interfaces follow <ulink url="http://0pointer.de/blog/projects/versioning-dbus.html">
+    the usual interface versioning guidelines</ulink>.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>See also</title>
+
+      <para><ulink url="https://lists.freedesktop.org/archives/systemd-devel/2011-May/002526.html">More information on how the system clock and RTC interact</ulink></para>
+  </refsect1>
+</refentry>
index 5a5e318f75bd3c6713880328eee8534893402719..a2164436c37502a5ddb8b2fa9040ae8bd10b0590 100644 (file)
       <varlistentry>
         <term><varname>ANSI_COLOR=</varname></term>
 
-        <listitem><para>A suggested presentation color when showing
-        the OS name on the console. This should be specified as string
-        suitable for inclusion in the ESC [ m ANSI/ECMA-48 escape code
-        for setting graphical rendition. This field is optional.
-        Example: <literal>ANSI_COLOR="0;31"</literal> for red, or
-        <literal>ANSI_COLOR="1;34"</literal> for light
-        blue.</para></listitem>
+        <listitem><para>A suggested presentation color when showing the OS name on the console. This should
+        be specified as string suitable for inclusion in the ESC [ m ANSI/ECMA-48 escape code for setting
+        graphical rendition. This field is optional.  Example: <literal>ANSI_COLOR="0;31"</literal> for red,
+        <literal>ANSI_COLOR="1;34"</literal> for light blue, or
+        <literal>ANSI_COLOR="0;38;2;60;110;180"</literal> for Fedora blue.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><varname>BUG_REPORT_URL=</varname></term>
         <term><varname>PRIVACY_POLICY_URL=</varname></term>
 
-        <listitem><para>Links to resources on the Internet related to 
+        <listitem><para>Links to resources on the Internet related to
         the operating system.
         <varname>HOME_URL=</varname> should refer to the homepage of
         the operating system, or alternatively some homepage of the
     name in order to avoid name clashes. Applications
     reading this file must ignore unknown fields. Example:
     <literal>DEBIAN_BTS="debbugs://bugs.debian.org/"</literal></para>
+
+    <para>Container and sandbox runtime managers may make the host's
+    identification data available to applications by providing the host's
+    <filename>/etc/os-release</filename> (if available, otherwise
+    <filename>/usr/lib/os-release</filename> as a fallback) as
+    <filename>/run/host/os-release</filename>.</para>
   </refsect1>
 
   <refsect1>
     <title>Example</title>
 
     <programlisting>NAME=Fedora
-VERSION="17 (Beefy Miracle)"
+VERSION="32 (Workstation Edition)"
 ID=fedora
-VERSION_ID=17
-PRETTY_NAME="Fedora 17 (Beefy Miracle)"
-ANSI_COLOR="0;34"
-CPE_NAME="cpe:/o:fedoraproject:fedora:17"
+VERSION_ID=32
+PRETTY_NAME="Fedora 32 (Workstation Edition)"
+ANSI_COLOR="0;38;2;60;110;180"
+LOGO=fedora-logo-icon
+CPE_NAME="cpe:/o:fedoraproject:fedora:32"
 HOME_URL="https://fedoraproject.org/"
-BUG_REPORT_URL="https://bugzilla.redhat.com/"</programlisting>
+DOCUMENTATION_URL="https://docs.fedoraproject.org/en-US/fedora/f32/system-administrators-guide/"
+SUPPORT_URL="https://fedoraproject.org/wiki/Communicating_and_getting_help"
+BUG_REPORT_URL="https://bugzilla.redhat.com/"
+REDHAT_BUGZILLA_PRODUCT="Fedora"
+REDHAT_BUGZILLA_PRODUCT_VERSION=32
+REDHAT_SUPPORT_PRODUCT="Fedora"
+REDHAT_SUPPORT_PRODUCT_VERSION=32
+PRIVACY_POLICY_URL="https://fedoraproject.org/wiki/Legal:PrivacyPolicy"
+VARIANT="Workstation Edition"
+VARIANT_ID=workstation</programlisting>
   </refsect1>
 
   <refsect1>
index 70927d737423c278e4b48bcc256717c812a029fc..609743be6b2f24b34725c76daa11d3bf146d9d16 100644 (file)
         hence be used to uniquely label files or other resources of this session. Combine this ID with the boot
         identifier, as returned by
         <citerefentry><refentrytitle>sd_id128_get_boot</refentrytitle><manvolnum>3</manvolnum></citerefentry>, for a
-        globally unique identifier for the current session.</para></listitem>
+        globally unique identifier.</para></listitem>
       </varlistentry>
 
       <varlistentry>
 
     <para> See
     <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry> for more information about the resources.
-    Also, see <citerefentry><refentrytitle>pam_set_data</refentrytitle><manvolnum>3</manvolnum></citerefentry> for additional information about how to set
+    Also, see <citerefentry project='man-pages'><refentrytitle>pam_set_data</refentrytitle><manvolnum>3</manvolnum></citerefentry> for additional information about how to set
     the context objects.
     </para>
 
index 341f8a8e4910df16dfa124e9bdb519e857e488d1..ab02f98337202e60dbc21d42e27de819796e3c9b 100644 (file)
         coming back from suspend. It is recommended to set this parameter for all PAM applications that have
         support for automatically re-authenticating via PAM on system resume. If multiple sessions of the
         same user are open in parallel the user's home directory will be left unsuspended on system suspend
-        as long as at least one of the sessions does not set this parameter. Defaults to
-        off.</para></listitem>
+        as long as at least one of the sessions does not set this parameter to on. Defaults to
+        off.</para>
+
+        <para>Note that TTY logins generally do not support re-authentication on system resume.
+        Re-authentication on system resume is primarily a concept implementable in graphical environments, in
+        the form of lock screens brought up automatically when the system goes to sleep. This means that if a
+        user concurrently uses graphical login sessions that implement the required re-authentication
+        mechanism and console logins that do not, the home directory is not locked during suspend, due to the
+        logic explained above. That said, it is possible to set this field for TTY logins too, ignoring the
+        fact that TTY logins actually don't support the re-authentication mechanism. In that case the TTY
+        sessions will appear hung until the user logs in on another virtual terminal (regardless if via
+        another TTY session or graphically) which will resume the home directory and unblock the original TTY
+        session. (Do note that lack of screen locking on TTY sessions means even though the TTY session
+        appears hung, keypresses can still be queued into it, and the existing screen contents be read
+        without re-authentication; this limitation is unrelated to the home directory management
+        <command>pam_systemd_home</command> and <filename>systemd-homed.service</filename> implement.)</para>
+
+        <para>Turning this option on by default is highly recommended for all sessions, but only if the
+        service managing these sessions correctly implements the aforementioned re-authentication. Note that
+        the re-authentication must take place from a component running outside of the user's context, so that
+        it does not require access to the user's home directory for operation. Traditionally, most desktop
+        environments do not implement screen locking this way, and need to be updated
+        accordingly.</para>
+
+        <para>This setting may also be controlled via the <varname>$SYSTEMD_HOME_SUSPEND</varname>
+        environment variable (see below), which <command>pam_systemd_home</command> reads during initialization and sets
+        for sessions. If both the environment variable is set and the module parameter specified the latter
+        takes precedence.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <listitem><para>Indicates that the user's home directory is managed by <filename>systemd-homed.service</filename>.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>$SYSTEMD_HOME_SUSPEND=</varname></term>
+
+        <listitem><para>Indicates whether the session has been registered with the suspend mechanism enabled
+        or disabled (see above). The variable's value is either <literal>0</literal> or
+        <literal>1</literal>. Note that the module both reads the variable when initializing, and sets it for
+        sessions.</para></listitem>
+      </varlistentry>
+
     </variablelist>
   </refsect1>
 
diff --git a/man/path-documents.c b/man/path-documents.c
new file mode 100644 (file)
index 0000000..a6c1f93
--- /dev/null
@@ -0,0 +1,9 @@
+#include <stdio.h>
+#include <sd-path.h>
+
+int main(void) {
+  char *t;
+
+  sd_path_lookup(SD_PATH_USER_DOCUMENTS, NULL, &t);
+  printf("~/Documents: %s\n", t);
+}
index f2d8da40c410ffaff35f3100273fa5b2f09ed61c..962429683d6b38fcbe3424713f575c0c798a1143 100644 (file)
 
         <para>By default all unit files whose names start with a prefix generated from the image's file name are copied
         out. Specifically, the prefix is determined from the image file name with any suffix such as
-        <filename>.raw</filename> removed, truncated at the first occurrence of and underscore character
+        <filename>.raw</filename> removed, truncated at the first occurrence of an underscore character
         (<literal>_</literal>), if there is one. The underscore logic is supposed to be used to versioning so that the
         an image file <filename>foobar_47.11.raw</filename> will result in a unit file matching prefix of
         <filename>foobar</filename>. This prefix is then compared with all unit files names contained in the image in
       </tgroup>
     </table>
 
-    <para>For details on this profiles, and their effects please have a look at their precise definitions,
+    <para>For details on these profiles and their effects see their precise definitions,
     e.g. <filename>/usr/lib/systemd/portable/profile/default/service.conf</filename> and similar.</para>
   </refsect1>
 
index 2b9c8b1a7145ca015ab1079d52c02bdb3a55afe1..501171e78a887f245756cfce1206ef43c6a02816 100644 (file)
@@ -44,7 +44,7 @@
     <title>Options</title>
 
     <para>All options are configured in the
-    <literal>[PStore]</literal> section:</para>
+    [PStore] section:</para>
 
     <variablelist class='config-directives'>
 
@@ -82,7 +82,7 @@
   <refsect1>
     <title>See Also</title>
     <para>
-      <citerefentry><refentrytitle>systemd-journald.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-journald.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
     </para>
   </refsect1>
 
index ae8b17ff9336f15c315e7870bab67dc80bdb63a5..1b104e76d075c599124a88b7e55c8d172287e2dc 100644 (file)
@@ -1,7 +1,8 @@
 <?xml version='1.0'?>
 <!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
   "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
-<refentry id="repart.d" conditional='ENABLE_REPART'>
+<refentry id="repart.d" conditional='ENABLE_REPART'
+          xmlns:xi="http://www.w3.org/2001/XInclude">
 
   <refentryinfo>
     <title>repart.d</title>
 
               <row>
                 <entry><constant>root-secondary</constant></entry>
-                <entry>Root file system partition of the secondary architecture of the local architectureusually the matching 32bit architecture for the local 64bit architecture)</entry>
+                <entry>Root file system partition of the secondary architecture of the local architecture (usually the matching 32bit architecture for the local 64bit architecture)</entry>
               </row>
 
               <row>
         setting is not used for matching. It is also not used when a label is already set for an existing
         partition. It is thus only used when a partition is newly created or when an existing one had a no
         label set (that is: an empty label). If not specified a label derived from the partition type is
-        automatically used.</para></listitem>
+        automatically used. Simple specifier expansion is supported, see below.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>UUID=</varname></term>
+
+        <listitem><para>The UUID to assign to the partition if none is assigned yet. Note that this
+        setting is not used for matching. It is also not used when a UUID is already set for an existing
+        partition. It is thus only used when a partition is newly created or when an existing one had a
+        all-zero UUID set. If not specified a UUID derived from the partition type is automatically
+        used.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><varname>PaddingMinBytes=</varname></term>
         <term><varname>PaddingMaxBytes=</varname></term>
 
-        <listitem><para>Specifies minimum and maximum size constrains in bytes for the free space after the
+        <listitem><para>Specifies minimum and maximum size constraints in bytes for the free space after the
         partition (the "padding"). Semantics are similar to <varname>SizeMinBytes=</varname> and
         <varname>SizeMaxBytes=</varname>, except that unlike partition sizes free space can be shrunk and can
         be as small as zero. By default no size constraints on padding are set, so that only
         <varname>PaddingWeight=</varname> determines the size of the padding applied.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>CopyBlocks=</varname></term>
+
+        <listitem><para>Takes a path to a regular file, block device node or directory. If specified and the
+        partition is newly created the data from the specified path is written to the newly created
+        partition, on the block level. If a directory is specified the backing block device of the file
+        system the directory is on is determined and the data read directly from that. This option is useful
+        to efficiently replicate existing file systems on the block level on a new partition, for example to
+        build a simple OS installer or OS image builder.</para>
+
+        <para>The file specified here must have a size that is a multiple of the basic block size 512 and not
+        be empty. If this option is used, the size allocation algorithm is slightly altered: the partition is
+        created as least as big as required to fit the data in, i.e. the data size is an additional minimum
+        size value taken into consideration for the allocation algorithm, similar to and in addition to the
+        <varname>SizeMin=</varname> value configured above.</para>
+
+        <para>This option has no effect if the partition it is declared for already exists, i.e. existing
+        data is never overwritten. Note that the data is copied in before the partition table is updated,
+        i.e. before the partition actually is persistently created. This provides robustness: it is
+        guaranteed that the partition either doesn't exist or exists fully populated; it is not possible that
+        the partition exists but is not or only partially populated.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>FactoryReset=</varname></term>
 
     </variablelist>
   </refsect1>
 
+  <refsect1>
+    <title>Specifiers</title>
+
+    <para>Specifiers may be used in the <varname>Label=</varname> setting. The following expansions are understood:</para>
+      <table class='specifiers'>
+        <title>Specifiers available</title>
+        <tgroup cols='3' align='left' colsep='1' rowsep='1'>
+          <colspec colname="spec" />
+          <colspec colname="mean" />
+          <colspec colname="detail" />
+          <thead>
+            <row>
+              <entry>Specifier</entry>
+              <entry>Meaning</entry>
+              <entry>Details</entry>
+            </row>
+          </thead>
+          <tbody>
+            <xi:include href="standard-specifiers.xml" xpointer="a"/>
+            <xi:include href="standard-specifiers.xml" xpointer="b"/>
+            <xi:include href="standard-specifiers.xml" xpointer="B"/>
+            <xi:include href="standard-specifiers.xml" xpointer="H"/>
+            <xi:include href="standard-specifiers.xml" xpointer="l"/>
+            <xi:include href="standard-specifiers.xml" xpointer="m"/>
+            <xi:include href="standard-specifiers.xml" xpointer="o"/>
+            <xi:include href="standard-specifiers.xml" xpointer="v"/>
+            <xi:include href="standard-specifiers.xml" xpointer="w"/>
+            <xi:include href="standard-specifiers.xml" xpointer="W"/>
+            <xi:include href="standard-specifiers.xml" xpointer="percent"/>
+          </tbody>
+        </tgroup>
+      </table>
+  </refsect1>
+
   <refsect1>
     <title>Examples</title>
 
index a7de5a309f42382d071ca14b16c081027dcaed31..a4bd8f52d774d40bc487aaf6b57b6c9c1547cdf8 100644 (file)
@@ -45,7 +45,7 @@
     interface the data was discovered. It also contains information on whether the information could be
     authenticated. All data for which local DNSSEC validation succeeds is considered authenticated. Moreover all data
     originating from local, trusted sources is also reported authenticated, including resolution of the local host
-    name, the <literal>localhost</literal> host name or all data from <filename>/etc/hosts</filename>.</para>
+    name, the <literal>localhost</literal> hostname or all data from <filename>/etc/hosts</filename>.</para>
   </refsect1>
 
   <refsect1>
           settings for network interfaces. These commands may be used to inform
           <command>systemd-resolved</command> or <command>systemd-networkd</command> about per-interface DNS
           configuration determined through external means. The <command>dns</command> command expects IPv4 or
-          IPv6 address specifications of DNS servers to use. The <command>domain</command> command expects
-          valid DNS domains, possibly prefixed with <literal>~</literal>, and configures a per-interface
-          search or route-only domain. The <command>default-route</command> command expects a boolean
-          parameter, and configures whether the link may be used as default route for DNS lookups, i.e. if it
-          is suitable for lookups on domains no other link explicitly is configured for. The
-          <command>llmnr</command>, <command>mdns</command>, <command>dnssec</command> and
-          <command>dnsovertls</command> commands may be used to configure the per-interface LLMNR,
-          MulticastDNS, DNSSEC and DNSOverTLS settings. Finally, <command>nta</command> command may be used
-          to configure additional per-interface DNSSEC NTA domains.</para>
+          IPv6 address specifications of DNS servers to use. Each address can optionally take a port number
+          separated with <literal>:</literal>, a network interface name or index separated with
+          <literal>%</literal>, and a Server Name Indication (SNI) separated with <literal>#</literal>. When
+          IPv6 address is specified with a port number, then the address must be in the square brackets. That
+          is, the acceptable full formats are <literal>111.222.333.444:9953%ifname#example.com</literal> for
+          IPv4 and <literal>[1111:2222::3333]:9953%ifname#example.com</literal> for IPv6. The
+          <command>domain</command> command expects valid DNS domains, possibly prefixed with
+          <literal>~</literal>, and configures a per-interface search or route-only domain. The
+          <command>default-route</command> command expects a boolean parameter, and configures whether the
+          link may be used as default route for DNS lookups, i.e. if it is suitable for lookups on domains no
+          other link explicitly is configured for. The <command>llmnr</command>, <command>mdns</command>,
+          <command>dnssec</command> and <command>dnsovertls</command> commands may be used to configure the
+          per-interface LLMNR, MulticastDNS, DNSSEC and DNSOverTLS settings. Finally, <command>nta</command>
+          command may be used to configure additional per-interface DNSSEC NTA domains.</para>
 
           <para>Commands <command>dns</command>, <command>domain</command> and <command>nta</command> can take
           a single empty string argument to clear their respective value lists.</para>
         automatically, an explicit reverting is not necessary in that case.</para></listitem>
       </varlistentry>
 
+      <xi:include href="systemctl.xml" xpointer="log-level" />
     </variablelist>
   </refsect1>
 
         <term><option>-a</option></term>
         <listitem><para>Registers per-interface DNS configuration data with
         <command>systemd-resolved</command>. Expects a network interface name as only command line argument. Reads
-        <citerefentry><refentrytitle>resolv.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry> compatible DNS
-        configuration data from its standard input. Relevant fields are <literal>nameserver</literal> and
+        <citerefentry project='man-pages'><refentrytitle>resolv.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>-compatible
+        DNS configuration data from its standard input. Relevant fields are <literal>nameserver</literal> and
         <literal>domain</literal>/<literal>search</literal>. This command is mostly identical to invoking
-        <command>resolvectl</command> with a combination of <option>dns</option> and
-        <option>domain</option> commands.</para></listitem>
+        <command>resolvectl</command> with a combination of <option>dns</option> and <option>domain</option>
+        commands.</para></listitem>
       </varlistentry>
 
       <varlistentry>
index 0f70ced5b54f6aba824fee8d4f2cf54e7e95cff2..535a23f500a12853fe081b744a31c4846ad7c159 100644 (file)
   <refsect1>
     <title>Options</title>
 
-    <para>The following options are available in the <literal>[Resolve]</literal> section:</para>
+    <para>The following options are available in the [Resolve] section:</para>
 
     <variablelist class='network-directives'>
 
       <varlistentry>
         <term><varname>DNS=</varname></term>
-        <listitem><para>A space-separated list of IPv4 and IPv6 addresses to use as system DNS servers. DNS requests
-        are sent to one of the listed DNS servers in parallel to suitable per-link DNS servers acquired from
+        <listitem><para>A space-separated list of IPv4 and IPv6 addresses to use as system DNS servers. Each address can
+        optionally take a port number separated with <literal>:</literal>, a network interface name or index separated with
+        <literal>%</literal>, and a Server Name Indication (SNI) separated with <literal>#</literal>. When IPv6 address is
+        specified with a port number, then the address must be in the square brackets. That is, the acceptable full formats
+        are <literal>111.222.333.444:9953%ifname#example.com</literal> for IPv4 and
+        <literal>[1111:2222::3333]:9953%ifname#example.com</literal> for IPv6. DNS requests are sent to one of the listed
+        DNS servers in parallel to suitable per-link DNS servers acquired from
         <citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry> or
         set at runtime by external applications.  For compatibility reasons, if this setting is not specified, the DNS
         servers listed in <filename>/etc/resolv.conf</filename> are used instead, if that file exists and any servers
@@ -57,8 +62,8 @@
 
       <varlistentry>
         <term><varname>FallbackDNS=</varname></term>
-        <listitem><para>A space-separated list of IPv4 and IPv6 addresses to use as the fallback DNS servers. Any
-        per-link DNS servers obtained from
+        <listitem><para>A space-separated list of IPv4 and IPv6 addresses to use as the fallback DNS servers. Please see
+        <varname>DNS=</varname> for acceptable format of adddresses. Any per-link DNS servers obtained from
         <citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
         take precedence over this setting, as do any servers set via <varname>DNS=</varname> above or
         <filename>/etc/resolv.conf</filename>. This setting is hence only used if no other DNS server information is
 
       <varlistentry>
         <term><varname>Domains=</varname></term>
-        <listitem><para>A space-separated list of domains. These domains are used as search suffixes when resolving
-        single-label host names (domain names which contain no dot), in order to qualify them into fully-qualified
-        domain names (FQDNs). Search domains are strictly processed in the order they are specified, until the name
-        with the suffix appended is found. For compatibility reasons, if this setting is not specified, the search
-        domains listed in <filename>/etc/resolv.conf</filename> are used instead, if that file exists and any domains
-        are configured in it. This setting defaults to the empty list.</para>
-
-        <para>Specified domain names may optionally be prefixed with <literal>~</literal>. In this case they do not
-        define a search path, but preferably direct DNS queries for the indicated domains to the DNS servers configured
-        with the system <varname>DNS=</varname> setting (see above), in case additional, suitable per-link DNS servers
-        are known. If no per-link DNS servers are known using the <literal>~</literal> syntax has no effect. Use the
-        construct <literal>~.</literal> (which is composed of <literal>~</literal> to indicate a routing domain and
-        <literal>.</literal> to indicate the DNS root domain that is the implied suffix of all DNS domains) to use the
-        system DNS server defined with <varname>DNS=</varname> preferably for all domains.</para></listitem>
+        <listitem><para>A space-separated list of domains optionally prefixed with <literal>~</literal>,
+        used for two distinct purposes described below. Defaults to the empty list.</para>
+
+        <para>Any domains <emphasis>not</emphasis> prefixed with <literal>~</literal> are used as search
+        suffixes when resolving single-label hostnames (domain names which contain no dot), in order to
+        qualify them into fully-qualified domain names (FQDNs). These "search domains" are strictly processed
+        in the order they are specified in, until the name with the suffix appended is found. For
+        compatibility reasons, if this setting is not specified, the search domains listed in
+        <filename>/etc/resolv.conf</filename> with the <varname>search</varname> keyword are used instead, if
+        that file exists and any domains are configured in it.</para>
+
+        <para>The domains prefixed with <literal>~</literal> are called "routing domains". All domains listed
+        here (both search domains and routing domains after removing the <literal>~</literal> prefix) define
+        a search path that preferably directs DNS queries to this interface. This search path has an effect
+        only when suitable per-link DNS servers are known. Such servers may be defined through the
+        <varname>DNS=</varname> setting (see above) and dynamically at run time, for example from DHCP
+        leases. If no per-link DNS servers are known, routing domains have no effect.</para>
+
+        <para>Use the construct <literal>~.</literal> (which is composed from <literal>~</literal> to
+        indicate a routing domain and <literal>.</literal> to indicate the DNS root domain that is the
+        implied suffix of all DNS domains) to use the DNS servers defined for this link preferably for all
+        domains.</para></listitem>
       </varlistentry>
 
       <varlistentry>
       <varlistentry>
         <term><varname>DNSOverTLS=</varname></term>
         <listitem>
-        <para>Takes a boolean argument or <literal>opportunistic</literal>.
-        If true all connections to the server will be encrypted. Note that
-        this mode requires a DNS server that supports DNS-over-TLS and has
-        a valid certificate for it's IP. If the DNS server does not support
-        DNS-over-TLS all DNS requests will fail. When set to <literal>opportunistic</literal>
+        <para>Takes a boolean argument or <literal>opportunistic</literal>. If
+        true all connections to the server will be encrypted. Note that this
+        mode requires a DNS server that supports DNS-over-TLS and has a valid
+        certificate. If the hostname was specified in <varname>DNS=</varname>
+        by using the format format <literal>address#server_name</literal> it
+        is used to validate its certificate and also to enable Server Name
+        Indication (SNI) when opening a TLS connection. Otherwise
+        the certificate is checked against the server's IP.
+        If the DNS server does not support DNS-over-TLS all DNS requests will fail.</para>
+
+        <para>When set to <literal>opportunistic</literal>
         DNS request are attempted to send encrypted with DNS-over-TLS.
         If the DNS server does not support TLS, DNS-over-TLS is disabled.
         Note that this mode makes DNS-over-TLS vulnerable to "downgrade"
         resolver is not capable of authenticating the server, so it is
         vulnerable to "man-in-the-middle" attacks.</para>
 
-        <para>Server Name Indication (SNI) can be used when opening a TLS connection.
-        Entries in <varname>DNS=</varname> should be in format <literal>address#server_name</literal>.</para>
-
         <para>In addition to this global DNSOverTLS setting
         <citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
         also maintains per-link DNSOverTLS settings. For system DNS
 
       <varlistentry>
         <term><varname>Cache=</varname></term>
-        <listitem><para>Takes a boolean or <literal>no-negative</literal> as argument. If <literal>yes</literal> (the default), resolving a domain name
-        which already got queried earlier will return the previous result as long as it is still valid, and thus does
-        not result in a new network request. Be aware that turning off caching comes at a performance penalty, which
-        is particularly high when DNSSEC is used.</para>
-        If <literal>no-negative</literal>, only positive answers are cached.
+        <listitem><para>Takes a boolean or <literal>no-negative</literal> as argument. If
+        <literal>yes</literal> (the default), resolving a domain name which already got queried earlier will
+        return the previous result as long as it is still valid, and thus does not result in a new network
+        request. Be aware that turning off caching comes at a performance penalty, which is particularly high
+        when DNSSEC is used. If <literal>no-negative</literal>, only positive answers are cached.</para>
 
         <para>Note that caching is turned off implicitly if the configured DNS server is on a host-local IP address
         (such as 127.0.0.1 or ::1), in order to avoid duplicate local caching.</para></listitem>
 
       <varlistentry>
         <term><varname>ReadEtcHosts=</varname></term>
-        <listitem><para>Takes a boolean argument. If <literal>yes</literal> (the default), the DNS stub resolver will read
-        <filename>/etc/hosts</filename>, and try to resolve hosts or address by using the entries in the file before
-        sending query to DNS servers.</para></listitem>
+        <listitem><para>Takes a boolean argument. If <literal>yes</literal> (the default),
+        <command>systemd-resolved</command> will read <filename>/etc/hosts</filename>, and try to resolve
+        hosts or address by using the entries in the file before sending query to DNS servers.
+        </para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>ResolveUnicastSingleLabel=</varname></term>
+        <listitem><para>Takes a boolean argument. When false (the default),
+        <command>systemd-resolved</command> will not resolve A and AAAA queries for single-label names over
+        classic DNS. Note that such names may still be resolved if search domains are specified (see
+        <varname>Domains=</varname> above), or using other mechanisms, in particular via LLMNR or from
+        <filename>/etc/hosts</filename>. When true, queries for single-label names will be forwarded to
+        global DNS servers even if no search domains are defined.
+        </para>
+
+        <para>This option is provided for compatibility with configurations where <emphasis>public DNS
+        servers are not used</emphasis>. Forwarding single-label names to servers not under your control is
+        not standard-conformant, see <ulink
+        url="https://www.iab.org/documents/correspondence-reports-documents/2013-2/iab-statement-dotless-domains-considered-harmful/">IAB
+        Statement</ulink>, and may create a privacy and security risk.</para></listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 
index 296dd7da3a73890cc913d7a395abc4112cc880ae..3fb454faa907d15b87af52095b79522f27709aa1 100644 (file)
@@ -1,4 +1,4 @@
-# Do not edit. Generated by make-man-rules.py.
+# Do not edit. Generated by update-man-rules.py.
 # Update with:
 #     ninja -C build man/update-man-rules
 manpages = [
@@ -18,6 +18,7 @@ manpages = [
  ['file-hierarchy', '7', [], ''],
  ['halt', '8', ['poweroff', 'reboot'], ''],
  ['homectl', '1', [], 'ENABLE_HOMED'],
+ ['homed.conf', '5', ['homed.conf.d'], 'ENABLE_HOMED'],
  ['hostname', '5', [], ''],
  ['hostnamectl', '1', [], 'ENABLE_HOSTNAMED'],
  ['hwdb', '7', [], 'ENABLE_HWDB'],
@@ -44,6 +45,16 @@ manpages = [
  ['nss-mymachines', '8', ['libnss_mymachines.so.2'], 'ENABLE_NSS_MYMACHINES'],
  ['nss-resolve', '8', ['libnss_resolve.so.2'], 'ENABLE_NSS_RESOLVE'],
  ['nss-systemd', '8', ['libnss_systemd.so.2'], 'ENABLE_NSS_SYSTEMD'],
+ ['org.freedesktop.LogControl1', '5', [], ''],
+ ['org.freedesktop.home1', '5', [], 'ENABLE_HOMED'],
+ ['org.freedesktop.hostname1', '5', [], 'ENABLE_HOSTNAMED'],
+ ['org.freedesktop.import1', '5', [], 'ENABLE_IMPORTD'],
+ ['org.freedesktop.locale1', '5', [], 'ENABLE_LOCALED'],
+ ['org.freedesktop.login1', '5', [], 'ENABLE_LOGIND'],
+ ['org.freedesktop.machine1', '5', [], 'ENABLE_MACHINED'],
+ ['org.freedesktop.resolve1', '5', [], 'ENABLE_RESOLVE'],
+ ['org.freedesktop.systemd1', '5', [], ''],
+ ['org.freedesktop.timedate1', '5', [], 'ENABLE_TIMEDATED'],
  ['os-release', '5', [], ''],
  ['pam_systemd', '8', [], 'HAVE_PAM'],
  ['pam_systemd_home', '8', [], 'ENABLE_PAM_HOME'],
@@ -99,6 +110,7 @@ manpages = [
    'SD_WARNING'],
   ''],
  ['sd-event', '3', [], ''],
+ ['sd-hwdb', '3', [], ''],
  ['sd-id128',
   '3',
   ['SD_ID128_CONST_STR',
@@ -121,7 +133,8 @@ manpages = [
    'sd_bus_match_signal',
    'sd_bus_match_signal_async'],
   ''],
- ['sd_bus_add_object_vtable',
+ ['sd_bus_add_node_enumerator', '3', [], ''],
+ ['sd_bus_add_object',
   '3',
   ['SD_BUS_METHOD',
    'SD_BUS_METHOD_WITH_NAMES',
@@ -134,10 +147,22 @@ manpages = [
    'SD_BUS_VTABLE_END',
    'SD_BUS_VTABLE_START',
    'SD_BUS_WRITABLE_PROPERTY',
-   'sd_bus_add_fallback_vtable'],
+   'sd_bus_add_fallback',
+   'sd_bus_add_fallback_vtable',
+   'sd_bus_add_filter',
+   'sd_bus_add_object_vtable'],
   ''],
+ ['sd_bus_add_object_manager', '3', [], ''],
  ['sd_bus_attach_event', '3', ['sd_bus_detach_event', 'sd_bus_get_event'], ''],
- ['sd_bus_close', '3', ['sd_bus_flush'], ''],
+ ['sd_bus_call', '3', ['sd_bus_call_async'], ''],
+ ['sd_bus_call_method',
+  '3',
+  ['sd_bus_call_method_async',
+   'sd_bus_call_method_asyncv',
+   'sd_bus_call_methodv'],
+  ''],
+ ['sd_bus_can_send', '3', [], ''],
+ ['sd_bus_close', '3', ['sd_bus_default_flush_close', 'sd_bus_flush'], ''],
  ['sd_bus_creds_get_pid',
   '3',
   ['sd_bus_creds_get_audit_login_uid',
@@ -195,6 +220,18 @@ manpages = [
    'sd_bus_open_user_with_description',
    'sd_bus_open_with_description'],
   ''],
+ ['sd_bus_emit_signal',
+  '3',
+  ['sd_bus_emit_interfaces_added',
+   'sd_bus_emit_interfaces_added_strv',
+   'sd_bus_emit_interfaces_removed',
+   'sd_bus_emit_interfaces_removed_strv',
+   'sd_bus_emit_object_added',
+   'sd_bus_emit_object_removed',
+   'sd_bus_emit_properties_changed',
+   'sd_bus_emit_properties_changed_strv',
+   'sd_bus_emit_signalv'],
+  ''],
  ['sd_bus_enqueue_for_read', '3', [], ''],
  ['sd_bus_error',
   '3',
@@ -217,9 +254,27 @@ manpages = [
   '3',
   ['SD_BUS_ERROR_END', 'SD_BUS_ERROR_MAP', 'sd_bus_error_map'],
   ''],
- ['sd_bus_get_fd', '3', ['sd_bus_get_events', 'sd_bus_get_timeout'], ''],
+ ['sd_bus_get_current_handler',
+  '3',
+  ['sd_bus_get_current_message',
+   'sd_bus_get_current_slot',
+   'sd_bus_get_current_userdata'],
+  ''],
+ ['sd_bus_get_fd',
+  '3',
+  ['sd_bus_get_events', 'sd_bus_get_timeout', 'sd_bus_set_fd'],
+  ''],
  ['sd_bus_get_n_queued_read', '3', ['sd_bus_get_n_queued_write'], ''],
+ ['sd_bus_get_name_creds', '3', ['sd_bus_get_owner_creds'], ''],
+ ['sd_bus_get_name_machine_id', '3', [], ''],
+ ['sd_bus_interface_name_is_valid',
+  '3',
+  ['sd_bus_member_name_is_valid',
+   'sd_bus_object_path_is_valid',
+   'sd_bus_service_name_is_valid'],
+  ''],
  ['sd_bus_is_open', '3', ['sd_bus_is_ready'], ''],
+ ['sd_bus_list_names', '3', [], ''],
  ['sd_bus_message_append', '3', ['sd_bus_message_appendv'], ''],
  ['sd_bus_message_append_array',
   '3',
@@ -233,6 +288,7 @@ manpages = [
   ['sd_bus_message_append_string_iovec', 'sd_bus_message_append_string_space'],
   ''],
  ['sd_bus_message_append_strv', '3', [], ''],
+ ['sd_bus_message_at_end', '3', [], ''],
  ['sd_bus_message_copy', '3', [], ''],
  ['sd_bus_message_dump', '3', [], ''],
  ['sd_bus_message_get_cookie', '3', ['sd_bus_message_get_reply_cookie'], ''],
@@ -246,7 +302,10 @@ manpages = [
   ''],
  ['sd_bus_message_get_type',
   '3',
-  ['sd_bus_message_is_method_call',
+  ['sd_bus_message_get_creds',
+   'sd_bus_message_get_errno',
+   'sd_bus_message_get_error',
+   'sd_bus_message_is_method_call',
    'sd_bus_message_is_method_error',
    'sd_bus_message_is_signal'],
   ''],
@@ -272,10 +331,21 @@ manpages = [
    'sd_bus_message_new_method_errorf'],
   ''],
  ['sd_bus_message_new_signal', '3', [], ''],
- ['sd_bus_message_read', '3', ['sd_bus_message_readv'], ''],
+ ['sd_bus_message_open_container',
+  '3',
+  ['sd_bus_message_close_container',
+   'sd_bus_message_enter_container',
+   'sd_bus_message_exit_container'],
+  ''],
+ ['sd_bus_message_read',
+  '3',
+  ['sd_bus_message_peek_type', 'sd_bus_message_readv'],
+  ''],
  ['sd_bus_message_read_array', '3', [], ''],
  ['sd_bus_message_read_basic', '3', [], ''],
+ ['sd_bus_message_read_strv', '3', [], ''],
  ['sd_bus_message_rewind', '3', [], ''],
+ ['sd_bus_message_seal', '3', [], ''],
  ['sd_bus_message_sensitive', '3', [], ''],
  ['sd_bus_message_set_destination',
   '3',
@@ -288,15 +358,19 @@ manpages = [
   ''],
  ['sd_bus_message_set_expect_reply',
   '3',
-  ['sd_bus_message_get_auto_start',
+  ['sd_bus_message_get_allow_interactive_authorization',
+   'sd_bus_message_get_auto_start',
    'sd_bus_message_get_expect_reply',
+   'sd_bus_message_set_allow_interactive_authorization',
    'sd_bus_message_set_auto_start'],
   ''],
  ['sd_bus_message_skip', '3', [], ''],
  ['sd_bus_message_verify_type', '3', [], ''],
  ['sd_bus_negotiate_fds',
   '3',
-  ['sd_bus_negotiate_creds', 'sd_bus_negotiate_timestamp'],
+  ['sd_bus_get_creds_mask',
+   'sd_bus_negotiate_creds',
+   'sd_bus_negotiate_timestamp'],
   ''],
  ['sd_bus_new',
   '3',
@@ -313,34 +387,70 @@ manpages = [
   ['sd_bus_path_decode', 'sd_bus_path_decode_many', 'sd_bus_path_encode_many'],
   ''],
  ['sd_bus_process', '3', [], ''],
+ ['sd_bus_query_sender_creds', '3', ['sd_bus_query_sender_privilege'], ''],
  ['sd_bus_reply_method_error',
   '3',
   ['sd_bus_reply_method_errno',
    'sd_bus_reply_method_errnof',
-   'sd_bus_reply_method_errorf'],
+   'sd_bus_reply_method_errnofv',
+   'sd_bus_reply_method_errorf',
+   'sd_bus_reply_method_errorfv'],
   ''],
+ ['sd_bus_reply_method_return', '3', ['sd_bus_reply_method_returnv'], ''],
  ['sd_bus_request_name',
   '3',
   ['sd_bus_release_name',
    'sd_bus_release_name_async',
    'sd_bus_request_name_async'],
   ''],
+ ['sd_bus_send', '3', ['sd_bus_send_to'], ''],
+ ['sd_bus_set_address', '3', ['sd_bus_get_address', 'sd_bus_set_exec'], ''],
  ['sd_bus_set_close_on_exit', '3', ['sd_bus_get_close_on_exit'], ''],
  ['sd_bus_set_connected_signal', '3', ['sd_bus_get_connected_signal'], ''],
  ['sd_bus_set_description',
   '3',
   ['sd_bus_get_allow_interactive_authorization',
    'sd_bus_get_description',
+   'sd_bus_get_scope',
+   'sd_bus_get_tid',
+   'sd_bus_get_unique_name',
+   'sd_bus_is_anonymous',
+   'sd_bus_is_trusted',
    'sd_bus_set_allow_interactive_authorization',
    'sd_bus_set_anonymous',
    'sd_bus_set_trusted'],
   ''],
+ ['sd_bus_set_exit_on_disconnect', '3', ['sd_bus_get_exit_on_disconnect'], ''],
+ ['sd_bus_set_method_call_timeout',
+  '3',
+  ['sd_bus_get_method_call_timeout'],
+  ''],
+ ['sd_bus_set_property',
+  '3',
+  ['sd_bus_get_property',
+   'sd_bus_get_property_string',
+   'sd_bus_get_property_strv',
+   'sd_bus_get_property_trivial',
+   'sd_bus_set_propertyv'],
+  ''],
  ['sd_bus_set_sender', '3', ['sd_bus_get_sender'], ''],
+ ['sd_bus_set_server',
+  '3',
+  ['sd_bus_get_bus_id',
+   'sd_bus_is_bus_client',
+   'sd_bus_is_monitor',
+   'sd_bus_is_server',
+   'sd_bus_set_bus_client',
+   'sd_bus_set_monitor'],
+  ''],
  ['sd_bus_set_watch_bind', '3', ['sd_bus_get_watch_bind'], ''],
- ['sd_bus_slot_ref',
+ ['sd_bus_slot_get_bus',
   '3',
-  ['sd_bus_slot_get_bus', 'sd_bus_slot_unref', 'sd_bus_slot_unrefp'],
+  ['sd_bus_slot_get_current_handler',
+   'sd_bus_slot_get_current_message',
+   'sd_bus_slot_get_current_userdata'],
   ''],
+ ['sd_bus_slot_ref', '3', ['sd_bus_slot_unref', 'sd_bus_slot_unrefp'], ''],
  ['sd_bus_slot_set_description', '3', ['sd_bus_slot_get_description'], ''],
  ['sd_bus_slot_set_destroy_callback',
   '3',
@@ -351,6 +461,7 @@ manpages = [
   ''],
  ['sd_bus_slot_set_floating', '3', ['sd_bus_slot_get_floating'], ''],
  ['sd_bus_slot_set_userdata', '3', ['sd_bus_slot_get_userdata'], ''],
+ ['sd_bus_start', '3', [], ''],
  ['sd_bus_track_add_name',
   '3',
   ['sd_bus_track_add_sender',
@@ -486,6 +597,11 @@ manpages = [
   '3',
   ['sd_get_machine_names', 'sd_get_sessions', 'sd_get_uids'],
   'HAVE_PAM'],
+ ['sd_hwdb_get',
+  '3',
+  ['SD_HWDB_FOREACH_PROPERTY', 'sd_hwdb_enumerate', 'sd_hwdb_seek'],
+  ''],
+ ['sd_hwdb_new', '3', ['sd_hwdb_ref', 'sd_hwdb_unref'], ''],
  ['sd_id128_get_machine',
   '3',
   ['sd_id128_get_boot',
@@ -523,6 +639,7 @@ manpages = [
  ['sd_journal_get_data',
   '3',
   ['SD_JOURNAL_FOREACH_DATA',
+   'sd_journal_enumerate_available_data',
    'sd_journal_enumerate_data',
    'sd_journal_get_data_threshold',
    'sd_journal_restart_data',
@@ -564,19 +681,26 @@ manpages = [
    'sd_journal_open_directory',
    'sd_journal_open_directory_fd',
    'sd_journal_open_files',
-   'sd_journal_open_files_fd'],
+   'sd_journal_open_files_fd',
+   'sd_journal_open_namespace'],
   ''],
  ['sd_journal_print',
   '3',
   ['SD_JOURNAL_SUPPRESS_LOCATION',
    'sd_journal_perror',
+   'sd_journal_perror_with_location',
+   'sd_journal_print_with_location',
    'sd_journal_printv',
+   'sd_journal_printv_with_location',
    'sd_journal_send',
-   'sd_journal_sendv'],
+   'sd_journal_send_with_location',
+   'sd_journal_sendv',
+   'sd_journal_sendv_with_location'],
   ''],
  ['sd_journal_query_unique',
   '3',
   ['SD_JOURNAL_FOREACH_UNIQUE',
+   'sd_journal_enumerate_available_unique',
    'sd_journal_enumerate_unique',
    'sd_journal_restart_unique'],
   ''],
@@ -605,8 +729,13 @@ manpages = [
  ['sd_machine_get_class', '3', ['sd_machine_get_ifindices'], ''],
  ['sd_notify',
   '3',
-  ['sd_notifyf', 'sd_pid_notify', 'sd_pid_notify_with_fds', 'sd_pid_notifyf'],
+  ['sd_notify_barrier',
+   'sd_notifyf',
+   'sd_pid_notify',
+   'sd_pid_notify_with_fds',
+   'sd_pid_notifyf'],
   ''],
+ ['sd_path_lookup', '3', ['sd_path_lookup_strv'], ''],
  ['sd_pid_get_owner_uid',
   '3',
   ['sd_peer_get_cgroup',
@@ -627,10 +756,7 @@ manpages = [
   'HAVE_PAM'],
  ['sd_seat_get_active',
   '3',
-  ['sd_seat_can_graphical',
-   'sd_seat_can_multi_session',
-   'sd_seat_can_tty',
-   'sd_seat_get_sessions'],
+  ['sd_seat_can_graphical', 'sd_seat_can_tty', 'sd_seat_get_sessions'],
   'HAVE_PAM'],
  ['sd_session_is_active',
   '3',
@@ -726,7 +852,7 @@ manpages = [
  ['systemd-initctl.service',
   '8',
   ['systemd-initctl', 'systemd-initctl.socket'],
-  ''],
+  'HAVE_SYSV_COMPAT'],
  ['systemd-journal-gatewayd.service',
   '8',
   ['systemd-journal-gatewayd', 'systemd-journal-gatewayd.socket'],
@@ -851,6 +977,7 @@ manpages = [
   ['systemd-veritysetup'],
   'HAVE_LIBCRYPTSETUP'],
  ['systemd-volatile-root.service', '8', ['systemd-volatile-root'], ''],
+ ['systemd-xdg-autostart-generator', '8', [], 'ENABLE_XDG_AUTOSTART'],
  ['systemd', '1', ['init'], ''],
  ['systemd.automount', '5', [], ''],
  ['systemd.device', '5', [], ''],
@@ -971,7 +1098,10 @@ manpages = [
   ''],
  ['udev_new', '3', ['udev_ref', 'udev_unref'], ''],
  ['udevadm', '8', [], ''],
- ['user@.service', '5', ['user-runtime-dir@.service'], ''],
+ ['user@.service',
+  '5',
+  ['systemd-user-runtime-dir', 'user-runtime-dir@.service'],
+  ''],
  ['userdbctl', '1', [], 'ENABLE_USERDB'],
  ['vconsole.conf', '5', [], 'ENABLE_VCONSOLE']
 ]
diff --git a/man/sd-bus-container-append.c b/man/sd-bus-container-append.c
new file mode 100644 (file)
index 0000000..e350ea0
--- /dev/null
@@ -0,0 +1,17 @@
+#include <systemd/sd-bus.h>
+
+int append_strings_to_message(sd_bus_message *m, const char *const *arr) {
+  int r;
+
+  r = sd_bus_message_open_container(m, 'a', "s");
+  if (r < 0)
+    return r;
+
+  for (const char *s = *arr; *s; s++) {
+    r = sd_bus_message_append(m, "s", s);
+    if (r < 0)
+      return r;
+  }
+
+  return sd_bus_message_close_container(m);
+}
diff --git a/man/sd-bus-container-read.c b/man/sd-bus-container-read.c
new file mode 100644 (file)
index 0000000..b6c95f4
--- /dev/null
@@ -0,0 +1,25 @@
+#include <stdio.h>
+
+#include <systemd/sd-bus.h>
+
+int read_strings_from_message(sd_bus_message *m) {
+  int r;
+
+  r = sd_bus_message_enter_container(m, 'a', "s");
+  if (r < 0)
+    return r;
+
+  for (;;) {
+    const char *s;
+
+    r = sd_bus_message_read(m, "s", &s);
+    if (r < 0)
+      return r;
+    if (r == 0)
+      break;
+
+    printf("%s\n", s);
+  }
+
+  return sd_bus_message_exit_container(m);
+}
index 76865e1f8ee671c99e3eba6aea10857d6196f149..199a4a81e998c36f547c86739d479e05288c50f1 100644 (file)
 
     <para>See
     <literallayout><citerefentry><refentrytitle>sd_bus_add_match</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_add_object</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_add_object_manager</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_add_object_vtable</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_add_fallback</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_add_fallback_vtable</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_add_filter</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_add_node_enumerator</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_attach_event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_call</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_call_async</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_call_method</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_call_method_async</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_can_send</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_creds_get_pid</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_creds_new_from_pid</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_close</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_default</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_emit_interfaces_added</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_emit_interfaces_added_strv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_emit_interfaces_removed</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_emit_interfaces_removed_strv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_emit_object_added</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_emit_object_removed</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_emit_properties_changed</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_emit_properties_changed_strv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_emit_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_emit_signalv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd-bus-errors</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_error_add_map</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_get_address</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_get_allow_interactive_authorization</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_get_bus_id</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_get_creds_mask</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_get_current_handler</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_get_current_message</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_get_current_slot</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_get_current_userdata</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_get_exit_on_disconnect</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_get_fd</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_get_method_call_timeout</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_get_n_queued_read</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_get_name_machine_id</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_get_name_creds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_get_owner_creds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_get_property</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_get_property_trivial</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_get_property_string</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_get_property_strv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_get_scope</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_get_tid</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_get_unique_name</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_interface_name_is_valid</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_is_monitor</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_is_bus_client</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_is_server</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_list_names</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_message_append</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_message_append_array</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_message_append_basic</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_message_append_string_memfd</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_message_append_strv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_message_at_end</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_message_close_container</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_message_copy</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_message_dump</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_message_enter_container</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_message_exit_container</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_message_get_allow_interactive_authorization</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_message_get_cookie</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_message_get_creds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_message_get_errno</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_message_get_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_message_get_monotonic_usec</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_message_get_sender</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_message_get_signature</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_message_get_type</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_message_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_message_new_method_call</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_message_new_method_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_message_new_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_message_open_container</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_message_peek_type</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_message_read</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_message_read_array</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_message_read_basic</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_message_read_strv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_message_rewind</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_message_seal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_message_set_allow_interactive_authorization</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_message_set_destination</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_message_set_sender</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_message_set_expect_reply</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_message_skip</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_message_verify_type</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_path_encode</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_process</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_query_sender_creds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_query_sender_privilege</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_reply_method_return</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_reply_method_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_request_name</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_send</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_send_to</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_set_address</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_set_allow_interactive_authorization</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_set_bus_client</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_set_close_on_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_set_connected_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_set_description</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_set_exit_on_disconnect</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_set_method_call_timeout</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_set_monitor</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_set_property</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_set_propertyv</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_set_sender</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_set_server</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_set_watch_bind</refentrytitle><manvolnum>3</manvolnum></citerefentry>
-<citerefentry><refentrytitle>sd_bus_set_close_on_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+<citerefentry><refentrytitle>sd_bus_slot_get_current_handler</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_slot_get_current_message</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_slot_get_current_userdata</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_slot_set_description</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_slot_set_destroy_callback</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_slot_set_floating</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_slot_set_userdata</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+<citerefentry><refentrytitle>sd_bus_start</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_track_add_name</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
 <citerefentry><refentrytitle>sd_bus_track_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>
 </literallayout>
diff --git a/man/sd-hwdb.xml b/man/sd-hwdb.xml
new file mode 100644 (file)
index 0000000..13552e5
--- /dev/null
@@ -0,0 +1,57 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd-hwdb" xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>sd-hwdb</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd-hwdb</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd-hwdb</refname>
+    <refpurpose>Read-only access to the hardware description database</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-hwdb.h&gt;</funcsynopsisinfo>
+    </funcsynopsis>
+
+    <cmdsynopsis>
+      <command>pkg-config --cflags --libs libsystemd</command>
+    </cmdsynopsis>
+
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><filename>sd-hwdb.h</filename> allows read-only access the systemd database of hardware properties.
+    See <citerefentry><refentrytitle>hwdb</refentrytitle><manvolnum>7</manvolnum></citerefentry> and
+    <citerefentry><refentrytitle>systemd-hwdb</refentrytitle><manvolnum>8</manvolnum></citerefentry> for more
+    information about the database.</para>
+
+    <para>See <citerefentry><refentrytitle>sd_hwdb_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    and <citerefentry><refentrytitle>sd_hwdb_get</refentrytitle><manvolnum>3</manvolnum></citerefentry> for
+    information about the functions available.</para>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>See Also</title>
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-udevd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
index 2787bd74743270a291f4c813ffa68f3cb2a486e0..ecc6e28a84a021568c5d1a49ba8b4025eb600bd1 100644 (file)
 
         <listitem><para>When set to <literal>1</literal>, this device automatically
         generates a new and independent seat, which is named after the path of the
-        device. This is set for specialized USB hubs like the Plugable devices, which when
+        device. This is set for specialized USB hubs like the Pluggable devices, which when
         plugged in should create a hotplug seat without further configuration.</para>
         </listitem>
       </varlistentry>
index a1065443869ceee9c582b3038bdbbb6ed37fb869..071060dde6987d98e3fb459858b61065c35c12f5 100644 (file)
@@ -33,7 +33,7 @@
     <funcsynopsis>
       <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
 
-      <funcprototype>
+      <funcprototype id="sd_bus_message_handler_t">
         <funcdef>typedef int (*<function>sd_bus_message_handler_t</function>)</funcdef>
         <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
         <paramdef>void *<parameter>userdata</parameter></paramdef>
diff --git a/man/sd_bus_add_node_enumerator.xml b/man/sd_bus_add_node_enumerator.xml
new file mode 100644 (file)
index 0000000..fd11e46
--- /dev/null
@@ -0,0 +1,137 @@
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_bus_add_node_enumerator"
+          xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>sd_bus_add_node_enumerator</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_bus_add_node_enumerator</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_bus_add_node_enumerator</refname>
+
+    <refpurpose>Add a node enumerator for a D-Bus object path prefix</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>typedef int (*<function>sd_bus_node_enumerator_t</function>)</funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>const char *<parameter>prefix</parameter></paramdef>
+        <paramdef>void *<parameter>userdata</parameter></paramdef>
+        <paramdef>char ***<parameter>ret_nodes</parameter></paramdef>
+        <paramdef>sd_bus_error *<parameter>ret_error</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_add_node_enumerator</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>sd_bus_slot **<parameter>slot</parameter></paramdef>
+        <paramdef>const char *<parameter>path</parameter></paramdef>
+        <paramdef>sd_bus_node_enumerator_t <parameter>callback</parameter></paramdef>
+        <paramdef>void *<parameter>userdata</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>sd_bus_add_node_enumerator()</function> adds a D-Bus node enumerator for the
+    given path prefix. The given callback is called to enumerate all the available objects with
+    the given path prefix when required (e.g. when
+    <constant>org.freedesktop.DBus.Introspectable.Introspect</constant> or
+    <constant>org.freedesktop.DBus.ObjectManager.GetManagedObjects</constant> are called on a
+    D-Bus service managed by sd-bus).</para>
+
+    <para><parameter>callback</parameter> is called with the path and userdata pointer registered
+    with <function>sd_bus_add_node_enumerator()</function>. When called, it should store all the
+    child object paths of the given path prefix in <parameter>ret_nodes</parameter> and return the
+    number of child objects under the given prefix. If an error occurs, it can either return a
+    negative integer, set <parameter>ret_error</parameter> to a non-empty error or do both. Any
+    errors returned by the callback are encoded as D-Bus errors and sent back to the caller. Errors
+    in <parameter>ret_error</parameter> take priority over negative return values.</para>
+
+    <para>Note that a node enumerator callback will only ever be called for a single  path prefix
+    and hence, for normal operation, <parameter>prefix</parameter> can be ignored. Also, a node
+    enumerator is only used to enumerate the available child objects under a given prefix. To
+    install a handler for a set of dynamic child objects, use
+    <citerefentry><refentrytitle>sd_bus_add_fallback_vtable</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+    </para>
+
+    <para>When <function>sd_bus_add_node_enumerator()</function> succeeds, a slot is created
+    internally. If the output parameter <replaceable>slot</replaceable> is <constant>NULL</constant>,
+    a "floating" slot object is created, see
+    <citerefentry><refentrytitle>sd_bus_slot_set_floating</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+    Otherwise, a pointer to the slot object is returned. In that case, the reference to the slot
+    object should be dropped when the node enumerator is not needed anymore, see
+    <citerefentry><refentrytitle>sd_bus_slot_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On success, <function>sd_bus_add_node_enumerator()</function> returns a non-negative
+    integer. On failure, it returns a negative errno-style error code.</para>
+
+    <refsect2>
+      <title>Errors</title>
+
+      <para>Returned errors may indicate the following problems:</para>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>-EINVAL</constant></term>
+
+          <listitem><para>One of the required parameters is <constant>NULL</constant> or
+          <parameter>path</parameter> is not a valid object path.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENOPKG</constant></term>
+
+          <listitem><para>The bus cannot be resolved.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ECHILD</constant></term>
+
+          <listitem><para>The bus was created in a different process.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENOMEM</constant></term>
+
+          <listitem><para>Memory allocation failed.</para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>busctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_add_fallback_vtable</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_slot_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+</refentry>
diff --git a/man/sd_bus_add_object.xml b/man/sd_bus_add_object.xml
new file mode 100644 (file)
index 0000000..bce71bd
--- /dev/null
@@ -0,0 +1,651 @@
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_bus_add_object"
+          xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>sd_bus_add_object</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_bus_add_object</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_bus_add_object</refname>
+    <refname>sd_bus_add_fallback</refname>
+    <refname>sd_bus_add_object_vtable</refname>
+    <refname>sd_bus_add_fallback_vtable</refname>
+    <refname>sd_bus_add_filter</refname>
+    <refname>SD_BUS_VTABLE_START</refname>
+    <refname>SD_BUS_VTABLE_END</refname>
+    <refname>SD_BUS_METHOD_WITH_NAMES_OFFSET</refname>
+    <refname>SD_BUS_METHOD_WITH_NAMES</refname>
+    <refname>SD_BUS_METHOD_WITH_OFFSET</refname>
+    <refname>SD_BUS_METHOD</refname>
+    <refname>SD_BUS_SIGNAL_WITH_NAMES</refname>
+    <refname>SD_BUS_SIGNAL</refname>
+    <refname>SD_BUS_WRITABLE_PROPERTY</refname>
+    <refname>SD_BUS_PROPERTY</refname>
+    <refname>SD_BUS_PARAM</refname>
+
+    <refpurpose>Declare properties and methods for a D-Bus path</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-bus-vtable.h&gt;</funcsynopsisinfo>
+
+      <xi:include href="sd_bus_add_match.xml" xpointer="sd_bus_message_handler_t"/>
+
+      <funcprototype>
+        <funcdef>typedef int (*<function>sd_bus_property_get_t</function>)</funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>const char *<parameter>path</parameter></paramdef>
+        <paramdef>const char *<parameter>interface</parameter></paramdef>
+        <paramdef>const char *<parameter>property</parameter></paramdef>
+        <paramdef>sd_bus_message *<parameter>reply</parameter></paramdef>
+        <paramdef>void *<parameter>userdata</parameter></paramdef>
+        <paramdef>sd_bus_error *<parameter>ret_error</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>typedef int (*<function>sd_bus_property_set_t</function>)</funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>const char *<parameter>path</parameter></paramdef>
+        <paramdef>const char *<parameter>interface</parameter></paramdef>
+        <paramdef>const char *<parameter>property</parameter></paramdef>
+        <paramdef>sd_bus_message *<parameter>value</parameter></paramdef>
+        <paramdef>void *<parameter>userdata</parameter></paramdef>
+        <paramdef>sd_bus_error *<parameter>ret_error</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>typedef int (*<function>sd_bus_object_find_t</function>)</funcdef>
+        <paramdef>const char *<parameter>path</parameter></paramdef>
+        <paramdef>const char *<parameter>interface</parameter></paramdef>
+        <paramdef>void *<parameter>userdata</parameter></paramdef>
+        <paramdef>void **<parameter>ret_found</parameter></paramdef>
+        <paramdef>sd_bus_error *<parameter>ret_error</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_add_object</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>sd_bus_slot **<parameter>slot</parameter></paramdef>
+        <paramdef>const char *<parameter>path</parameter></paramdef>
+        <paramdef>sd_bus_message_handler_t <parameter>callback</parameter></paramdef>
+        <paramdef>void *<parameter>userdata</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_add_fallback</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>sd_bus_slot **<parameter>slot</parameter></paramdef>
+        <paramdef>const char *<parameter>path</parameter></paramdef>
+        <paramdef>sd_bus_message_handler_t <parameter>callback</parameter></paramdef>
+        <paramdef>void *<parameter>userdata</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_add_object_vtable</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>sd_bus_slot **<parameter>slot</parameter></paramdef>
+        <paramdef>const char *<parameter>path</parameter></paramdef>
+        <paramdef>const char *<parameter>interface</parameter></paramdef>
+        <paramdef>const sd_bus_vtable *<parameter>vtable</parameter></paramdef>
+        <paramdef>void *<parameter>userdata</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_add_fallback_vtable</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>sd_bus_slot **<parameter>slot</parameter></paramdef>
+        <paramdef>const char *<parameter>prefix</parameter></paramdef>
+        <paramdef>const char *<parameter>interface</parameter></paramdef>
+        <paramdef>const sd_bus_vtable *<parameter>vtable</parameter></paramdef>
+        <paramdef>sd_bus_object_find_t <parameter>find</parameter></paramdef>
+        <paramdef>void *<parameter>userdata</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_add_filter</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>sd_bus_slot **<parameter>slot</parameter></paramdef>
+        <paramdef>sd_bus_message_handler_t <parameter>callback</parameter></paramdef>
+        <paramdef>void *<parameter>userdata</parameter></paramdef>
+      </funcprototype>
+
+      <para>
+        <constant>SD_BUS_VTABLE_START(<replaceable>flags</replaceable>)</constant>
+      </para>
+      <para>
+        <constant>SD_BUS_VTABLE_END</constant>
+      </para>
+      <para>
+        <constant>SD_BUS_METHOD_WITH_ARGS_OFFSET(
+        <replaceable>member</replaceable>,
+        <replaceable>args</replaceable>,
+        <replaceable>result</replaceable>,
+        <replaceable>handler</replaceable>,
+        <replaceable>offset</replaceable>,
+        <replaceable>flags</replaceable>)
+        </constant>
+      </para>
+      <para>
+        <constant>SD_BUS_METHOD_WITH_ARGS(
+        <replaceable>member</replaceable>,
+        <replaceable>args</replaceable>,
+        <replaceable>result</replaceable>,
+        <replaceable>handler</replaceable>,
+        <replaceable>flags</replaceable>)
+        </constant>
+      </para>
+      <para>
+        <constant>SD_BUS_METHOD_WITH_NAMES_OFFSET(
+        <replaceable>member</replaceable>,
+        <replaceable>signature</replaceable>,
+        <replaceable>in_names</replaceable>,
+        <replaceable>result</replaceable>,
+        <replaceable>out_names</replaceable>,
+        <replaceable>handler</replaceable>,
+        <replaceable>offset</replaceable>,
+        <replaceable>flags</replaceable>)
+        </constant>
+      </para>
+      <para>
+        <constant>SD_BUS_METHOD_WITH_NAMES(
+        <replaceable>member</replaceable>,
+        <replaceable>signature</replaceable>,
+        <replaceable>in_names</replaceable>,
+        <replaceable>result</replaceable>,
+        <replaceable>out_names</replaceable>,
+        <replaceable>handler</replaceable>,
+        <replaceable>flags</replaceable>)
+        </constant>
+      </para>
+      <para>
+        <constant>SD_BUS_METHOD_WITH_OFFSET(
+        <replaceable>member</replaceable>,
+        <replaceable>signature</replaceable>,
+        <replaceable>result</replaceable>,
+        <replaceable>handler</replaceable>,
+        <replaceable>offset</replaceable>,
+        <replaceable>flags</replaceable>)
+        </constant>
+      </para>
+      <para>
+        <constant>SD_BUS_METHOD(
+        <replaceable>member</replaceable>,
+        <replaceable>signature</replaceable>,
+        <replaceable>result</replaceable>,
+        <replaceable>handler</replaceable>,
+        <replaceable>flags</replaceable>)
+        </constant>
+      </para>
+      <para>
+        <constant>SD_BUS_SIGNAL_WITH_ARGS(
+        <replaceable>member</replaceable>,
+        <replaceable>args</replaceable>,
+        <replaceable>flags</replaceable>)
+        </constant>
+      </para>
+      <para>
+        <constant>SD_BUS_SIGNAL_WITH_NAMES(
+        <replaceable>member</replaceable>,
+        <replaceable>signature</replaceable>,
+        <replaceable>names</replaceable>,
+        <replaceable>flags</replaceable>)
+        </constant>
+      </para>
+      <para>
+        <constant>SD_BUS_SIGNAL(
+        <replaceable>member</replaceable>,
+        <replaceable>signature</replaceable>,
+        <replaceable>flags</replaceable>)
+        </constant>
+      </para>
+      <para>
+        <constant>SD_BUS_WRITABLE_PROPERTY(
+        <replaceable>member</replaceable>,
+        <replaceable>signature</replaceable>,
+        <replaceable>get</replaceable>,
+        <replaceable>set</replaceable>,
+        <replaceable>offset</replaceable>,
+        <replaceable>flags</replaceable>)
+        </constant>
+      </para>
+      <para>
+        <constant>SD_BUS_PROPERTY(
+        <replaceable>member</replaceable>,
+        <replaceable>signature</replaceable>,
+        <replaceable>get</replaceable>,
+        <replaceable>offset</replaceable>,
+        <replaceable>flags</replaceable>)
+        </constant>
+      </para>
+      <para>
+        <constant>SD_BUS_PARAM(<replaceable>name</replaceable>)</constant>
+        <constant>SD_BUS_ARGS(<replaceable>...</replaceable>)</constant>
+        <constant>SD_BUS_RESULT(<replaceable>...</replaceable>)</constant>
+        <constant>SD_BUS_NO_ARGS</constant>
+        <constant>SD_BUS_NO_RESULT</constant>
+      </para>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>sd_bus_add_object_vtable()</function> is used to declare attributes for the
+    object path <parameter>path</parameter> connected to the bus connection
+    <parameter>bus</parameter> under the interface <parameter>interface</parameter>. The table
+    <parameter>vtable</parameter> may contain property declarations using
+    <constant>SD_BUS_PROPERTY()</constant> or <constant>SD_BUS_WRITABLE_PROPERTY()</constant>,
+    method declarations using <constant>SD_BUS_METHOD()</constant>,
+    <constant>SD_BUS_METHOD_WITH_NAMES()</constant>,
+    <constant>SD_BUS_METHOD_WITH_OFFSET()</constant>, or
+    <constant>SD_BUS_METHOD_WITH_NAMES_OFFSET()</constant>, and signal declarations using
+    <constant>SD_BUS_SIGNAL_WITH_NAMES()</constant> or <constant>SD_BUS_SIGNAL()</constant>, see
+    below. The <replaceable>userdata</replaceable> parameter contains a pointer that will be passed
+    to various callback functions. It may be specified as <constant>NULL</constant> if no value is
+    necessary. An interface can have any number of vtables attached to it.</para>
+
+    <para><function>sd_bus_add_fallback_vtable()</function> is similar to
+    <function>sd_bus_add_object_vtable()</function>, but is used to register "fallback" attributes.
+    When looking for an attribute declaration, bus object paths registered with
+    <function>sd_bus_add_object_vtable()</function> are checked first. If no match is found, the
+    fallback vtables are checked for each prefix of the bus object path, i.e. with the last
+    slash-separated components successively removed. This allows the vtable to be used for an
+    arbitrary number of dynamically created objects.</para>
+
+    <para>Parameter <replaceable>find</replaceable> is a function which is used to locate the target
+    object based on the bus object path <replaceable>path</replaceable>. It must return
+    <constant>1</constant> and set the <parameter>ret_found</parameter> output parameter if the
+    object is found, return <constant>0</constant> if the object was not found, and return a
+    negative errno-style error code or initialize the error structure
+    <replaceable>ret_error</replaceable> on error. The pointer passed in
+    <parameter>ret_found</parameter> will be used as the <parameter>userdata</parameter> parameter
+    for the callback functions (offset by the <parameter>offset</parameter> offsets as specified in
+    the vtable entries).</para>
+
+    <para><function>sd_bus_add_object()</function> attaches a callback directly to the object path
+    <parameter>path</parameter>. An object path can have any number of callbacks attached to it.
+    Each callback is prepended to the list of callbacks which are always called in order.
+    <function>sd_bus_add_fallback()</function> is similar to
+    <function>sd_bus_add_object()</function> but applies to fallback paths instead.</para>
+
+    <para><function>sd_bus_add_filter()</function> installs a callback that is invoked for each
+    incoming D-Bus message. Filters can be used to handle logic common to all messages received by
+    a service (e.g. authentication or authorization).</para>
+
+    <para>When a request is received, any associated callbacks are called sequentially until a
+    callback returns a non-zero integer. Return zero from a callback to give other callbacks the
+    chance to process the request. Callbacks are called in the following order: first, global
+    callbacks installed with <function>sd_bus_add_filter()</function> are called. Second, callbacks
+    attached directly to the request object path are called, followed by any D-Bus method callbacks
+    attached to the request object path, interface and member. Finally, the property callbacks
+    attached to the request object path, interface and member are called. If the final callback
+    returns zero, an error reply is sent back to the caller indicating no matching object for the
+    request was found. Note that you can return a positive integer from a callback without
+    immediately sending a reply. This informs sd-bus this callback will take responsibility for
+    replying to the request without forcing the callback to produce a reply immediately. This allows
+    a callback to perform any number of asynchronous operations required to construct a reply. Note
+    that if producing a reply takes too long, the method call will time out at the caller.</para>
+
+    <para>If a callback was invoked to handle a request that expects a reply and the callback
+    returns a negative value, the value is interpreted as a negative errno-style error code and sent
+    back to the caller as a D-Bus error as if
+    <citerefentry><refentrytitle>sd_bus_reply_method_errno</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    was called. Additionally, all callbacks take a <structname>sd_bus_error</structname> output
+    parameter that can be used to provide more detailed error information. If
+    <parameter>ret_error</parameter> is set when the callback finishes, the corresponding D-Bus
+    error is sent back to the caller as if
+    <citerefentry><refentrytitle>sd_bus_reply_method_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    was called. Any error stored in <parameter>ret_error</parameter> takes priority over any
+    negative values returned by the same callback when determining which error to send back to
+    the caller. Use
+    <citerefentry><refentrytitle>sd_bus_error_set</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    or one of its variants to set <parameter>ret_error</parameter> and return a negative integer
+    from a callback with a single function call. To send an error reply after a callback has already
+    finished, use
+    <citerefentry><refentrytitle>sd_bus_reply_method_errno</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    or one of its variants.</para>
+
+    <para>For all functions, a match slot is created internally. If the output parameter
+    <replaceable>slot</replaceable> is <constant>NULL</constant>, a "floating" slot object is
+    created, see
+    <citerefentry><refentrytitle>sd_bus_slot_set_floating</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+    Otherwise, a pointer to the slot object is returned. In that case, the reference to the slot
+    object should be dropped when the vtable is not needed anymore, see
+    <citerefentry><refentrytitle>sd_bus_slot_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+    </para>
+
+    <refsect2>
+      <title>The <structname>sd_bus_vtable</structname> array</title>
+
+      <para>The array consists of the structures of type <structname>sd_bus_vtable</structname>, but it
+      should never be filled in manually, but through one of the following macros:</para>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>SD_BUS_VTABLE_START()</constant></term>
+          <term><constant>SD_BUS_VTABLE_END</constant></term>
+
+          <listitem><para>Those must always be the first and last element.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>SD_BUS_METHOD_WITH_ARGS_OFFSET()</constant></term>
+          <term><constant>SD_BUS_METHOD_WITH_ARGS()</constant></term>
+
+          <listitem><para>Declare a D-Bus method with the name <replaceable>member</replaceable>,
+          arguments <replaceable>args</replaceable> and result <replaceable>result</replaceable>.
+          <replaceable>args</replaceable> expects a sequence of argument type/name pairs wrapped in the
+          <constant>SD_BUS_ARGS()</constant> macro. The elements at even indices in this list describe the
+          types of the method's arguments. The method's parameter signature is the concatenation of all the
+          string literals at even indices in <replaceable>args</replaceable>. If a method has no parameters,
+          pass <constant>SD_BUS_NO_ARGS</constant> to <replaceable>args</replaceable>. The elements at uneven
+          indices describe the names of the method's arguments. <replaceable>result</replaceable> expects a
+          sequence of type/name pairs wrapped in the <constant>SD_BUS_RESULT()</constant> macro in the same
+          format as <constant>SD_BUS_ARGS()</constant>. The method's result signature is the concatenation of
+          all the string literals at even indices in <replaceable>result</replaceable>. If a method has no
+          result, pass <constant>SD_BUS_NO_RESULT</constant> to <replaceable>result</replaceable>. Note that
+          argument types are expected to be quoted string literals and argument names are expected to be
+          unquoted string literals. See below for a complete example.</para>
+
+          <para>The handler function <replaceable>handler</replaceable> must be of type
+          <function>sd_bus_message_handler_t</function>. It will be called to handle the incoming messages
+          that call this method. It receives a pointer that is the <replaceable>userdata</replaceable>
+          parameter passed to the registration function offset by <replaceable>offset</replaceable> bytes.
+          This may be used to pass pointers to different fields in the same data structure to different
+          methods in the same vtable. To send a reply from <parameter>handler</parameter>, call
+          <citerefentry><refentrytitle>sd_bus_reply_method_return</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+          with the message the callback was invoked with. Parameter <replaceable>flags</replaceable> is a
+          combination of flags, see below.</para>
+
+          <constant>SD_BUS_METHOD_WITH_ARGS()</constant> is a shorthand for calling
+          <constant>SD_BUS_METHOD_WITH_ARGS_OFFSET()</constant> with an offset of zero.
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>SD_BUS_METHOD_WITH_NAMES_OFFSET()</constant></term>
+          <term><constant>SD_BUS_METHOD_WITH_NAMES()</constant></term>
+          <term><constant>SD_BUS_METHOD_WITH_OFFSET()</constant></term>
+          <term><constant>SD_BUS_METHOD()</constant></term>
+
+          <listitem><para>Declare a D-Bus method with the name <replaceable>member</replaceable>,
+          parameter signature <replaceable>signature</replaceable>, result signature
+          <replaceable>result</replaceable>. Parameters <replaceable>in_names</replaceable> and
+          <replaceable>out_names</replaceable> specify the argument names of the input and output
+          arguments in the function signature. <replaceable>in_names</replaceable> and
+          <replaceable>out_names</replaceable> should be created using the
+          <constant>SD_BUS_PARAM()</constant> macro, see below. In all other regards, this macro behaves
+          exactly the same as <constant>SD_BUS_METHOD_WITH_ARGS_OFFSET()</constant>.</para>
+
+          <para><constant>SD_BUS_METHOD_WITH_NAMES()</constant>,
+          <constant>SD_BUS_METHOD_WITH_OFFSET()</constant>, and <constant>SD_BUS_METHOD()</constant>
+          are variants which specify zero offset (<replaceable>userdata</replaceable> parameter is
+          passed with no change), leave the names unset (i.e. no parameter names), or both.</para>
+
+          <para>Prefer using <constant>SD_BUS_METHOD_WITH_ARGS_OFFSET()</constant> and
+          <constant>SD_BUS_METHOD_WITH_ARGS()</constant> over these macros as they allow specifying argument
+          types and names next to each other which is less error-prone than first specifying all argument
+          types followed by specifying all argument names.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>SD_BUS_SIGNAL_WITH_ARGS()</constant></term>
+
+          <listitem><para>>Declare a D-Bus signal with the name <replaceable>member</replaceable> and
+          arguments <replaceable>args</replaceable>. <replaceable>args</replaceable> expects a sequence of
+          argument type/name pairs wrapped in the <constant>SD_BUS_ARGS()</constant> macro. The elements at
+          even indices in this list describe the types of the signal's arguments. The signal's parameter
+          signature is the concatenation of all the string literals at even indices in
+          <replaceable>args</replaceable>. If a signal has no parameters, pass
+          <constant>SD_BUS_NO_ARGS</constant> to <replaceable>args</replaceable>. The elements at uneven
+          indices describe the names of the signal's arguments. Parameter <replaceable>flags</replaceable> is
+          a combination of flags. See below for a complete example.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>SD_BUS_SIGNAL_WITH_NAMES()</constant></term>
+          <term><constant>SD_BUS_SIGNAL()</constant></term>
+
+          <listitem><para>Declare a D-Bus signal with the name <replaceable>member</replaceable>,
+          parameter signature <replaceable>signature</replaceable>, and argument names
+          <replaceable>names</replaceable>. <replaceable>names</replaceable> should be
+          created using the <constant>SD_BUS_PARAM()</constant> macro, see below.
+          Parameter <replaceable>flags</replaceable> is a combination of flags, see below.
+          </para>
+
+          <para><constant>SD_BUS_SIGNAL()</constant> is equivalent to
+          <constant>SD_BUS_SIGNAL_WITH_NAMES()</constant> with the <replaceable>names</replaceable> parameter
+          unset (i.e. no parameter names).</para>
+
+          <para>Prefer using <constant>SD_BUS_SIGNAL_WITH_ARGS()</constant> over these macros as it allows
+          specifying argument types and names next to each other which is less error-prone than first
+          specifying all argument types followed by specifying all argument names.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>SD_BUS_WRITABLE_PROPERTY()</constant></term>
+          <term><constant>SD_BUS_PROPERTY()</constant></term>
+
+          <listitem><para>Declare a D-Bus property with the name <replaceable>member</replaceable>
+          and value signature <replaceable>signature</replaceable>. Parameters
+          <replaceable>get</replaceable> and <replaceable>set</replaceable> are the getter and
+          setter methods. They are called with a pointer that is the
+          <replaceable>userdata</replaceable> parameter passed to the registration function offset
+          by <replaceable>offset</replaceable> bytes. This may be used pass pointers to different
+          fields in the same data structure to different setters and getters in the same vtable.
+          Parameter <replaceable>flags</replaceable> is a combination of flags, see below.</para>
+
+          <para>The setter and getter methods may be omitted (specified as
+          <constant>NULL</constant>), if the property is one of the basic types or
+          <literal>as</literal> in case of read-only properties. In those cases, the
+          <replaceable>userdata</replaceable> and <replaceable>offset</replaceable> parameters must
+          together point to a valid variable of the corresponding type. A default setter and getter
+          will be provided, which simply copy the argument between this variable and the message.
+          </para>
+
+          <para><constant>SD_BUS_PROPERTY()</constant> is used to define a read-only property.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>SD_BUS_PARAM()</constant></term>
+          <listitem><para>Parameter names should be wrapped in this macro, see the example below.
+          </para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+
+    <refsect2>
+      <title>Flags</title>
+
+      <para>The <replaceable>flags</replaceable> parameter is used to specify a combination of
+      <ulink url="https://dbus.freedesktop.org/doc/dbus-specification.html#introspection-format">D-Bus annotations</ulink>.
+      </para>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>SD_BUS_VTABLE_DEPRECATED</constant></term>
+
+          <listitem><para>Mark this vtable entry as deprecated using the
+          <constant>org.freedesktop.DBus.Deprecated</constant> annotation in introspection data. If
+          specified for <constant>SD_BUS_VTABLE_START()</constant>, the annotation is applied to the
+          enclosing interface.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>SD_BUS_VTABLE_HIDDEN</constant></term>
+
+          <listitem><para>Make this vtable entry hidden. It will not be shown in introspection data.
+          If specified for <constant>SD_BUS_VTABLE_START()</constant>, all entries in the array are
+          hidden.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>SD_BUS_VTABLE_UNPRIVILEGED</constant></term>
+
+          <listitem><para>Mark this vtable entry as unprivileged. If not specified, the
+          <constant>org.freedesktop.systemd1.Privileged</constant> annotation with value
+          <literal>true</literal> will be shown in introspection data.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>SD_BUS_VTABLE_METHOD_NO_REPLY</constant></term>
+
+          <listitem><para>Mark his vtable entry as a method that will not return a reply using the
+          <constant>org.freedesktop.DBus.Method.NoReply</constant> annotation in introspection data.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>SD_BUS_VTABLE_PROPERTY_CONST</constant></term>
+          <term><constant>SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE</constant></term>
+          <term><constant>SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION</constant></term>
+
+          <listitem><para>Those three flags correspond to different values of the
+          <constant>org.freedesktop.DBus.Property.EmitsChangedSignal</constant> annotation, which
+          specifies whether the
+          <constant>org.freedesktop.DBus.Properties.PropertiesChanged</constant> signal is emitted
+          whenever the property changes. <constant>SD_BUS_VTABLE_PROPERTY_CONST</constant>
+          corresponds to <constant>const</constant> and means that the property never changes during
+          the lifetime of the object it belongs to, so no signal needs to be emitted.
+          <constant>SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE</constant> corresponds to
+          <constant>true</constant> and means that the signal is emitted.
+          <constant>SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION</constant> corresponds to
+          <constant>invalidates</constant> and means that the signal is emitted, but the value is
+          not included in the signal.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>SD_BUS_VTABLE_PROPERTY_EXPLICIT</constant></term>
+
+          <listitem><para>Mark this vtable property entry as requiring explicit request to for the
+          value to be shown (generally because the value is large or slow to calculate). This entry
+          cannot be combined with <constant>SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE</constant>, and will
+          not be shown in property listings by default (e.g. <command>busctl introspect</command>).
+          This corresponds to the <constant>org.freedesktop.systemd1.Explicit</constant> annotation
+          in introspection data.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>SD_BUS_VTABLE_SENSITIVE</constant></term>
+
+          <listitem><para>Mark this vtable method entry as processing sensitive data. When set,
+          incoming method call messages and their outgoing reply messages are marked as sensitive using
+          <citerefentry><refentrytitle>sd_bus_message_sensitive</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+          so that they are erased from memory when freed.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>SD_BUS_VTABLE_ABSOLUTE_OFFSET</constant></term>
+
+          <listitem><para>Mark this vtable method or property entry so that the user data pointer passed to
+          its associated handler functions is determined slightly differently: instead of adding the offset
+          parameter of the entry to the user data pointer specified during vtable registration, the offset is
+          passed directly, converted to a pointer, without taking the user data pointer specified during
+          vtable registration into account.</para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>Examples</title>
+
+    <example>
+      <title>Create a simple listener on the bus</title>
+
+      <programlisting><xi:include href="vtable-example.c" parse="text" /></programlisting>
+
+      <para>This creates a simple client on the bus (the user bus, when run as normal user). We may
+      use the D-Bus <constant>org.freedesktop.DBus.Introspectable.Introspect</constant> call to
+      acquire the XML description of the interface:</para>
+
+      <programlisting><xi:include href="vtable-example.xml" parse="text" /></programlisting>
+    </example>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On success, <function>sd_bus_add_object_vtable()</function> and
+    <function>sd_bus_add_fallback_vtable()</function> return a non-negative integer. On
+    failure, they return a negative errno-style error code.</para>
+
+    <refsect2>
+      <title>Errors</title>
+
+      <para>Returned errors may indicate the following problems:</para>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>-EINVAL</constant></term>
+
+          <listitem><para>One of the required parameters is <constant>NULL</constant> or invalid. A
+          reserved D-Bus interface was passed as the <replaceable>interface</replaceable> parameter.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENOPKG</constant></term>
+
+          <listitem><para>The bus cannot be resolved.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ECHILD</constant></term>
+
+          <listitem><para>The bus was created in a different process.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENOMEM</constant></term>
+
+          <listitem><para>Memory allocation failed.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-EPROTOTYPE</constant></term>
+
+          <listitem><para><function>sd_bus_add_object_vtable</function> and
+          <function>sd_bus_add_fallback_vtable</function> have been both called for the same bus
+          object path, which is not allowed.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-EEXIST</constant></term>
+
+          <listitem><para>This vtable has already been registered for this
+          <replaceable>interface</replaceable> and <replaceable>path</replaceable>.
+          </para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>busctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_emit_properties_changed</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_emit_object_added</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+</refentry>
diff --git a/man/sd_bus_add_object_manager.xml b/man/sd_bus_add_object_manager.xml
new file mode 100644 (file)
index 0000000..cc442d1
--- /dev/null
@@ -0,0 +1,118 @@
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_bus_add_object_manager"
+          xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>sd_bus_add_object_manager</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_bus_add_object_manager</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_bus_add_object_manager</refname>
+
+    <refpurpose>Add a D-Bus object manager for a D-Bus object sub-tree</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_add_object_manager</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>sd_bus_slot **<parameter>slot</parameter></paramdef>
+        <paramdef>const char *<parameter>path</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>sd_bus_add_object_manager()</function> installs a handler for the given path
+    that implements the <function>GetManagedObjects()</function> method of the
+    <constant>org.freedesktop.DBus.ObjectManager</constant> interface. See
+    <ulink url="https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager">
+    org.freedesktop.DBus.ObjectManager</ulink> for more information.</para>
+
+    <para>To implement the <function>InterfacesAdded</function> and
+    <function>InterfacesRemoved</function> signals of the
+    <constant>org.freedesktop.DBus.ObjectManager</constant> interface, call
+    <citerefentry><refentrytitle>sd_bus_emit_interfaces_added</refentrytitle><manvolnum>3</manvolnum></citerefentry> and
+    <citerefentry><refentrytitle>sd_bus_emit_interfaces_removed</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    whenever interfaces are added or removed from the sub-tree, respectively.</para>
+
+    <para>When <function>sd_bus_add_object_manager()</function> succeeds, a slot is created
+    internally. If the output parameter <replaceable>slot</replaceable> is <constant>NULL</constant>,
+    a "floating" slot object is created, see
+    <citerefentry><refentrytitle>sd_bus_slot_set_floating</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+    Otherwise, a pointer to the slot object is returned. In that case, the reference to the slot
+    object should be dropped when the object manager is not needed anymore, see
+    <citerefentry><refentrytitle>sd_bus_slot_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On success, <function>sd_bus_add_object_manager()</function> returns a non-negative
+    integer. On failure, it returns a negative errno-style error code.</para>
+
+    <refsect2>
+      <title>Errors</title>
+
+      <para>Returned errors may indicate the following problems:</para>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>-EINVAL</constant></term>
+
+          <listitem><para>One of the required parameters is <constant>NULL</constant> or
+          <parameter>path</parameter> is not a valid object path.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENOPKG</constant></term>
+
+          <listitem><para>The bus cannot be resolved.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ECHILD</constant></term>
+
+          <listitem><para>The bus was created in a different process.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENOMEM</constant></term>
+
+          <listitem><para>Memory allocation failed.</para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>busctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_add_object_vtable</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_emit_interfaces_added</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_slot_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+</refentry>
diff --git a/man/sd_bus_add_object_vtable.xml b/man/sd_bus_add_object_vtable.xml
deleted file mode 100644 (file)
index 9d7e30a..0000000
+++ /dev/null
@@ -1,473 +0,0 @@
-<?xml version='1.0'?> <!--*-nxml-*-->
-<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
-  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
-<!-- SPDX-License-Identifier: LGPL-2.1+ -->
-
-<refentry id="sd_bus_add_object_vtable"
-          xmlns:xi="http://www.w3.org/2001/XInclude">
-
-  <refentryinfo>
-    <title>sd_bus_add_object_vtable</title>
-    <productname>systemd</productname>
-  </refentryinfo>
-
-  <refmeta>
-    <refentrytitle>sd_bus_add_object_vtable</refentrytitle>
-    <manvolnum>3</manvolnum>
-  </refmeta>
-
-  <refnamediv>
-    <refname>sd_bus_add_object_vtable</refname>
-    <refname>sd_bus_add_fallback_vtable</refname>
-    <refname>SD_BUS_VTABLE_START</refname>
-    <refname>SD_BUS_VTABLE_END</refname>
-    <refname>SD_BUS_METHOD_WITH_NAMES_OFFSET</refname>
-    <refname>SD_BUS_METHOD_WITH_NAMES</refname>
-    <refname>SD_BUS_METHOD_WITH_OFFSET</refname>
-    <refname>SD_BUS_METHOD</refname>
-    <refname>SD_BUS_SIGNAL_WITH_NAMES</refname>
-    <refname>SD_BUS_SIGNAL</refname>
-    <refname>SD_BUS_WRITABLE_PROPERTY</refname>
-    <refname>SD_BUS_PROPERTY</refname>
-    <refname>SD_BUS_PARAM</refname>
-
-    <refpurpose>Declare properties and methods for a D-Bus path</refpurpose>
-  </refnamediv>
-
-  <refsynopsisdiv>
-    <funcsynopsis>
-      <funcsynopsisinfo>#include &lt;systemd/sd-bus-vtable.h&gt;</funcsynopsisinfo>
-
-      <funcprototype>
-        <funcdef>typedef int (*<function>sd_bus_message_handler_t</function>)</funcdef>
-        <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
-        <paramdef>void *<parameter>userdata</parameter></paramdef>
-        <paramdef>sd_bus_error *<parameter>ret_error</parameter></paramdef>
-      </funcprototype>
-
-      <funcprototype>
-        <funcdef>typedef int (*<function>sd_bus_property_get_t</function>)</funcdef>
-        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
-        <paramdef>const char *<parameter>path</parameter></paramdef>
-        <paramdef>const char *<parameter>interface</parameter></paramdef>
-        <paramdef>const char *<parameter>property</parameter></paramdef>
-        <paramdef>sd_bus_message *<parameter>reply</parameter></paramdef>
-        <paramdef>void *<parameter>userdata</parameter></paramdef>
-        <paramdef>sd_bus_error *<parameter>ret_error</parameter></paramdef>
-      </funcprototype>
-
-      <funcprototype>
-        <funcdef>typedef int (*<function>sd_bus_property_set_t</function>)</funcdef>
-        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
-        <paramdef>const char *<parameter>path</parameter></paramdef>
-        <paramdef>const char *<parameter>interface</parameter></paramdef>
-        <paramdef>const char *<parameter>property</parameter></paramdef>
-        <paramdef>sd_bus_message *<parameter>value</parameter></paramdef>
-        <paramdef>void *<parameter>userdata</parameter></paramdef>
-        <paramdef>sd_bus_error *<parameter>ret_error</parameter></paramdef>
-      </funcprototype>
-
-      <funcprototype>
-        <funcdef>typedef int (*<function>sd_bus_object_find_t</function>)</funcdef>
-        <paramdef>const char *<parameter>path</parameter></paramdef>
-        <paramdef>const char *<parameter>interface</parameter></paramdef>
-        <paramdef>void *<parameter>userdata</parameter></paramdef>
-        <paramdef>void **<parameter>ret_found</parameter></paramdef>
-        <paramdef>sd_bus_error *<parameter>ret_error</parameter></paramdef>
-      </funcprototype>
-
-      <funcprototype>
-        <funcdef>int <function>sd_bus_add_object_vtable</function></funcdef>
-        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
-        <paramdef>sd_bus_slot **<parameter>slot</parameter></paramdef>
-        <paramdef>const char *<parameter>path</parameter></paramdef>
-        <paramdef>const char *<parameter>interface</parameter></paramdef>
-        <paramdef>const sd_bus_vtable *<parameter>vtable</parameter></paramdef>
-        <paramdef>void *<parameter>userdata</parameter></paramdef>
-      </funcprototype>
-
-      <funcprototype>
-        <funcdef>int <function>sd_bus_add_fallback_vtable</function></funcdef>
-        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
-        <paramdef>sd_bus_slot **<parameter>slot</parameter></paramdef>
-        <paramdef>const char *<parameter>prefix</parameter></paramdef>
-        <paramdef>const char *<parameter>interface</parameter></paramdef>
-        <paramdef>const sd_bus_vtable *<parameter>vtable</parameter></paramdef>
-        <paramdef>sd_bus_object_find_t <parameter>find</parameter></paramdef>
-        <paramdef>void *<parameter>userdata</parameter></paramdef>
-      </funcprototype>
-
-      <para>
-        <constant>SD_BUS_VTABLE_START(<replaceable>flags</replaceable>)</constant>
-      </para>
-      <para>
-        <constant>SD_BUS_VTABLE_END</constant>
-      </para>
-      <para>
-        <constant>SD_BUS_METHOD_WITH_NAMES_OFFSET(
-        <replaceable>member</replaceable>,
-        <replaceable>signature</replaceable>,
-        <replaceable>in_names</replaceable>,
-        <replaceable>result</replaceable>,
-        <replaceable>out_names</replaceable>,
-        <replaceable>handler</replaceable>,
-        <replaceable>offset</replaceable>,
-        <replaceable>flags</replaceable>)
-        </constant>
-      </para>
-      <para>
-        <constant>SD_BUS_METHOD_WITH_NAMES(
-        <replaceable>member</replaceable>,
-        <replaceable>signature</replaceable>,
-        <replaceable>in_names</replaceable>,
-        <replaceable>result</replaceable>,
-        <replaceable>out_names</replaceable>,
-        <replaceable>handler</replaceable>,
-        <replaceable>flags</replaceable>)
-        </constant>
-      </para>
-      <para>
-        <constant>SD_BUS_METHOD_WITH_OFFSET(
-        <replaceable>member</replaceable>,
-        <replaceable>signature</replaceable>,
-        <replaceable>result</replaceable>,
-        <replaceable>handler</replaceable>,
-        <replaceable>offset</replaceable>,
-        <replaceable>flags</replaceable>)
-        </constant>
-      </para>
-      <para>
-        <constant>SD_BUS_METHOD(
-        <replaceable>member</replaceable>,
-        <replaceable>signature</replaceable>,
-        <replaceable>result</replaceable>,
-        <replaceable>handler</replaceable>,
-        <replaceable>flags</replaceable>)
-        </constant>
-      </para>
-      <para>
-        <constant>SD_BUS_SIGNAL_WITH_NAMES(
-        <replaceable>member</replaceable>,
-        <replaceable>signature</replaceable>,
-        <replaceable>names</replaceable>,
-        <replaceable>flags</replaceable>)
-        </constant>
-      </para>
-      <para>
-        <constant>SD_BUS_SIGNAL(
-        <replaceable>member</replaceable>,
-        <replaceable>signature</replaceable>,
-        <replaceable>flags</replaceable>)
-        </constant>
-      </para>
-      <para>
-        <constant>SD_BUS_WRITABLE_PROPERTY(
-        <replaceable>member</replaceable>,
-        <replaceable>signature</replaceable>,
-        <replaceable>get</replaceable>,
-        <replaceable>set</replaceable>,
-        <replaceable>offset</replaceable>,
-        <replaceable>flags</replaceable>)
-        </constant>
-      </para>
-      <para>
-        <constant>SD_BUS_PROPERTY(
-        <replaceable>member</replaceable>,
-        <replaceable>signature</replaceable>,
-        <replaceable>get</replaceable>,
-        <replaceable>offset</replaceable>,
-        <replaceable>flags</replaceable>)
-        </constant>
-      </para>
-      <para>
-        <constant>SD_BUS_PARAM(<replaceable>name</replaceable>)</constant>
-      </para>
-    </funcsynopsis>
-  </refsynopsisdiv>
-
-  <refsect1>
-    <title>Description</title>
-
-    <para><function>sd_bus_add_object_vtable()</function> is used to declare attributes for the path object
-    path <parameter>path</parameter> connected to the bus connection <parameter>bus</parameter> under the
-    interface <parameter>interface</parameter>. The table <parameter>vtable</parameter> may contain property
-    declarations using <constant>SD_BUS_PROPERTY()</constant> or
-    <constant>SD_BUS_WRITABLE_PROPERTY()</constant>, method declarations using
-    <constant>SD_BUS_METHOD()</constant>, <constant>SD_BUS_METHOD_WITH_NAMES()</constant>,
-    <constant>SD_BUS_METHOD_WITH_OFFSET()</constant>, or
-    <constant>SD_BUS_METHOD_WITH_NAMES_OFFSET()</constant>, and signal declarations using
-    <constant>SD_BUS_SIGNAL_WITH_NAMES()</constant> or <constant>SD_BUS_SIGNAL()</constant>, see below. The
-    <replaceable>userdata</replaceable> parameter contains a pointer that will be passed to various callback
-    functions. It may be specified as <constant>NULL</constant> if no value is necessary.</para>
-
-    <para><function>sd_bus_add_fallback_vtable()</function> is similar to
-    <function>sd_bus_add_object_vtable()</function>, but is used to register "fallback" attributes. When
-    looking for an attribute declaration, bus object paths registered with
-    <function>sd_bus_add_object_vtable()</function> are checked first. If no match is found, the fallback
-    vtables are checked for each prefix of the bus object path, i.e. with the last slash-separated components
-    successively removed. This allows the vtable to be used for an arbitrary number of dynamically created
-    objects.</para>
-
-    <para>Parameter <replaceable>find</replaceable> is a function which is used to locate the target object
-    based on the bus object path <replaceable>path</replaceable>. It must return <constant>1</constant> and
-    set the <parameter>ret_found</parameter> output parameter if the object is found, return
-    <constant>0</constant> if the object was not found, and return a negative errno-style error code or
-    initialize the error structure <replaceable>ret_error</replaceable> on error. The pointer passed in
-    <parameter>ret_found</parameter> will be used as the <parameter>userdata</parameter> parameter for the
-    callback functions (offset by the <parameter>offset</parameter> offsets as specified in the vtable
-    entries).</para>
-
-    <para>For both functions, a match slot is created internally. If the output parameter
-    <replaceable>slot</replaceable> is <constant>NULL</constant>, a "floating" slot object is created, see
-    <citerefentry><refentrytitle>sd_bus_slot_set_floating</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
-    Otherwise, a pointer to the slot object is returned. In that case, the reference to the slot object
-    should be dropped when the vtable is not needed anymore, see
-    <citerefentry><refentrytitle>sd_bus_slot_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
-    </para>
-
-    <refsect2>
-      <title>The <structname>sd_bus_vtable</structname> array</title>
-
-      <para>The array consists of the structures of type <structname>sd_bus_vtable</structname>, but it
-      should never be filled in manually, but through one of the following macros:</para>
-
-      <variablelist>
-        <varlistentry>
-          <term><constant>SD_BUS_VTABLE_START()</constant></term>
-          <term><constant>SD_BUS_VTABLE_END</constant></term>
-
-          <listitem><para>Those must always be the first and last element.</para></listitem>
-        </varlistentry>
-
-        <varlistentry>
-          <term><constant>SD_BUS_METHOD_WITH_NAMES_OFFSET()</constant></term>
-          <term><constant>SD_BUS_METHOD_WITH_NAMES()</constant></term>
-          <term><constant>SD_BUS_METHOD_WITH_OFFSET()</constant></term>
-          <term><constant>SD_BUS_METHOD()</constant></term>
-
-          <listitem><para>Declare a D-Bus method with the name <replaceable>member</replaceable>, parameter
-          signature <replaceable>signature</replaceable>, result signature <replaceable>result</replaceable>.
-          Parameters <replaceable>in_names</replaceable> and <replaceable>out_names</replaceable> specify the
-          argument names of the input and output arguments in the function signature. The handler function
-          <replaceable>handler</replaceable> must be of type <function>sd_bus_message_handler_t</function>.
-          It will be called to handle the incoming messages that call this method. It receives a pointer that
-          is the <replaceable>userdata</replaceable> parameter passed to the registration function offset by
-          <replaceable>offset</replaceable> bytes. This may be used to pass pointers to different fields in
-          the same data structure to different methods in the same
-          vtable. <replaceable>in_names</replaceable> and <replaceable>out_names</replaceable> should be
-          created using the <constant>SD_BUS_PARAM()</constant> macro, see below. Parameter
-          <replaceable>flags</replaceable> is a combination of flags, see below.</para>
-
-          <para><constant>SD_BUS_METHOD_WITH_NAMES()</constant>,
-          <constant>SD_BUS_METHOD_WITH_OFFSET()</constant>, and <constant>SD_BUS_METHOD()</constant> are
-          variants which specify zero offset (<replaceable>userdata</replaceable> parameter is passed with
-          no change), leave the names unset (i.e. no parameter names), or both.</para>
-          </listitem>
-        </varlistentry>
-
-        <varlistentry>
-          <term><constant>SD_BUS_SIGNAL_WITH_NAMES()</constant></term>
-          <term><constant>SD_BUS_SIGNAL()</constant></term>
-
-          <listitem><para>Declare a D-Bus signal with the name <replaceable>member</replaceable>,
-          parameter signature <replaceable>signature</replaceable>, and argument names
-          <replaceable>names</replaceable>. <replaceable>names</replaceable> should be
-          created using the <constant>SD_BUS_PARAM()</constant> macro, see below.
-          Parameter <replaceable>flags</replaceable> is a combination of flags, see below.
-          </para>
-
-          <para>Equivalent to <constant>SD_BUS_SIGNAL_WITH_NAMES()</constant> with the
-          <replaceable>names</replaceable> parameter unset (i.e. no parameter names).</para>
-          </listitem>
-        </varlistentry>
-
-        <varlistentry>
-          <term><constant>SD_BUS_WRITABLE_PROPERTY()</constant></term>
-          <term><constant>SD_BUS_PROPERTY()</constant></term>
-
-          <listitem><para>Declare a D-Bus property with the name <replaceable>member</replaceable> and value
-          signature <replaceable>signature</replaceable>. Parameters <replaceable>get</replaceable> and
-          <replaceable>set</replaceable> are the getter and setter methods. They are called with a pointer
-          that is the <replaceable>userdata</replaceable> parameter passed to the registration function
-          offset by <replaceable>offset</replaceable> bytes. This may be used pass pointers to different
-          fields in the same data structure to different setters and getters in the same vtable. Parameter
-          <replaceable>flags</replaceable> is a combination of flags, see below.</para>
-
-          <para>The setter and getter methods may be omitted (specified as <constant>NULL</constant>), if the
-          property has one of the basic types or <literal>as</literal> in case of read-only properties. In
-          those cases, the <replaceable>userdata</replaceable> and <replaceable>offset</replaceable>
-          parameters must together point to valid variable of the corresponding type. A default setter and
-          getters will be provided, which simply copy the argument between this variable and the message.
-          </para>
-
-          <para><constant>SD_BUS_PROPERTY()</constant> is used to define a read-only property.</para>
-          </listitem>
-        </varlistentry>
-
-        <varlistentry>
-          <term><constant>SD_BUS_PARAM()</constant></term>
-          <listitem><para>Parameter names should be wrapped in this macro, see the example below.</para>
-          </listitem>
-        </varlistentry>
-      </variablelist>
-    </refsect2>
-
-    <refsect2>
-      <title>Flags</title>
-
-      <para>The <replaceable>flags</replaceable> parameter is used to specify a combination of
-      <ulink url="https://dbus.freedesktop.org/doc/dbus-specification.html#introspection-format">D-Bus annotations</ulink>.
-      </para>
-
-      <variablelist>
-        <varlistentry>
-          <term><constant>SD_BUS_VTABLE_DEPRECATED</constant></term>
-
-          <listitem><para>Mark this vtable entry as deprecated using the
-          <constant>org.freedesktop.DBus.Deprecated</constant> annotation in introspection data.  If
-          specified for <constant>SD_BUS_VTABLE_START()</constant>, the annotation is applied to the
-          enclosing interface.</para></listitem>
-        </varlistentry>
-
-        <varlistentry>
-          <term><constant>SD_BUS_VTABLE_HIDDEN</constant></term>
-
-          <listitem><para>Make this vtable entry hidden. It will not be shown in introspection data.  If
-          specified for <constant>SD_BUS_VTABLE_START()</constant>, all entries in the array are hidden.
-          </para>
-          </listitem>
-        </varlistentry>
-
-        <varlistentry>
-          <term><constant>SD_BUS_VTABLE_UNPRIVILEGED</constant></term>
-
-          <listitem><para>Mark this vtable entry as unprivileged. If not specified, the
-          <constant>org.freedesktop.systemd1.Privileged</constant> annotation with value
-          <literal>true</literal> will be shown in introspection data.</para>
-          </listitem>
-        </varlistentry>
-
-        <varlistentry>
-          <term><constant>SD_BUS_VTABLE_METHOD_NO_REPLY</constant></term>
-
-          <listitem><para>Mark his vtable entry as a method that will not return a reply using the
-          <constant>org.freedesktop.DBus.Method.NoReply</constant> annotation in introspection data.
-          </para></listitem>
-        </varlistentry>
-
-        <varlistentry>
-          <term><constant>SD_BUS_VTABLE_PROPERTY_CONST</constant></term>
-          <term><constant>SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE</constant></term>
-          <term><constant>SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION</constant></term>
-
-          <listitem><para>Those three flags correspond to different values of the
-          <constant>org.freedesktop.DBus.Property.EmitsChangedSignal</constant> annotation, which specifies
-          whether the <constant>org.freedesktop.DBus.Properties.PropertiesChanged</constant> signal is
-          emitted whenever the property changes. <constant>SD_BUS_VTABLE_PROPERTY_CONST</constant> corresponds to
-          <constant>const</constant> and means that the property never changes during the lifetime of the
-          object it belongs to, so no signal needs to be emitted.
-          <constant>SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE</constant> corresponds to <constant>true</constant> and means
-          that the signal is emitted. <constant>SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION</constant> corresponds to
-          <constant>invalidates</constant> and means that the signal is emitted, but the value is not included
-          in the signal.</para>
-          </listitem>
-        </varlistentry>
-
-        <varlistentry>
-          <term><constant>SD_BUS_VTABLE_PROPERTY_EXPLICIT</constant></term>
-
-          <listitem><para>Mark this vtable property entry as requiring explicit request to for the value to
-          be shown (generally because the value is large or slow to calculate). This entry cannot be combined
-          with <constant>SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE</constant>, and will not be shown in property listings by
-          default (e.g. <command>busctl introspect</command>).  This corresponds to the
-          <constant>org.freedesktop.systemd1.Explicit</constant> annotation in introspection data.</para>
-          </listitem>
-        </varlistentry>
-      </variablelist>
-    </refsect2>
-  </refsect1>
-
-  <refsect1>
-    <title>Examples</title>
-
-    <example>
-      <title>Create a simple listener on the bus</title>
-
-      <programlisting><xi:include href="vtable-example.c" parse="text" /></programlisting>
-
-      <para>This creates a simple client on the bus (the user bus, when run as normal user).
-      We may use the D-Bus <constant>org.freedesktop.DBus.Introspectable.Introspect</constant>
-      call to acquire the XML description of the interface:</para>
-
-      <programlisting><xi:include href="vtable-example.xml" parse="text" /></programlisting>
-    </example>
-  </refsect1>
-
-  <refsect1>
-    <title>Return Value</title>
-
-    <para>On success, <function>sd_bus_add_object_vtable</function> and
-    <function>sd_bus_add_fallback_vtable</function> calls return 0 or a positive integer. On failure, they
-    return a negative errno-style error code.</para>
-
-    <refsect2>
-      <title>Errors</title>
-
-      <para>Returned errors may indicate the following problems:</para>
-
-      <variablelist>
-        <varlistentry>
-          <term><constant>-EINVAL</constant></term>
-
-          <listitem><para>One of the required parameters is <constant>NULL</constant> or invalid. A reserved
-          D-Bus interface was passed as the <replaceable>interface</replaceable> parameter.</para></listitem>
-        </varlistentry>
-
-        <varlistentry>
-          <term><constant>-ENOPKG</constant></term>
-
-          <listitem><para>The bus cannot be resolved.</para></listitem>
-        </varlistentry>
-
-        <varlistentry>
-          <term><constant>-ECHILD</constant></term>
-
-          <listitem><para>The bus was created in a different process.</para></listitem>
-        </varlistentry>
-
-        <varlistentry>
-          <term><constant>-ENOMEM</constant></term>
-
-          <listitem><para>Memory allocation failed.</para></listitem>
-        </varlistentry>
-
-        <varlistentry>
-          <term><constant>-EPROTOTYPE</constant></term>
-
-          <listitem><para><function>sd_bus_add_object_vtable</function> and
-          <function>sd_bus_add_fallback_vtable</function> have been both called
-          for the same bus object path, which is not allowed.</para></listitem>
-        </varlistentry>
-
-        <varlistentry>
-          <term><constant>-EEXIST</constant></term>
-
-          <listitem><para>This vtable has already been registered for this
-          <replaceable>interface</replaceable> and <replaceable>path</replaceable>.
-          </para></listitem>
-        </varlistentry>
-      </variablelist>
-    </refsect2>
-  </refsect1>
-
-  <xi:include href="libsystemd-pkgconfig.xml" />
-
-  <refsect1>
-    <title>See Also</title>
-
-    <para>
-      <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>busctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
-    </para>
-  </refsect1>
-</refentry>
diff --git a/man/sd_bus_call.xml b/man/sd_bus_call.xml
new file mode 100644 (file)
index 0000000..f47f9c8
--- /dev/null
@@ -0,0 +1,187 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_bus_call"
+          xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>sd_bus_call</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_bus_call</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_bus_call</refname>
+    <refname>sd_bus_call_async</refname>
+
+    <refpurpose>Invoke a D-Bus method call</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+      <xi:include href="sd_bus_add_match.xml" xpointer="sd_bus_message_handler_t"/>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_call</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
+        <paramdef>uint64_t <parameter>usec</parameter></paramdef>
+        <paramdef>sd_bus_error *<parameter>ret_error</parameter></paramdef>
+        <paramdef>sd_bus_message **<parameter>reply</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_call_async</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>sd_bus_slot **<parameter>slot</parameter></paramdef>
+        <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
+        <paramdef>sd_bus_message_handler_t <parameter>callback</parameter></paramdef>
+        <paramdef>void *<parameter>userdata</parameter></paramdef>
+        <paramdef>uint64_t <parameter>usec</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>sd_bus_call()</function> takes a complete bus message object and calls the
+    corresponding D-Bus method. On success, the response is stored in <parameter>reply</parameter>.
+    <parameter>usec</parameter> indicates the timeout in microseconds. If
+    <parameter>ret_error</parameter> is not <constant>NULL</constant> and
+    <function>sd_bus_call()</function> fails (either because of an internal error or because it
+    received a D-Bus error reply), <parameter>ret_error</parameter> is initialized to an instance of
+    <structname>sd_bus_error</structname> describing the error.</para>
+
+    <para><function>sd_bus_call_async()</function> is like <function>sd_bus_call()</function> but works
+    asynchronously. The <parameter>callback</parameter> indicates the function to call when the response
+    arrives. The <parameter>userdata</parameter> pointer will be passed to the callback function, and may be
+    chosen freely by the caller. If <parameter>slot</parameter> is not <constant>NULL</constant> and
+    <function>sd_bus_call_async()</function> succeeds, <parameter>slot</parameter> is set to a slot object
+    which can be used to cancel the method call at a later time using
+    <citerefentry><refentrytitle>sd_bus_slot_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+    If <parameter>slot</parameter> is <constant>NULL</constant>, the lifetime of the method call is bound to
+    the lifetime of the bus object itself, and it cannot be cancelled independently. See
+    <citerefentry><refentrytitle>sd_bus_slot_set_floating</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    for details. <parameter>callback</parameter> is called when a reply arrives with the reply,
+    <parameter>userdata</parameter> and an <structname>sd_bus_error</structname> output parameter as its
+    arguments. Unlike <function>sd_bus_call()</function>, the <structname>sd_bus_error</structname> output
+    parameter passed to the callback will be empty. To determine whether the method call succeeded, use
+    <citerefentry><refentrytitle>sd_bus_message_is_method_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    on the reply message passed to the callback instead. If the callback returns zero and the
+    <structname>sd_bus_error</structname> output parameter is still empty when the callback finishes, other
+    handlers registered with functions such as
+    <citerefentry><refentrytitle>sd_bus_add_filter</refentrytitle><manvolnum>3</manvolnum></citerefentry> or
+    <citerefentry><refentrytitle>sd_bus_add_match</refentrytitle><manvolnum>3</manvolnum></citerefentry> are
+    given a chance to process the message. If the callback returns a non-zero value or the
+    <structname>sd_bus_error</structname> output parameter is not empty when the callback finishes, no
+    further processing of the message is done. Generally, you want to return zero from the callback to give
+    other registered handlers a chance to process the reply as well. (Note that the
+    <structname>sd_bus_error</structname> parameter is an output parameter of the callback function, not an
+    input parameter; it can be used to propagate errors from the callback handler, it will not receive any
+    error that was received as method reply.)</para>
+
+    <para>If <parameter>usec</parameter> is zero, the default D-Bus method call timeout is used. See
+    <citerefentry><refentrytitle>sd_bus_get_method_call_timeout</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+    </para>
+
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On success, these functions return a non-negative integer. On failure, they return a
+    negative errno-style error code.</para>
+
+    <refsect2 id='errors'>
+      <title>Errors</title>
+
+      <para>Returned errors may indicate the following problems:</para>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>-EINVAL</constant></term>
+
+          <listitem><para>The input parameter <parameter>m</parameter> is <constant>NULL</constant>.
+          </para></listitem>
+
+          <listitem><para>The input parameter <parameter>m</parameter> is not a D-Bus method call.
+          To create a new D-Bus method call, use
+          <citerefentry><refentrytitle>sd_bus_message_new_method_call</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+          </para></listitem>
+
+          <listitem><para>The input parameter <parameter>m</parameter> has the
+          <constant>BUS_MESSAGE_NO_REPLY_EXPECTED</constant> flag set.</para></listitem>
+
+          <listitem><para>The input parameter <parameter>error</parameter> is
+          non-<constant>NULL</constant> but was not set to <constant>SD_BUS_ERROR_NULL</constant>.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ECHILD</constant></term>
+
+          <listitem><para>The bus connection was allocated in a parent process and is being reused
+          in a child process after <function>fork()</function>.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENOTCONN</constant></term>
+
+          <listitem><para>The input parameter <parameter>bus</parameter> is
+          <constant>NULL</constant> or the bus is not connected.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ECONNRESET</constant></term>
+
+          <listitem><para>The bus connection was closed while waiting for the response.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ETIMEDOUT</constant></term>
+
+          <listitem><para>A response was not received within the given timeout.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ELOOP</constant></term>
+
+          <listitem><para>The message <parameter>m</parameter> is addressed to its own client.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENOMEM</constant></term>
+
+          <listitem><para>Memory allocation failed.</para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_call_method</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_call_method_async</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_message_new_method_call</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_message_append</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
diff --git a/man/sd_bus_call_method.xml b/man/sd_bus_call_method.xml
new file mode 100644 (file)
index 0000000..ac9cf14
--- /dev/null
@@ -0,0 +1,143 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_bus_call_method"
+          xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>sd_bus_call_method</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_bus_call_method</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_bus_call_method</refname>
+    <refname>sd_bus_call_methodv</refname>
+    <refname>sd_bus_call_method_async</refname>
+    <refname>sd_bus_call_method_asyncv</refname>
+
+    <refpurpose>Initialize a bus message object and invoke the corresponding D-Bus method call
+    </refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+      <xi:include href="sd_bus_add_match.xml" xpointer="sd_bus_message_handler_t"/>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_call_method</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>const char *<parameter>destination</parameter></paramdef>
+        <paramdef>const char *<parameter>path</parameter></paramdef>
+        <paramdef>const char *<parameter>interface</parameter></paramdef>
+        <paramdef>const char *<parameter>member</parameter></paramdef>
+        <paramdef>sd_bus_error *<parameter>ret_error</parameter></paramdef>
+        <paramdef>sd_bus_message **<parameter>reply</parameter></paramdef>
+        <paramdef>const char *<parameter>types</parameter></paramdef>
+        <paramdef>...</paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_call_methodv</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>const char *<parameter>destination</parameter></paramdef>
+        <paramdef>const char *<parameter>path</parameter></paramdef>
+        <paramdef>const char *<parameter>interface</parameter></paramdef>
+        <paramdef>const char *<parameter>member</parameter></paramdef>
+        <paramdef>sd_bus_error *<parameter>ret_error</parameter></paramdef>
+        <paramdef>sd_bus_message **<parameter>reply</parameter></paramdef>
+        <paramdef>const char *<parameter>types</parameter></paramdef>
+        <paramdef>va_list <parameter>ap</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_call_method_async</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>sd_bus_slot **<parameter>slot</parameter></paramdef>
+        <paramdef>const char *<parameter>destination</parameter></paramdef>
+        <paramdef>const char *<parameter>path</parameter></paramdef>
+        <paramdef>const char *<parameter>interface</parameter></paramdef>
+        <paramdef>const char *<parameter>member</parameter></paramdef>
+        <paramdef>sd_bus_message_handler_t <parameter>callback</parameter></paramdef>
+        <paramdef>void *<parameter>userdata</parameter></paramdef>
+        <paramdef>const char *<parameter>types</parameter></paramdef>
+        <paramdef>...</paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_call_method_asyncv</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>sd_bus_slot **<parameter>slot</parameter></paramdef>
+        <paramdef>const char *<parameter>destination</parameter></paramdef>
+        <paramdef>const char *<parameter>path</parameter></paramdef>
+        <paramdef>const char *<parameter>interface</parameter></paramdef>
+        <paramdef>const char *<parameter>member</parameter></paramdef>
+        <paramdef>sd_bus_message_handler_t <parameter>callback</parameter></paramdef>
+        <paramdef>void *<parameter>userdata</parameter></paramdef>
+        <paramdef>const char *<parameter>types</parameter></paramdef>
+        <paramdef>va_list <parameter>ap</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>sd_bus_call_method()</function> is a convenience function for initializing a
+    bus message object and calling the corresponding D-Bus method. It combines the
+    <citerefentry><refentrytitle>sd_bus_message_new_method_call</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_bus_message_append</refentrytitle><manvolnum>3</manvolnum></citerefentry> and
+    <citerefentry><refentrytitle>sd_bus_call</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    functions into a single function call.</para>
+
+    <para><function>sd_bus_call_method_async()</function> is a convenience function for initializing
+    a bus message object and calling the corresponding D-Bus method asynchronously. It combines the
+    <citerefentry><refentrytitle>sd_bus_message_new_method_call</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_bus_message_append</refentrytitle><manvolnum>3</manvolnum></citerefentry> and
+    <citerefentry><refentrytitle>sd_bus_call_async</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    functions into a single function call.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On success, these functions return a non-negative integer. On failure, they return a
+    negative errno-style error code.</para>
+
+    <refsect2 id='errors'>
+      <title>Errors</title>
+
+      <para>See the man pages of
+      <citerefentry><refentrytitle>sd_bus_message_new_method_call</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_message_append</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_call</refentrytitle><manvolnum>3</manvolnum></citerefentry> and
+      <citerefentry><refentrytitle>sd_bus_call_async</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+      for a list of possible errors.</para>
+    </refsect2>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_message_new_method_call</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_message_append</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_call</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_set_property</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_emit_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
diff --git a/man/sd_bus_can_send.xml b/man/sd_bus_can_send.xml
new file mode 100644 (file)
index 0000000..ba2a180
--- /dev/null
@@ -0,0 +1,93 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_bus_can_send"
+          xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>sd_bus_can_send</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_bus_can_send</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_bus_can_send</refname>
+
+    <refpurpose>Check which types can be sent over a bus object</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>void <function>sd_bus_can_send</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>char <parameter>type</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>sd_bus_can_send</function> is mostly used for checking if file descriptor
+    passing is available on the given bus. <parameter>type</parameter> can be any of the
+    <constant>SD_BUS_TYPE</constant> constants.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On failure, <function>sd_bus_can_send()</function> returns a negative errno-style error
+    code. If values of the given type can be sent over the given bus, it returns a positive integer.
+    Otherwise, it returns zero.</para>
+
+    <refsect2>
+      <title>Errors</title>
+
+      <para>Returned errors may indicate the following problems:</para>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>-ENOPKG</constant></term>
+
+          <listitem><para>The bus object <parameter>bus</parameter> could not be resolved.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENOTCONN</constant></term>
+
+          <listitem><para>The input parameter <parameter>bus</parameter> is
+          <constant>NULL</constant> or the bus is not connected.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ECHILD</constant></term>
+
+          <listitem><para>The bus object <parameter>bus</parameter> was created in a different
+          process.</para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
index d81c593878d8143841fe4f01356ba421c7f4d945..42db1074700de4ea82b1f6d4a495ffb6549e6dd7 100644 (file)
@@ -19,6 +19,7 @@
   <refnamediv>
     <refname>sd_bus_close</refname>
     <refname>sd_bus_flush</refname>
+    <refname>sd_bus_default_flush_close</refname>
 
     <refpurpose>Close and flush a bus connection</refpurpose>
   </refnamediv>
         <funcdef>int <function>sd_bus_flush</function></funcdef>
         <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
       </funcprototype>
+
+      <funcprototype>
+        <funcdef>void <function>sd_bus_default_flush_close</function></funcdef>
+        <paramdef>void</paramdef>
+      </funcprototype>
     </funcsynopsis>
   </refsynopsisdiv>
 
   <refsect1>
     <title>Description</title>
 
-    <para><function>sd_bus_close()</function> disconnects the specified bus connection. When this call is invoked and
-    the specified bus object refers to an active connection it is immediately terminated. No further messages may be
-    sent or received on it. Any messages queued in the bus object (both incoming and outgoing) are released. If
-    invoked on <constant>NULL</constant> bus object or when the bus connection is already closed this function executes
-    no operation. This call does not free or unreference the bus object itself. Use
-    <citerefentry><refentrytitle>sd_bus_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry> for that.</para>
+    <para><function>sd_bus_close()</function> disconnects the specified bus connection. When this
+    call is invoked and the specified bus object refers to an active connection it is immediately
+    terminated. No further messages may be sent or received on it. Any messages queued in the bus
+    object (both incoming and outgoing) are released. If invoked on <constant>NULL</constant> bus
+    object or when the bus connection is already closed this function executes no operation. This
+    call does not free or unreference the bus object itself. Use
+    <citerefentry><refentrytitle>sd_bus_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    for that.</para>
 
-    <para><function>sd_bus_flush()</function> synchronously writes out all outgoing queued message on a bus connection
-    if there are any. This function call may block if the peer is not processing bus messages quickly.</para>
+    <para><function>sd_bus_flush()</function> synchronously writes out all outgoing queued message
+    on a bus connection if there are any. This function call may block if the peer is not processing
+    bus messages quickly.</para>
 
     <para>Before a program exits it is usually a good idea to flush any pending messages with
-    <function>sd_bus_flush()</function> and then close connections with <function>sd_bus_close()</function> to ensure
-    that no unwritten messages are lost, no further messages may be queued and all incoming but unprocessed messages
-    are released. After both operations have been done, it is a good idea to also drop any remaining references to the
-    bus object so that it may be freed. Since these three operations are frequently done together a helper call
-    <citerefentry><refentrytitle>sd_bus_flush_close_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry> is
-    provided that combines them into one.</para>
+    <function>sd_bus_flush()</function> and then close connections with
+    <function>sd_bus_close()</function> to ensure that no unwritten messages are lost, no further
+    messages may be queued and all incoming but unprocessed messages are released. After both
+    operations have been done, it is a good idea to  also drop any remaining references to the bus
+    object so that it may be freed. Since these three operations are frequently done together a
+    helper call
+    <citerefentry><refentrytitle>sd_bus_flush_close_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    is provided that combines them into one.</para>
+
+    <para><function>sd_bus_default_flush_close()</function> is similar to
+    <function>sd_bus_flush_close_unref</function>, but does not take a bus pointer argument and
+    instead iterates over any of the "default" buses opened by
+    <citerefentry><refentrytitle>sd_bus_default</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_bus_default_user</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_bus_default_system</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    and similar calls. <function>sd_bus_default_flush_close()</function> is particularly useful to
+    clean up any buses opened using those calls before the program exits.</para>
   </refsect1>
 
   <refsect1>
     <title>Return Value</title>
 
-    <para>On success, <function>sd_bus_flush()</function> returns 0 or a positive integer. On failure, it returns a
-    negative errno-style error code.</para>
+    <para>On success, <function>sd_bus_flush()</function> returns a non-negative integer. On
+    failure, it returns a negative errno-style error code.</para>
 
     <refsect2>
       <title>Errors</title>
@@ -76,7 +96,8 @@
         <varlistentry>
           <term><constant>-ECHILD</constant></term>
 
-          <listitem><para>The bus connection has been created in a different process.</para></listitem>
+          <listitem><para>The bus connection has been created in a different process.</para>
+          </listitem>
         </varlistentry>
       </variablelist>
     </refsect2>
index a7690d5817443da6c6b169d9123c18476e5105ac..a3e8079c5187a930675a1ff3722c3bcb4aeed770 100644 (file)
     information, in particular it should not be used for security-relevant decisions. That's because the
     executable might have been replaced or removed by the time the value can be processed. Moreover, the
     kernel exports this information in an ambiguous way (i.e. a deleted executable cannot be safely
-    distinguished from one whose name suffix is <literal> (deleted)</literal>.</para>
+    distinguished from one whose name suffix is <literal> (deleted)</literal>).</para>
 
     <para><function>sd_bus_creds_get_cmdline()</function> will
     retrieve an array of command line arguments (as stored in
index 51c27f04fffb195ff5ddd574a1b1037b7317c277..8532c2bf466c85c8c915434172146ce067b6af58 100644 (file)
     <para>Note that entering a container is a privileged operation, and will likely only
     work for the root user on the remote machine.</para>
 
-    <para><function>sd_bus_open_system_machine()</function> connects
-    to the system bus in the specified <parameter>machine</parameter>,
-    where <parameter>machine</parameter> is the name of a local
-    container.  See
-    <citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
-    for more information about the "machine" concept. Note that
-    connections into local containers are only available to privileged
-    processes at this time.</para>
+    <para><function>sd_bus_open_system_machine()</function> connects to the system bus in the specified
+    <parameter>machine</parameter>, where <parameter>machine</parameter> is the name of a local
+    container. See
+    <citerefentry><refentrytitle>sd_bus_set_address</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    for a description of the address syntax, and
+    <citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry> for more
+    information about the "machine" concept. Note that connections into local containers are only available
+    to privileged processes at this time.</para>
 
     <para>These calls allocate a bus connection object and initiate
     the connection to a well-known bus of some form. An alternative to
         </varlistentry>
       </variablelist>
 
-      <para>In addition, any further connection-related errors may be by returned. See
+      <para>In addition, other connection-related errors may be returned. See
       <citerefentry><refentrytitle>sd_bus_send</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
     </refsect2>
   </refsect1>
       <citerefentry><refentrytitle>sd_bus_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_bus_ref</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_bus_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_close</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry project='die-net'><refentrytitle>ssh</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd-machined.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
diff --git a/man/sd_bus_emit_signal.xml b/man/sd_bus_emit_signal.xml
new file mode 100644 (file)
index 0000000..26ec7d1
--- /dev/null
@@ -0,0 +1,243 @@
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_bus_emit_signal"
+          xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>sd_bus_emit_signal</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_bus_emit_signal</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_bus_emit_signal</refname>
+    <refname>sd_bus_emit_signalv</refname>
+    <refname>sd_bus_emit_interfaces_added</refname>
+    <refname>sd_bus_emit_interfaces_added_strv</refname>
+    <refname>sd_bus_emit_interfaces_removed</refname>
+    <refname>sd_bus_emit_interfaces_removed_strv</refname>
+    <refname>sd_bus_emit_properties_changed</refname>
+    <refname>sd_bus_emit_properties_changed_strv</refname>
+    <refname>sd_bus_emit_object_added</refname>
+    <refname>sd_bus_emit_object_removed</refname>
+
+    <refpurpose>Convenience functions for emitting (standard) D-Bus signals</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-bus-vtable.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_emit_signal</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>const char *<parameter>path</parameter></paramdef>
+        <paramdef>const char *<parameter>interface</parameter></paramdef>
+        <paramdef>const char *<parameter>member</parameter></paramdef>
+        <paramdef>const char *<parameter>types</parameter></paramdef>
+        <paramdef>...</paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_emit_signalv</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>const char *<parameter>path</parameter></paramdef>
+        <paramdef>const char *<parameter>interface</parameter></paramdef>
+        <paramdef>const char *<parameter>member</parameter></paramdef>
+        <paramdef>const char *<parameter>types</parameter></paramdef>
+        <paramdef>va_list <parameter>ap</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_emit_interfaces_added</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>const char *<parameter>path</parameter></paramdef>
+        <paramdef>const char *<parameter>interface</parameter></paramdef>
+        <paramdef>...</paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_emit_interfaces_added_strv</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>const char *<parameter>path</parameter></paramdef>
+        <paramdef>const char **<parameter>interfaces</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_emit_interfaces_removed</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>const char *<parameter>path</parameter></paramdef>
+        <paramdef>const char *<parameter>interface</parameter></paramdef>
+        <paramdef>...</paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_emit_interfaces_removed_strv</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>const char *<parameter>path</parameter></paramdef>
+        <paramdef>const char **<parameter>interfaces</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_emit_properties_changed</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>const char *<parameter>path</parameter></paramdef>
+        <paramdef>const char *<parameter>interface</parameter></paramdef>
+        <paramdef>const char *<parameter>name</parameter></paramdef>
+        <paramdef>...</paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_emit_properties_changed_strv</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>const char *<parameter>path</parameter></paramdef>
+        <paramdef>const char *<parameter>interface</parameter></paramdef>
+        <paramdef>const char **<parameter>names</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_emit_object_added</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>const char *<parameter>path</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_emit_object_removed</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>const char *<parameter>path</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>sd_bus_emit_signal()</function> is a convenience function for initializing a
+    bus message object and emitting the corresponding D-Bus signal. It combines the
+    <citerefentry><refentrytitle>sd_bus_message_new_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_bus_message_append</refentrytitle><manvolnum>3</manvolnum></citerefentry> and
+    <citerefentry><refentrytitle>sd_bus_send</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    functions into a single function call. <function>sd_bus_emit_signalv()</function> is
+    equivalent to <function>sd_bus_message_append()</function>, except that it is called with a
+    <literal>va_list</literal> instead of a variable number of arguments.</para>
+
+    <para><function>sd_bus_emit_interfaces_added()</function> and
+    <function>sd_bus_emit_interfaces_removed()</function> are used to implement the
+    <function>InterfacesAdded</function> and <function>InterfacesRemoved</function> signals of the
+    <constant>org.freedesktop.DBus.ObjectManager</constant> interface. They take a path whose
+    interfaces have been modified as an argument and a variable list of interfaces that have been
+    added or removed, respectively. The final argument passed to
+    <function>sd_bus_emit_interfaces_added()</function> and
+    <function>sd_bus_emit_interfaces_removed()</function> <emphasis>must</emphasis> be
+    <constant>NULL</constant>. This allows both functions to safely determine the number of passed
+    interface arguments. <function>sd_bus_emit_interfaces_added_strv()</function> and
+    <function>sd_bus_emit_interfaces_removed_strv()</function> are identical to their respective
+    counterparts but both take the list of interfaces as a single argument instead of a variable
+    number of arguments.</para>
+
+    <para><function>sd_bus_emit_properties_changed()</function> is used to implement the
+    <function>PropertiesChanged</function> signal of the
+    <constant>org.freedesktop.DBus.Properties</constant> interface. It takes an object path,
+    interface and a variable list of property names as its arguments.  The final argument passed to
+    <function>sd_bus_emit_properties_changed()</function> <emphasis>must</emphasis> be
+    <constant>NULL</constant>. This allows it to safely determine the number of passed property
+    names. <function>sd_bus_emit_properties_changed_strv()</function> is identical to
+    <function>sd_bus_emit_properties_changed()</function> but takes the list of property names as a
+    single argument instead of a variable number of arguments.</para>
+
+    <para><function>sd_bus_emit_object_added()</function> and
+    <function>sd_bus_emit_object_removed()</function> are convenience functions for emitting the
+    <function>InterfacesAdded</function> or <function>InterfacesRemoved</function> signals for all
+    interfaces registered on a specific object path, respectively. This includes any parent fallback
+    vtables if they are not overridden by a more applicable child vtable. It also includes all the
+    standard D-Bus interfaces implemented by sd-bus itself on any registered object.</para>
+
+    <para>Note that <function>sd_bus_emit_interfaces_added()</function>,
+    <function>sd_bus_emit_interfaces_removed()</function>,
+    <function>sd_bus_emit_object_added()</function> and
+    <function>sd_bus_emit_object_removed()</function> require an object manager to have been
+    registered on the given object path or one of its parent object paths using
+    <citerefentry><refentrytitle>sd_bus_add_object_manager</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On success, these functions return a non-negative integer. On failure, they return a
+    negative errno-style error code.</para>
+
+    <refsect2>
+      <title>Errors</title>
+
+      <para>Returned errors may indicate the following problems:</para>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>-EINVAL</constant></term>
+
+          <listitem><para>One of the required parameters is <constant>NULL</constant> or invalid. A
+          reserved D-Bus interface was passed as the <replaceable>interface</replaceable> parameter.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENOPKG</constant></term>
+
+          <listitem><para>The bus cannot be resolved.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ECHILD</constant></term>
+
+          <listitem><para>The bus was created in a different process.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENOMEM</constant></term>
+
+          <listitem><para>Memory allocation failed.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ESRCH</constant></term>
+
+          <listitem><para>One of <function>sd_bus_emit_interfaces_added()</function>,
+          <function>sd_bus_emit_interfaces_removed()</function>,
+          <function>sd_bus_emit_object_added()</function> or
+          <function>sd_bus_emit_object_removed()</function> was called on an object without an
+          object manager registered on its own object path or one of its parent object paths.
+          </para></listitem>
+        </varlistentry>
+      </variablelist>
+
+      <para>See the man pages of
+      <citerefentry><refentrytitle>sd_bus_message_new_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_message_append</refentrytitle><manvolnum>3</manvolnum></citerefentry> and
+      <citerefentry><refentrytitle>sd_bus_send</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+      for more possible errors.</para>
+    </refsect2>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>busctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_message_new_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_message_append</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_send</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_call_method</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+</refentry>
index 3318a3031bc660caec63466374561a0ab619194b..f948b5914f54bcb42a3377b34be07729dfec633a 100644 (file)
@@ -19,7 +19,7 @@
   <refnamediv>
     <refname>sd_bus_enqueue_for_read</refname>
 
-    <refpurpose>Re-enqueue a bus message on a bus connection, for reading.</refpurpose>
+    <refpurpose>Re-enqueue a bus message on a bus connection, for reading</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
diff --git a/man/sd_bus_get_current_handler.xml b/man/sd_bus_get_current_handler.xml
new file mode 100644 (file)
index 0000000..0a1843a
--- /dev/null
@@ -0,0 +1,86 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_bus_get_current_handler" xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>sd_bus_get_current_handler</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_bus_get_current_handler</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_bus_get_current_handler</refname>
+    <refname>sd_bus_get_current_message</refname>
+    <refname>sd_bus_get_current_slot</refname>
+    <refname>sd_bus_get_current_userdata</refname>
+
+    <refpurpose>Query information of the callback a bus object is currently running</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+      <xi:include href="sd_bus_add_match.xml" xpointer="sd_bus_message_handler_t"/>
+
+      <funcprototype>
+        <funcdef>sd_bus_message_handler_t <function>sd_bus_get_current_handler</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>sd_bus_message* <function>sd_bus_get_current_message</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>sd_bus_slot* <function>sd_bus_get_current_slot</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>void* <function>sd_bus_get_current_userdata</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>Whenever sd-bus is about to invoke a user-supplied callback function, it stores the
+    current callback, D-Bus message, slot and userdata pointer and allows these to be queried via
+    <function>sd_bus_get_current_handler()</function>,
+    <function>sd_bus_get_current_message()</function>,
+    <function>sd_bus_get_current_slot()</function> and
+    <function>sd_bus_get_current_userdata()</function>, respectively. If <parameter>bus</parameter>
+    cannot be resolved or if execution does not reside in a user-supplied callback of
+    <parameter>bus</parameter>, these functions return <constant>NULL</constant>.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On success, these functions return the requested object. On failure, they return
+    <constant>NULL</constant>.</para>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
index 6a022b1a19053f91239b21418b6f9854a63ec869..466606118bd465e53875d7fac7df8c47cee1f761 100644 (file)
 
   <refnamediv>
     <refname>sd_bus_get_fd</refname>
+    <refname>sd_bus_set_fd</refname>
     <refname>sd_bus_get_events</refname>
     <refname>sd_bus_get_timeout</refname>
 
-    <refpurpose>Get the file descriptor, I/O events and time-out to wait for from a message bus object</refpurpose>
+    <refpurpose>Get the file descriptor, I/O events and timeout to wait for from a message bus
+    object</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
         <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
       </funcprototype>
 
+      <funcprototype>
+        <funcdef>int <function>sd_bus_set_fd</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>int <parameter>input_fd</parameter></paramdef>
+        <paramdef>int <parameter>output_fd</parameter></paramdef>
+      </funcprototype>
+
       <funcprototype>
         <funcdef>int <function>sd_bus_get_events</function></funcdef>
         <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
   <refsect1>
     <title>Description</title>
 
-    <para><function>sd_bus_get_fd()</function> returns the file descriptor used to communicate from a message bus
-    object. This descriptor can be used with <citerefentry
-    project='man-pages'><refentrytitle>poll</refentrytitle><manvolnum>3</manvolnum></citerefentry> or a similar
-    function to wait for I/O events on the specified bus connection object. If the bus object was configured with the
-    <citerefentry><refentrytitle>sd_bus_set_fd</refentrytitle><manvolnum>3</manvolnum></citerefentry> function, then
-    the <parameter>input_fd</parameter> file descriptor used in that call is returned.</para>
-
-    <para><function>sd_bus_get_events()</function> returns the I/O events to wait for, suitable for passing to
-    <function>poll()</function> or a similar call. Returns a combination of <constant>POLLIN</constant>,
-    <constant>POLLOUT</constant>, … events, or negative on error.</para>
-
-    <para><function>sd_bus_get_timeout()</function> returns the time-out in µs to pass to to
-    <function>poll()</function> or a similar call when waiting for events on the specified bus connection. The returned
-    time-out may be zero, in which case a subsequent I/O polling call should be invoked in non-blocking mode. The
-    returned timeout may be <constant>UINT64_MAX</constant> in which case the I/O polling call may block indefinitely,
-    without any applied time-out. Note that the returned time-out should be considered only a maximum sleeping time. It
-    is permissible (and even expected) that shorter time-outs are used by the calling program, in case other event
-    sources are polled in the same event loop. Note that the returned time-value is relative and specified in
-    microseconds. When converting this value in order to pass it as third argument to <function>poll()</function>
-    (which expects milliseconds), care should be taken to use a division that rounds up to ensure the I/O polling
-    operation doesn't sleep for shorter than necessary, which might result in unintended busy looping (alternatively,
-    use <citerefentry project='man-pages'><refentrytitle>ppoll</refentrytitle><manvolnum>3</manvolnum></citerefentry>
-    instead of plain <function>poll()</function>, which understands time-outs with nano-second granularity).</para>
-
-    <para>These three functions are useful to hook up a bus connection object with an external or manual event loop
-    involving <function>poll()</function> or a similar I/O polling call. Before each invocation of the I/O polling
-    call, all three functions should be invoked: the file descriptor returned by <function>sd_bus_get_fd()</function>
-    should be polled for the events indicated by <function>sd_bus_get_events()</function>, and the I/O call should
-    block for that up to the time-out returned by <function>sd_bus_get_timeout()</function>. After each I/O polling
+    <para><function>sd_bus_get_fd()</function> returns the file descriptor used to communicate from
+    a message bus object. This descriptor can be used with
+    <citerefentry project='man-pages'><refentrytitle>poll</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    or a similar function to wait for I/O events on the specified bus connection object. If the bus
+    object was configured with the <function>sd_bus_set_fd()</function> function, then the
+    <parameter>input_fd</parameter> file descriptor used in that call is returned.</para>
+
+    <para><function>sd_bus_set_fd()</function> sets the file descriptors used to communicate from a
+    message bus object. Both <parameter>input_fd</parameter> and <parameter>output_fd</parameter>
+    must be valid file descriptors. The same file descriptor may be used as both the input and the
+    output file descriptor. This function must be called before the bus is started.</para>
+
+    <para><function>sd_bus_get_events()</function> returns the I/O events to wait for, suitable for
+    passing to <function>poll()</function> or a similar call. Returns a combination of
+    <constant>POLLIN</constant>, <constant>POLLOUT</constant>, … events, or negative on error.
+    </para>
+
+    <para><function>sd_bus_get_timeout()</function> returns the timeout in µs to pass to
+    <function>poll()</function> or a similar call when waiting for events on the specified bus
+    connection. The returned timeout may be zero, in which case a subsequent I/O polling call
+    should be invoked in non-blocking mode. The returned timeout may be
+    <constant>UINT64_MAX</constant> in which case the I/O polling call may block indefinitely,
+    without any applied timeout. Note that the returned timeout should be considered only a
+    maximum sleeping time. It is permissible (and even expected) that shorter timeouts are used by
+    the calling program, in case other event sources are polled in the same event loop. Note that
+    the returned time-value is relative and specified in microseconds. When converting this value in
+    order to pass it as third argument to <function>poll()</function> (which expects milliseconds),
+    care should be taken to use a division that rounds up to ensure the I/O polling operation
+    doesn't sleep for shorter than necessary, which might result in unintended busy looping
+    (alternatively, use
+    <citerefentry project='man-pages'><refentrytitle>ppoll</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    instead of plain <function>poll()</function>, which understands timeouts with nano-second
+    granularity).</para>
+
+    <para>These three functions are useful to hook up a bus connection object with an external or
+    manual event loop involving <function>poll()</function> or a similar I/O polling call. Before
+    each invocation of the I/O polling call, all three functions should be invoked: the file
+    descriptor returned by <function>sd_bus_get_fd()</function> should be polled for the events
+    indicated by <function>sd_bus_get_events()</function>, and the I/O call should block for that up
+    to the timeout returned by <function>sd_bus_get_timeout()</function>. After each I/O polling
     call the bus connection needs to process incoming or outgoing data, by invoking
-    <citerefentry><refentrytitle>sd_bus_process</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
+    <citerefentry><refentrytitle>sd_bus_process</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+    </para>
 
-    <para>Note that these function are only one of three supported ways to implement I/O event handling for bus
-    connections. Alternatively use
-    <citerefentry><refentrytitle>sd_bus_attach_event</refentrytitle><manvolnum>3</manvolnum></citerefentry> to attach a
-    bus connection to an <citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>
-    event loop. Or use <citerefentry><refentrytitle>sd_bus_wait</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    <para>Note that these functions are only one of three supported ways to implement I/O event
+    handling for bus connections. Alternatively use
+    <citerefentry><refentrytitle>sd_bus_attach_event</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    to attach a bus connection to an
+    <citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    event loop. Or use
+    <citerefentry><refentrytitle>sd_bus_wait</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     as a simple synchronous, blocking I/O waiting call.</para>
   </refsect1>
 
   <refsect1>
     <title>Return Value</title>
 
-    <para><function>sd_bus_get_fd()</function> returns the file descriptor used for communication, or a negative
-    <varname>errno</varname>-style error code on error.</para>
+    <para>On success, <function>sd_bus_get_fd()</function> returns the file descriptor used for
+    communication. On failure, it returns a negative errno-style error code.</para>
+
+    <para>On success, <function>sd_bus_set_fd()</function> returns a non-negative integer. On
+    failure, it returns a negative errno-style error code.</para>
 
-    <para><function>sd_bus_get_events()</function> returns the I/O event mask to use for I/O event watching, or a
-    negative <varname>errno</varname>-style error code on error.</para>
+    <para>On success, <function>sd_bus_get_events()</function> returns the I/O event mask to use for
+    I/O event watching. On failure, it returns a negative errno-style error code.</para>
 
-    <para><function>sd_bus_get_timeout()</function> returns zero or positive on success, or a negative
-    <varname>errno</varname>-style error code on error.</para>
+    <para>On success, <function>sd_bus_get_timeout()</function> returns a non-negative integer. On
+    failure, it returns a negative errno-style error code.</para>
 
     <refsect2>
       <title>Errors</title>
         <varlistentry>
           <term><constant>-ECHILD</constant></term>
 
-          <listitem><para>The bus connection was allocated in a parent process and is being reused in a child
-          process after <function>fork()</function>.</para></listitem>
+          <listitem><para>The bus connection was allocated in a parent process and is being reused
+          in a child process after <function>fork()</function>.</para></listitem>
         </varlistentry>
 
         <varlistentry>
           <function>sd_bus_set_fd()</function>, which <function>sd_bus_get_fd()</function> cannot
           return.</para></listitem>
         </varlistentry>
+
+        <varlistentry>
+          <term><constant>-EBADF</constant></term>
+
+          <listitem><para>An invalid file descriptor was passed to
+          <function>sd_bus_set_fd()</function>.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENOPKG</constant></term>
+
+          <listitem><para>The bus cannot be resolved.</para></listitem>
+        </varlistentry>
       </variablelist>
     </refsect2>
   </refsect1>
     <para>
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_bus_set_fd</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_bus_process</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_bus_attach_event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_bus_wait</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
diff --git a/man/sd_bus_get_name_creds.xml b/man/sd_bus_get_name_creds.xml
new file mode 100644 (file)
index 0000000..3731336
--- /dev/null
@@ -0,0 +1,121 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_bus_get_name_creds" xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>sd_bus_get_name_creds</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_bus_get_name_creds</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_bus_get_name_creds</refname>
+    <refname>sd_bus_get_owner_creds</refname>
+
+    <refpurpose>Query bus client credentials</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_get_name_creds</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>const char *<parameter>name</parameter></paramdef>
+        <paramdef>uint64_t <parameter>mask</parameter></paramdef>
+        <paramdef>sd_bus_creds **<parameter>creds</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_get_owner_creds</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>uint64_t <parameter>mask</parameter></paramdef>
+        <paramdef>sd_bus_creds **<parameter>creds</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>sd_bus_get_name_creds()</function> queries the credentials of the bus client
+    identified by <parameter>name</parameter>. The <parameter>mask</parameter> parameter is a combo of
+    <constant index='false'>SD_BUS_CREDS_*</constant> flags that indicate which credential info the caller is
+    interested in. See
+    <citerefentry><refentrytitle>sd_bus_creds_new_from_pid</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    for a list of possible flags. On success, <parameter>creds</parameter> contains a new
+    <structname>sd_bus_creds</structname> instance with the requested information. Ownership of this instance
+    belongs to the caller and it should be freed once no longer needed by calling
+    <citerefentry><refentrytitle>sd_bus_creds_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+    </para>
+
+    <para><function>sd_bus_get_owner_creds()</function> queries the credentials of the creator of the given
+    bus. The <parameter>mask</parameter> and <parameter>creds</parameter> parameters behave the same as in
+    <function>sd_bus_get_name_creds()</function>.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On success, these functions return a non-negative integer. On failure, they return a negative
+    errno-style error code.</para>
+
+    <refsect2>
+      <title>Errors</title>
+
+      <para>Returned errors may indicate the following problems:</para>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>-EINVAL</constant></term>
+
+          <listitem><para>An argument is invalid.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENOPKG</constant></term>
+
+          <listitem><para>The bus cannot be resolved.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-EPERM</constant></term>
+
+          <listitem><para>The bus has already been started.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ECHILD</constant></term>
+
+          <listitem><para>The bus was created in a different process.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENOMEM</constant></term>
+
+          <listitem><para>Memory allocation failed.</para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_creds_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+</refentry>
diff --git a/man/sd_bus_get_name_machine_id.xml b/man/sd_bus_get_name_machine_id.xml
new file mode 100644 (file)
index 0000000..8f3ce64
--- /dev/null
@@ -0,0 +1,98 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_bus_get_name_machine_id" xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>sd_bus_get_name_machine_id</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_bus_get_name_machine_id</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_bus_get_name_machine_id</refname>
+
+    <refpurpose>Retrieve a bus client's machine identity</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_get_name_machine_id</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>const char *<parameter>name</parameter></paramdef>
+        <paramdef>sd_id128_t *<parameter>machine</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>sd_bus_get_name_machine_id()</function> retrieves the D-Bus machine identity of the
+    machine that the bus client identified by <parameter>name</parameter> is running on. Internally, it calls
+    the <function>GetMachineId</function> method of the <constant>org.freedesktop.DBus.Peer</constant>
+    interface. The D-Bus machine identity is a 128-bit UUID. On Linux systems running systemd, this
+    corresponds to the contents of <filename>/etc/machine-id</filename>. On success, the machine identity is
+    stored in <parameter>machine</parameter>.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On success, this function returns a non-negative integer. On failure, it returns a negative
+    errno-style error code.</para>
+
+    <refsect2>
+      <title>Errors</title>
+
+      <para>Returned errors may indicate the following problems:</para>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>-EINVAL</constant></term>
+
+          <listitem><para>An argument is invalid.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENOPKG</constant></term>
+
+          <listitem><para>The bus cannot be resolved.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ECHILD</constant></term>
+
+          <listitem><para>The bus was created in a different process.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENOMEM</constant></term>
+
+          <listitem><para>Memory allocation failed.</para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
diff --git a/man/sd_bus_interface_name_is_valid.xml b/man/sd_bus_interface_name_is_valid.xml
new file mode 100644 (file)
index 0000000..a72024e
--- /dev/null
@@ -0,0 +1,98 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_bus_interface_name_is_valid" xmlns:xi="http://www.w3.org/2001/XInclude">
+  <refentryinfo>
+    <title>sd_bus_interface_name_is_valid</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_bus_interface_name_is_valid</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_bus_interface_name_is_valid</refname>
+    <refname>sd_bus_service_name_is_valid</refname>
+    <refname>sd_bus_member_name_is_valid</refname>
+    <refname>sd_bus_object_path_is_valid</refname>
+
+    <refpurpose>Check if a string is a valid bus name or object path</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_interface_name_is_valid</function></funcdef>
+        <paramdef>const char* <parameter>p</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_service_name_is_valid</function></funcdef>
+        <paramdef>const char* <parameter>p</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_member_name_is_valid</function></funcdef>
+        <paramdef>const char* <parameter>p</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_object_path_is_valid</function></funcdef>
+        <paramdef>const char* <parameter>p</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>sd_bus_interface_name_is_valid()</function> checks if a given string
+    <parameter>p</parameter> is a syntactically valid bus interface name. Similarly,
+    <function>sd_bus_service_name_is_valid()</function> checks if the argument is a valid bus service name,
+    <function>sd_bus_member_name_is_valid()</function> checks if the argument is a valid bus interface member
+    name, and <function>sd_bus_object_path_is_valid()</function> checks if the argument is a valid bus object
+    path. Those functions generally check that only allowed characters are used and that the length of the
+    string is within limits.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>Those functions return 1 if the argument is a valid interface / service / member name or object
+    path, and 0 if it is not. If the argument is NULL, an error is returned.</para>
+
+    <refsect2>
+      <title>Errors</title>
+
+      <para>Returned errors may indicate the following problems:</para>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>-EINVAL</constant></term>
+
+          <listitem><para>The <parameter>p</parameter> parameter is
+          <constant>NULL</constant>.</para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_call_method</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
index d993142cb5c9d45d9bfd0272685a050fa7942b57..4a21189c1d2a5f4cd6fb8f2645b485ef3e16aa84 100644 (file)
@@ -20,7 +20,7 @@
     <refname>sd_bus_is_open</refname>
     <refname>sd_bus_is_ready</refname>
 
-    <refpurpose>Check whether the a bus connection is open or ready.</refpurpose>
+    <refpurpose>Check whether the bus connection is open or ready</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
diff --git a/man/sd_bus_list_names.xml b/man/sd_bus_list_names.xml
new file mode 100644 (file)
index 0000000..ad7ecd0
--- /dev/null
@@ -0,0 +1,110 @@
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_bus_list_names"
+          xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>sd_bus_list_names</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_bus_list_names</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_bus_list_names</refname>
+
+    <refpurpose>Retrieve information about registered names on a bus</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_list_names</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>char ***<parameter>acquired</parameter></paramdef>
+        <paramdef>char ***<parameter>activatable</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>sd_bus_list_names()</function> retrieves information about the registered names on a bus.
+    If <parameter>acquired</parameter> is not <constant>NULL</constant>, this function calls
+    <ulink url="https://dbus.freedesktop.org/doc/dbus-specification.html#bus-messages-list-names">
+    org.freedesktop.DBus.ListNames</ulink> to retrieve the list of currently-owned names on the bus. If
+    <parameter>acquired</parameter> is not <constant>NULL</constant>, the function calls
+    <ulink url="https://dbus.freedesktop.org/doc/dbus-specification.html#bus-messages-list-activatable-names">
+    org.freedesktop.DBus.ListActivableNames</ulink> to retrieve the list of all names on the bus that can be
+    activated. Note that ownership of the arrays returned by <function>sd_bus_list_names()</function> in
+    <parameter>acquired</parameter> and <parameter>activatable</parameter> is transferred to the caller and
+    hence, the caller is responsible for freeing these arrays and their contents.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On success, <function>sd_bus_list_names()</function> returns a non-negative integer. On failure,
+    it returns a negative errno-style error code.</para>
+
+    <refsect2>
+      <title>Errors</title>
+
+      <para>Returned errors may indicate the following problems:</para>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>-EINVAL</constant></term>
+
+          <listitem><para><parameter>bus</parameter> or both <parameter>acquired</parameter> and
+          <parameter>activatable</parameter> were <constant>NULL</constant>.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENOPKG</constant></term>
+
+          <listitem><para>The bus cannot be resolved.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ECHILD</constant></term>
+
+          <listitem><para>The bus was created in a different process.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENOMEM</constant></term>
+
+          <listitem><para>Memory allocation failed.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENOTCONN</constant></term>
+
+          <listitem><para>The bus is not connected.</para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+</refentry>
index b87468e37336c79604846cc3a53a8a462e22b3b8..5faadd603a123cfd915e7fe998307826b90e3407 100644 (file)
@@ -20,8 +20,7 @@
     <refname>sd_bus_message_append</refname>
     <refname>sd_bus_message_appendv</refname>
 
-    <refpurpose>Attach fields to a D-Bus message based on a type
-    string</refpurpose>
+    <refpurpose>Attach fields to a D-Bus message based on a type string</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
       </funcprototype>
 
       <funcprototype>
-          <funcdef>int sd_bus_message_appendv</funcdef>
-          <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
-          <paramdef>const char *<parameter>types</parameter></paramdef>
-          <paramdef>va_list <parameter>ap</parameter></paramdef>
+        <funcdef>int sd_bus_message_appendv</funcdef>
+        <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
+        <paramdef>const char *<parameter>types</parameter></paramdef>
+        <paramdef>va_list <parameter>ap</parameter></paramdef>
       </funcprototype>
 
     </funcsynopsis>
   <refsect1>
     <title>Description</title>
 
-    <para>The <function>sd_bus_message_append()</function> function
-    appends a sequence of fields to the D-Bus message object
-    <parameter>m</parameter>. The type string
-    <parameter>types</parameter> describes the types of the field
-    arguments that follow. For each type specified in the type string,
-    one or more arguments need to be specified, in the same order as
-    declared in the type string.</para>
-
-    <para>The type string is composed of the elements shown in the
-    table below. It contains zero or more single "complete types".
-    Each complete type may be one of the basic types or a fully
-    described container type. A container type may be a structure with
-    the contained types, a variant, an array with its element type, or
-    a dictionary entry with the contained types. The type string is
-    <constant>NUL</constant>-terminated.</para>
-
-    <para>In case of a basic type, one argument of the corresponding
-    type is expected.</para>
-
-    <para>A structure is denoted by a sequence of complete types
-    between <literal>(</literal> and <literal>)</literal>. This
-    sequence cannot be empty — it must contain at least one type.
-    Arguments corresponding to this nested sequence follow the same
-    rules as if they were not nested.</para>
-
-    <para>A variant is denoted by <literal>v</literal>. Corresponding
-    arguments must begin with a type string denoting a complete type,
-    and following that, arguments corresponding to the specified type.
+    <para>The <function>sd_bus_message_append()</function> function appends a sequence of fields to
+    the D-Bus message object <parameter>m</parameter>. The type string <parameter>types</parameter>
+    describes the types of the field arguments that follow. For each type specified in the type
+    string, one or more arguments need to be specified, in the same order as declared in the type
+    string.</para>
+
+    <para>The type string is composed of the elements shown in the table below. It contains zero or
+    more single "complete types". Each complete type may be one of the basic types or a fully
+    described container type. A container type may be a structure with the contained types, a
+    variant, an array with its element type, or a dictionary entry with the contained types. The
+    type string is <constant>NUL</constant>-terminated.</para>
+
+    <para>In case of a basic type, one argument of the corresponding type is expected.</para>
+
+    <para>A structure is denoted by a sequence of complete types between <literal>(</literal> and
+    <literal>)</literal>. This sequence cannot be empty — it must contain at least one type.
+    Arguments corresponding to this nested sequence follow the same rules as if they were not
+    nested.</para>
+
+    <para>A variant is denoted by <literal>v</literal>. Corresponding arguments must begin with a
+    type string denoting a complete type, and following that, arguments corresponding to the
+    specified type.</para>
+
+    <para>An array is denoted by <literal>a</literal> followed by a complete type. Corresponding
+    arguments must begin with the number of entries in the array, followed by the entries
+    themselves, matching the element type of the array.</para>
+
+    <para>A dictionary is an array of dictionary entries, denoted by <literal>a</literal> followed
+    by a pair of complete types between <literal>{</literal> and <literal>}</literal>. The first of
+    those types must be a basic type. Corresponding arguments must begin with the number of
+    dictionary entries, followed by a pair of values for each entry matching the element type of the
+    dictionary entries.</para>
+
+    <para><function>sd_bus_message_appendv()</function> is equivalent to
+    <function>sd_bus_message_append()</function>, except that it is called with a
+    <literal>va_list</literal> instead of a variable number of arguments. This function does not
+    call the <function>va_end()</function> macro. Because it invokes the
+    <function>va_arg()</function> macro, the value of <parameter>ap</parameter> is undefined after
+    the call.</para>
+
+    <para>For further details on the D-Bus type system, please consult the
+    <ulink url="http://dbus.freedesktop.org/doc/dbus-specification.html#type-system">D-Bus Specification</ulink>.
     </para>
 
-    <para>An array is denoted by <literal>a</literal> followed by a
-    complete type. Corresponding arguments must begin with the number of
-    entries in the array, followed by the entries themselves,
-    matching the element type of the array.</para>
-
-    <para>A dictionary is an array of dictionary entries, denoted by
-    <literal>a</literal> followed by a pair of complete types between
-    <literal>{</literal> and <literal>}</literal>. The first of those
-    types must be a basic type. Corresponding arguments must begin
-    with the number of dictionary entries, followed by a pair of
-    values for each entry matching the element type of
-    the dictionary entries.</para>
-
-    <para>The <function>sd_bus_message_appendv()</function> is equivalent to the
-    <function>sd_bus_message_append()</function>, except that it is called with
-    a <literal>va_list</literal> instead of a variable number of arguments. This
-    function does not call the <function>va_end()</function> macro. Because it
-    invokes the <function>va_arg()</function> macro, the value of
-    <parameter>ap</parameter> is undefined after the call.</para>
-
-    <para>For further details on the D-Bus type system, please consult
-    the <ulink
-    url="http://dbus.freedesktop.org/doc/dbus-specification.html#type-system">D-Bus
-    Specification</ulink>.</para>
-
     <table>
       <title>Item type specifiers</title>
 
     <constant>NULL</constant>, which is equivalent to an empty string. See
     <citerefentry><refentrytitle>sd_bus_message_append_basic</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     for the precise interpretation of those and other types.</para>
-
   </refsect1>
 
   <refsect1>
@@ -205,12 +192,12 @@ sd_bus_message_append(m, "ynqiuxtd", y, n, q, i, u, x, t, d);</programlisting>
      <para>Append a structure composed of a string and a D-Bus path:</para>
 
      <programlisting>sd_bus_message_append(m, "(so)", "a string", "/a/path");
-</programlisting>
+    </programlisting>
 
      <para>Append an array of UNIX file descriptors:</para>
 
      <programlisting>sd_bus_message_append(m, "ah", 3, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO);
-</programlisting>
+    </programlisting>
 
      <para>Append a variant, with the real type "g" (signature),
      and value "sdbusisgood":</para>
@@ -227,8 +214,8 @@ sd_bus_message_append(m, "ynqiuxtd", y, n, q, i, u, x, t, d);</programlisting>
   <refsect1>
     <title>Return Value</title>
 
-    <para>On success, these functions return 0 or a positive integer. On failure, they return a negative
-    errno-style error code.</para>
+    <para>On success, these functions return a non-negative integer. On failure, they return a
+    negative errno-style error code.</para>
 
     <xi:include href="sd_bus_message_append_basic.xml" xpointer="errors" />
   </refsect1>
@@ -242,7 +229,8 @@ sd_bus_message_append(m, "ynqiuxtd", y, n, q, i, u, x, t, d);</programlisting>
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_bus_message_append_basic</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_bus_message_append_array</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+      <citerefentry><refentrytitle>sd_bus_message_append_array</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_message_open_container</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     </para>
   </refsect1>
 
diff --git a/man/sd_bus_message_at_end.xml b/man/sd_bus_message_at_end.xml
new file mode 100644 (file)
index 0000000..ce21c7e
--- /dev/null
@@ -0,0 +1,86 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_bus_message_at_end" xmlns:xi="http://www.w3.org/2001/XInclude">
+  <refentryinfo>
+    <title>sd_bus_message_at_end</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_bus_message_at_end</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_bus_message_at_end</refname>
+
+    <refpurpose>Check if a message has been fully read</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_message_at_end</function></funcdef>
+        <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
+        <paramdef>int <parameter>complete</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>sd_bus_message_at_end()</function> returns whether all data from the currently opened
+    container in <parameter>m</parameter> or all data from all containers in <parameter>m</parameter> has
+    been read. If <parameter>complete</parameter> is zero, this function returns whether all data from the
+    currently opened container has been read. If <parameter>complete</parameter> is non-zero, this function
+    returns whether all data from all containers in <parameter>m</parameter> has been read.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>If all data from all containers or the current container (depending on the value of
+    <parameter>complete</parameter>) has been read, <function>sd_bus_message_at_end()</function> returns a
+    positive integer. If there is still data left to be read, it returns zero. On failure, it returns a
+    negative errno-style error code.</para>
+
+    <refsect2>
+      <title>Errors</title>
+
+      <para>Returned errors may indicate the following problems:</para>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>-EINVAL</constant></term>
+
+          <listitem><para>The <parameter>m</parameter> parameter is <constant>NULL</constant>.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-EPERM</constant></term>
+
+          <listitem><para>The message is not sealed.</para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_message_read</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+</refentry>
index db9e46d991361dccda9901d452ff4b3e22f7c9f5..720b114273d21d9cb34cea1cb0199e0f25fe4c59 100644 (file)
@@ -65,7 +65,7 @@
 
     <para>Output for a signal message (with <constant>SD_BUS_MESSAGE_DUMP_WITH_HEADER</constant>):
     <programlisting>
-‣ Type=signal  Endian=l  Flags=1  Version=1  Priority=0 Cookie=22
+‣ Type=signal  Endian=l  Flags=1  Version=1  Cookie=22
   Path=/value/a  Interface=org.freedesktop.DBus.Properties  Member=PropertiesChanged
   MESSAGE "sa{sv}as" {
           STRING "org.freedesktop.systemd.ValueTest";
index d8a45ce7a07dda66c6905be2f78b55e7a6602b71..442c763a4924c22f7fc198ca0413cee0959d0991 100644 (file)
 
   <refnamediv>
     <refname>sd_bus_message_get_type</refname>
+    <refname>sd_bus_message_get_error</refname>
+    <refname>sd_bus_message_get_errno</refname>
+    <refname>sd_bus_message_get_creds</refname>
     <refname>sd_bus_message_is_signal</refname>
     <refname>sd_bus_message_is_method_call</refname>
     <refname>sd_bus_message_is_method_error</refname>
 
-    <refpurpose>Query bus message addressing metadata</refpurpose>
+    <refpurpose>Query bus message addressing/credentials metadata</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
 
       <funcprototype>
         <funcdef>int <function>sd_bus_message_get_type</function></funcdef>
-        <paramdef>sd_bus_message *<parameter>message</parameter></paramdef>
+        <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
         <paramdef>uint8_t *<parameter>type</parameter></paramdef>
       </funcprototype>
 
+      <funcprototype>
+        <funcdef>sd_bus_error* <function>sd_bus_message_get_error</function></funcdef>
+        <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_message_get_errno</function></funcdef>
+        <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>sd_bus_creds* <function>sd_bus_message_get_creds</function></funcdef>
+        <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
+      </funcprototype>
+
       <funcprototype>
         <funcdef>int <function>sd_bus_message_is_signal</function></funcdef>
-        <paramdef>sd_bus_message *<parameter>message</parameter></paramdef>
+        <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
         <paramdef>const char *<parameter>interface</parameter></paramdef>
         <paramdef>const char *<parameter>member</parameter></paramdef>
       </funcprototype>
 
       <funcprototype>
         <funcdef>int <function>sd_bus_message_is_method_call</function></funcdef>
-        <paramdef>sd_bus_message *<parameter>message</parameter></paramdef>
+        <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
         <paramdef>const char *<parameter>interface</parameter></paramdef>
         <paramdef>const char *<parameter>member</parameter></paramdef>
       </funcprototype>
 
       <funcprototype>
         <funcdef>int <function>sd_bus_message_is_method_error</function></funcdef>
-        <paramdef>sd_bus_message *<parameter>message</parameter></paramdef>
+        <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
         <paramdef>const char *<parameter>name</parameter></paramdef>
       </funcprototype>
     </funcsynopsis>
 
     <para><function>sd_bus_message_get_type()</function> returns the type of a message in the output
     parameter <parameter>type</parameter>, one of <constant>SD_BUS_MESSAGE_METHOD_CALL</constant>,
-    <constant>SD_BUS_MESSAGE_METHOD_RETURN</constant>,
-    <constant>SD_BUS_MESSAGE_METHOD_ERROR</constant>, <constant>SD_BUS_MESSAGE_SIGNAL</constant>.
-    This type is either specified as a parameter when the message is created using
-    <citerefentry><refentrytitle>sd_bus_set_message_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <constant>SD_BUS_MESSAGE_METHOD_RETURN</constant>, <constant>SD_BUS_MESSAGE_METHOD_ERROR</constant>,
+    <constant>SD_BUS_MESSAGE_SIGNAL</constant>. This type is either specified as a parameter when the message
+    is created using
+    <citerefentry><refentrytitle>sd_bus_message_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
     or is set automatically when the message is created using
-    <citerefentry><refentrytitle>sd_bus_set_message_new_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-    <citerefentry><refentrytitle>sd_bus_set_message_new_method_call</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-    <citerefentry><refentrytitle>sd_bus_set_message_new_method_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>
-    and similar functions.
+    <citerefentry><refentrytitle>sd_bus_message_new_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_bus_message_new_method_call</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_bus_message_new_method_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    and similar functions.</para>
+
+    <para><function>sd_bus_message_get_error()</function> returns the error stored in the message
+    <parameter>m</parameter>, if there is any. Otherwise, it returns <constant>NULL</constant>.
+    <function>sd_bus_message_get_errno()</function> returns the error stored in the message
+    <parameter>m</parameter> as a positive errno-style value, if there is any. Otherwise, it returns zero.
+    Errors are mapped to errno values according to the default and any additional registered error mappings.
+    See <citerefentry><refentrytitle>sd-bus-errors</refentrytitle><manvolnum>3</manvolnum></citerefentry> and
+    <citerefentry><refentrytitle>sd_bus_error_add_map</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
     </para>
 
-    <para><function>sd_bus_message_is_signal()</function> checks if message <parameter>m</parameter>
-    is a signal message. If <parameter>interface</parameter> is non-null, it also checks if the
-    message has the same interface set. If <parameter>member</parameter> is non-null, it also checks
-    if the message has the same member set. Also see
-    <citerefentry><refentrytitle>sd_bus_set_message_new_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>. It returns true when all checks pass.</para>
+    <para><function>sd_bus_message_get_creds()</function> returns the message credentials attached to the
+    message <parameter>m</parameter>. If no credentials are attached to the message, it returns
+    <constant>NULL</constant>. Ownership of the credentials instance is not transferred to the caller and
+    hence should not be freed.</para>
+
+    <para><function>sd_bus_message_is_signal()</function> checks if message <parameter>m</parameter> is a
+    signal message. If <parameter>interface</parameter> is non-null, it also checks if the message has the
+    same interface set. If <parameter>member</parameter> is non-null, it also checks if the message has the
+    same member set. Also see
+    <citerefentry><refentrytitle>sd_bus_message_new_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+    It returns true when all checks pass.</para>
 
     <para><function>sd_bus_message_is_method_call()</function> checks if message <parameter>m</parameter>
-    is a method call message. If <parameter>interface</parameter> is non-null, it also checks if the
-    message has the same interface set. If <parameter>member</parameter> is non-null, it also checks
-    if the message has the same member set. Also see
-    <citerefentry><refentrytitle>sd_bus_set_message_new_method_call</refentrytitle><manvolnum>3</manvolnum></citerefentry>. It returns true when all checks pass.</para>
+    is a method call message. If <parameter>interface</parameter> is non-null, it also checks if the message
+    has the same interface set. If <parameter>member</parameter> is non-null, it also checks if the message
+    has the same member set. Also see
+    <citerefentry><refentrytitle>sd_bus_message_new_method_call</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+    It returns true when all checks pass.</para>
 
     <para><function>sd_bus_message_is_method_error()</function> checks if message <parameter>m</parameter>
-    is an error reply message. If <parameter>name</parameter> is non-null, it also checks if the
-    message has the same error identifier set. Also see
-    <citerefentry><refentrytitle>sd_bus_set_message_new_method_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>. It returns true when all checks pass.</para>
-</refsect1>
+    is an error reply message. If <parameter>name</parameter> is non-null, it also checks if the message has
+    the same error identifier set. Also see
+    <citerefentry><refentrytitle>sd_bus_message_new_method_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+    It returns true when all checks pass.</para>
+  </refsect1>
 
   <refsect1>
     <title>Return Value</title>
 
-    <para>On success, those functions return 0 or a positive
-    integer. On failure, it returns a negative errno-style error code.</para>
+    <para>On success, these functions (except <function>sd_bus_message_get_error()</function> and
+    <function>sd_bus_message_get_creds()</function>) return a non-negative integer. On failure, they return a
+    negative errno-style error code. <function>sd_bus_message_get_errno()</function> always returns a
+    non-negative integer, even on failure.</para>
 
     <refsect2>
       <title>Errors</title>
         <varlistentry>
           <term><constant>-EINVAL</constant></term>
 
-          <listitem><para>The <parameter>message</parameter> parameter or the output parameter are
+          <listitem><para>The message parameter <parameter>m</parameter> or an output parameter is
           <constant>NULL</constant>.</para></listitem>
         </varlistentry>
       </variablelist>
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_bus_message_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_bus_message_set_destination</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+      <citerefentry><refentrytitle>sd_bus_message_set_destination</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd-bus-errors</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_error_add_map</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     </para>
   </refsect1>
 
index 0d181ed82eb71c46d90e19a0b6d6aaf27dc9544a..cfb13af51fafec2da1834d6f178c6d9618a462d3 100644 (file)
@@ -28,7 +28,7 @@
       <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
 
       <funcprototype>
-        <funcdef>int sd_bus_message_new_method_call</funcdef>
+        <funcdef>int <function>sd_bus_message_new_method_call</function></funcdef>
         <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
         <paramdef>sd_bus_message **<parameter>m</parameter></paramdef>
         <paramdef>const char *<parameter>destination</parameter></paramdef>
@@ -38,7 +38,7 @@
       </funcprototype>
 
       <funcprototype>
-        <funcdef>int sd_bus_message_new_method_return</funcdef>
+        <funcdef>int <function>sd_bus_message_new_method_return</function></funcdef>
         <paramdef>sd_bus_message *<parameter>call</parameter></paramdef>
         <paramdef>sd_bus_message **<parameter>m</parameter></paramdef>
       </funcprototype>
     has only a single member with the given name and there is no ambiguity if the interface name is
     omitted.</para>
 
-    <para>The <function>sd_bus_message_new_method_call()</function> function creates a new bus
+    <para>Note that this is a low level interface. See
+    <citerefentry><refentrytitle>sd_bus_call_method</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    for a more convenient way of calling D-Bus methods.</para>
+
+    <para>The <function>sd_bus_message_new_method_return()</function> function creates a new bus
     message object that is a reply to the method call <parameter>call</parameter> and returns it in
     the <parameter>m</parameter> output parameter. The <parameter>call</parameter> parameter must be
     a method call message. The sender of <parameter>call</parameter> is used as the destination.
@@ -80,8 +84,8 @@
   <refsect1>
     <title>Return Value</title>
 
-    <para>This function returns 0 if the message object was successfully created, and a negative
-    errno-style error code otherwise.</para>
+    <para>On success, these functions return a non-negative integer. On failure, they return a
+    negative errno-style error code.</para>
 
     <refsect2 id='errors'>
       <title>Errors</title>
     <para>
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_call</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_call_method</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_bus_path_encode</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     </para>
   </refsect1>
index 0c471c534ff6509b01a7a5979c51e1b3ff9cb397..39bb24c3a53613b10f0f7d45b050f68ee1dffae1 100644 (file)
@@ -22,7 +22,7 @@
     <refname>sd_bus_message_new_method_errno</refname>
     <refname>sd_bus_message_new_method_errnof</refname>
 
-    <refpurpose>Create a an error reply for a method call</refpurpose>
+    <refpurpose>Create an error reply for a method call</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
@@ -98,7 +98,7 @@
     an error reply similarly to
     <function>sd_bus_message_new_method_error()</function>, but in addition to the
     error structure <parameter>p</parameter>, it takes an
-    <citerefentry><refentrytitle>errno</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    <citerefentry project='man-pages'><refentrytitle>errno</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     error value in parameter <parameter>error</parameter>. If the error
     <parameter>p</parameter> is set (see
     <citerefentry><refentrytitle>sd_bus_error_is_set</refentrytitle><manvolnum>3</manvolnum></citerefentry>),
     <para>The <function>sd_bus_message_new_method_errnof()</function> function
     creates an error reply similarly to
     <function>sd_bus_message_new_method_error()</function>. It takes an
-    <citerefentry><refentrytitle>errno</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    <citerefentry project='man-pages'><refentrytitle>errno</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     error value in parameter <parameter>error</parameter>, plus a <citerefentry
     project='man-pages'><refentrytitle>printf</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     format string <parameter>format</parameter> and corresponding arguments.
index 61619304db9513fd53ffec4999f46246c6cc7311..5ac35e7a3a6dcbf7455f0c7b89f515d71414a904 100644 (file)
@@ -45,7 +45,7 @@
     parameter. The signal will be sent to path <parameter>path</parameter>, on the interface
     <parameter>interface</parameter>, member <parameter>member</parameter>. When this message is
     sent, no reply is expected. See
-    <citerefentry><refentrytitle>sd_bus_message_new_call</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+    <citerefentry><refentrytitle>sd_bus_message_new_method_call</refentrytitle><manvolnum>1</manvolnum></citerefentry>
     for a short description of the meaning of the <parameter>path</parameter>,
     <parameter>interface</parameter>, and <parameter>member</parameter> parameters.
     </para>
 
     <para>
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+      <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_emit_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     </para>
   </refsect1>
 
diff --git a/man/sd_bus_message_open_container.xml b/man/sd_bus_message_open_container.xml
new file mode 100644 (file)
index 0000000..5a65518
--- /dev/null
@@ -0,0 +1,165 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_bus_message_open_container"
+          xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>sd_bus_message_open_container</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_bus_message_open_container</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_bus_message_open_container</refname>
+    <refname>sd_bus_message_close_container</refname>
+    <refname>sd_bus_message_enter_container</refname>
+    <refname>sd_bus_message_exit_container</refname>
+
+    <refpurpose>Create and move between containers in D-Bus messages</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>int sd_bus_message_open_container</funcdef>
+        <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
+        <paramdef>char <parameter>type</parameter></paramdef>
+        <paramdef>const char *<parameter>contents</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int sd_bus_message_close_container</funcdef>
+        <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int sd_bus_message_enter_container</funcdef>
+        <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
+        <paramdef>char <parameter>type</parameter></paramdef>
+        <paramdef>const char *<parameter>contents</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int sd_bus_message_exit_container</funcdef>
+        <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>sd_bus_message_open_container()</function> appends a new container to the message
+    <parameter>m</parameter>. After opening a new container, it can be filled with content using
+    <citerefentry><refentrytitle>sd_bus_message_append</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    and similar functions. Containers behave like a stack. To nest containers inside each other, call
+    <function>sd_bus_message_open_container()</function> multiple times without calling
+    <function>sd_bus_message_close_container()</function> in between. Each container will be nested inside the
+    previous container. <parameter>type</parameter> represents the container type and should be one of
+    <literal>r</literal>, <literal>a</literal>, <literal>v</literal> or <literal>e</literal> as described in
+    <citerefentry><refentrytitle>sd_bus_message_append</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+    Instead of literals, the corresponding constants <constant>SD_BUS_TYPE_STRUCT</constant>,
+    <constant>SD_BUS_TYPE_ARRAY</constant>, <constant>SD_BUS_TYPE_VARIANT</constant> or
+    <constant>SD_BUS_TYPE_DICT_ENTRY</constant> can also be used. <parameter>contents</parameter> describes
+    the type of the container's elements and should be a D-Bus type string following the rules described in
+    <citerefentry><refentrytitle>sd_bus_message_append</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+    </para>
+
+    <para><function>sd_bus_message_close_container()</function> closes the last container opened with
+    <function>sd_bus_message_open_container()</function>. On success, the write pointer of the message
+    <parameter>m</parameter> is positioned after the closed container in its parent container or in
+    <parameter>m</parameter> itself if there is no parent container.</para>
+
+    <para><function>sd_bus_message_enter_container()</function> enters the next container of the message
+    <parameter>m</parameter>. It behaves mostly the same as
+    <function>sd_bus_message_open_container()</function>. Entering a container allows reading its contents
+    with
+    <citerefentry><refentrytitle>sd_bus_message_read</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    and similar functions. <parameter>type</parameter> and <parameter>contents</parameter> are the same as in
+    <function>sd_bus_message_open_container()</function>.</para>
+
+    <para><function>sd_bus_message_exit_container()</function> exits the scope of the last container entered
+    with <function>sd_bus_message_enter_container()</function>. It behaves mostly the same as
+    <function>sd_bus_message_close_container()</function>.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On success, these functions return a non-negative integer. On failure, they return a negative
+    errno-style error code.</para>
+
+    <refsect2>
+      <title>Errors</title>
+
+      <para>Returned errors may indicate the following problems:</para>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>-EINVAL</constant></term>
+
+          <listitem><para><parameter>m</parameter> or <parameter>contents</parameter> are
+          <constant>NULL</constant> or <parameter>type</parameter> is invalid.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-EPERM</constant></term>
+
+          <listitem><para>The message <parameter>m</parameter> is already sealed.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ESTALE</constant></term>
+
+          <listitem><para>The message <parameter>m</parameter> is in an invalid state.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENOMEM</constant></term>
+
+          <listitem><para>Memory allocation failed.</para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>Examples</title>
+
+    <example>
+      <title>Append an array of strings to a message</title>
+
+      <programlisting><xi:include href="sd-bus-container-append.c" parse="text" /></programlisting>
+    </example>
+
+    <example>
+      <title>Read an array of strings from a message</title>
+
+      <programlisting><xi:include href="sd-bus-container-read.c" parse="text" /></programlisting>
+    </example>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_message_append</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_message_read</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <ulink url="https://dbus.freedesktop.org/doc/dbus-specification.html">The D-Bus specification</ulink>
+    </para>
+  </refsect1>
+
+</refentry>
index 2afa44174229bba1e687e418bd13508462f790d4..1b9f36cd84ed8f2a55672e5bacd1f6db1213af6b 100644 (file)
@@ -19,6 +19,7 @@
   <refnamediv>
     <refname>sd_bus_message_read</refname>
     <refname>sd_bus_message_readv</refname>
+    <refname>sd_bus_message_peek_type</refname>
 
     <refpurpose>Read a sequence of values from a message</refpurpose>
   </refnamediv>
       <funcprototype>
         <funcdef>int <function>sd_bus_message_read</function></funcdef>
         <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
-        <paramdef>char char *<parameter>types</parameter></paramdef>
+        <paramdef>const char *<parameter>types</parameter></paramdef>
         <paramdef>...</paramdef>
       </funcprototype>
 
       <funcprototype>
         <funcdef>int <function>sd_bus_message_readv</function></funcdef>
         <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
-        <paramdef>char char *<parameter>types</parameter></paramdef>
+        <paramdef>const char *<parameter>types</parameter></paramdef>
         <paramdef>va_list <parameter>ap</parameter></paramdef>
       </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_message_peek_type</function></funcdef>
+        <paramdef>char *<parameter>type</parameter></paramdef>
+        <paramdef>const char **<parameter>contents</parameter></paramdef>
+      </funcprototype>
     </funcsynopsis>
   </refsynopsisdiv>
 
   <refsect1>
     <title>Description</title>
 
-    <para><function>sd_bus_message_read()</function> reads a sequence of fields from
-    the D-Bus message object <parameter>m</parameter> and advances the read position
-    in the message. The type string <parameter>types</parameter> describes the types
-    of items expected in the message and the field arguments that follow. The type
-    string may be <constant>NULL</constant> or empty, in which case nothing is
-    read.</para>
+    <para><function>sd_bus_message_read()</function> reads a sequence of fields from the D-Bus message object
+    <parameter>m</parameter> and advances the read position in the message. The type string
+    <parameter>types</parameter> describes the types of items expected in the message and the field arguments
+    that follow. The type string may be <constant>NULL</constant> or empty, in which case nothing is read.
+    </para>
 
     <para>The type string is composed of the elements described in
     <citerefentry><refentrytitle>sd_bus_message_append</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-    i.e. basic and container types. It must contain zero or more single "complete
-    types". The type string is <constant>NUL</constant>-terminated.</para>
-
-    <para>For each type specified in the type string, one or more arguments need to be specified
-    after the <parameter>types</parameter> parameter, in the same order. The arguments must be
-    pointers to appropriate types (a pointer to <type>int8_t</type> for a <literal>y</literal> in
-    the type string, a pointer to <type>int32_t</type> for an <literal>i</literal>, a pointer to
-    <type>const char*</type> for an <literal>s</literal>, ...)  which are set based on the values in
-    the message. As an exception, in case or array and variant types, the first argument is an
-    "input" argument that further specifies how the message should be read. See the table below for
-    a complete list of allowed arguments and their types. Note that, if the basic type is a pointer
-    (e.g., <type>const char *</type> in the case of a string), the argument is a pointer to a
-    pointer, and also the pointer value that is written is only borrowed and the contents must be
-    copied if they are to be used after the end of the messages lifetime.</para>
-
-    <para>Each argument may also be <constant>NULL</constant>, in which case the value is read and
-    ignored.</para>
+    i.e. basic and container types. It must contain zero or more single "complete types". The type string is
+    <constant>NUL</constant>-terminated.</para>
+
+    <para>For each type specified in the type string, one or more arguments need to be specified after the
+    <parameter>types</parameter> parameter, in the same order. The arguments must be pointers to appropriate
+    types (a pointer to <type>int8_t</type> for a <literal>y</literal> in the type string, a pointer to
+    <type>int32_t</type> for an <literal>i</literal>, a pointer to <type>const char*</type> for an
+    <literal>s</literal>, ...)  which are set based on the values in the message. As an exception, in case of
+    array and variant types, the first argument is an "input" argument that further specifies how the message
+    should be read. See the table below for a complete list of allowed arguments and their types. Note that,
+    if the basic type is a pointer (e.g., <type>const char *</type> in the case of a string), the argument is
+    a pointer to a pointer, and also the pointer value that is written is only borrowed and the contents must
+    be copied if they are to be used after the end of the messages lifetime.</para>
+
+    <para>Each argument may also be <constant>NULL</constant>, in which case the value is read and ignored.
+    </para>
 
     <table>
       <title>Item type specifiers</title>
       </tgroup>
     </table>
 
-    <para>If objects of the specified types are not present at the current position
-    in the message, an error is returned.
-    </para>
+    <para>If objects of the specified types are not present at the current position in the message, an error
+    is returned.</para>
 
     <para>The <function>sd_bus_message_readv()</function> is equivalent to the
-    <function>sd_bus_message_read()</function>, except that it is called with a
-    <literal>va_list</literal> instead of a variable number of arguments. This
-    function does not call the <function>va_end()</function> macro. Because it
-    invokes the <function>va_arg()</function> macro, the value of
-    <parameter>ap</parameter> is undefined after the call.</para>
+    <function>sd_bus_message_read()</function>, except that it is called with a <literal>va_list</literal>
+    instead of a variable number of arguments. This function does not call the <function>va_end()</function>
+    macro. Because it invokes the <function>va_arg()</function> macro, the value of <parameter>ap</parameter>
+    is undefined after the call.</para>
+
+    <para><function>sd_bus_message_peek_type()</function> determines the type of the next element in
+    <parameter>m</parameter> to be read by <function>sd_bus_message_read()</function> or similar functions.
+    On success, the type is stored in <parameter>type</parameter>, if it is not <constant>NULL</constant>.
+    If the type is a container type, the type of its elements is stored in <parameter>contents</parameter>,
+    if it is not <constant>NULL</constant>. If this function successfully determines the type of the next
+    element in <parameter>m</parameter>, it returns a positive integer. If there are no more elements to be
+    read, it returns zero.</para>
   </refsect1>
 
   <refsect1>
     <title>Return Value</title>
 
-    <para>On success, <function>sd_bus_message_read()</function> and
-    <function>sd_bus_message_readv()</function> return 0 or a positive integer. On failure, they return a
-    negative errno-style error code.</para>
+    <para>On success, these functions return a non-negative integer. On failure, they return a negative
+    errno-style error code.</para>
 
     <xi:include href="sd_bus_message_read_basic.xml" xpointer="errors" />
   </refsect1>
@@ -228,7 +238,8 @@ sd_bus_message_read(m, "a{is}", 3, &amp;i, &amp;s, &amp;j, &amp;t, &amp;k, &amp;
       <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_bus_message_read_basic</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_bus_message_skip</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_bus_message_append</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+      <citerefentry><refentrytitle>sd_bus_message_append</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_message_enter_container</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     </para>
   </refsect1>
 
index 26e8ebae60b737b7c0ae8537da00c39d9a03b7b8..7f9f9703b63c2a550dea320c6f5eaccb66e8c1da 100644 (file)
     <para>
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_bus_message_read</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+      <citerefentry><refentrytitle>sd_bus_message_read</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_message_read_strv</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     </para>
   </refsect1>
 
diff --git a/man/sd_bus_message_read_strv.xml b/man/sd_bus_message_read_strv.xml
new file mode 100644 (file)
index 0000000..a86bbe4
--- /dev/null
@@ -0,0 +1,90 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_bus_message_read_strv">
+
+  <refentryinfo>
+    <title>sd_bus_message_read_strv</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_bus_message_read_strv</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_bus_message_read_strv</refname>
+
+    <refpurpose>Access an array of strings in a message</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_message_read_strv</function></funcdef>
+        <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
+        <paramdef>char ***<parameter>l</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>sd_bus_message_read_strv()</function> gives access to an array of strings in message
+    <parameter>m</parameter>. The "read pointer" in the message must be right before an array of strings. On
+    success, a pointer to the <constant>NULL</constant>-terminated array of strings is returned in the output
+    parameter <parameter>l</parameter>. Note that ownership of this array is transferred to the caller.
+    Hence, the caller is responsible for freeing this array and its contents.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On success, <function>sd_bus_message_read_strv()</function> returns a non-negative integer. On
+    failure, it returns a negative errno-style error code.</para>
+
+    <refsect2>
+      <title>Errors</title>
+
+      <para>Returned errors may indicate the following problems:</para>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>-EINVAL</constant></term>
+
+          <listitem><para><parameter>m</parameter> or <parameter>l</parameter> are <constant>NULL</constant>.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-EPERM</constant></term>
+
+          <listitem><para>The message is not sealed.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-EBADMSG</constant></term>
+
+          <listitem><para>The message cannot be parsed.</para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_message_read</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
diff --git a/man/sd_bus_message_seal.xml b/man/sd_bus_message_seal.xml
new file mode 100644 (file)
index 0000000..03783d9
--- /dev/null
@@ -0,0 +1,106 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_bus_message_seal"
+          xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>sd_bus_message_seal</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_bus_message_seal</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_bus_message_seal</refname>
+
+    <refpurpose>Prepare a D-Bus message for transmission</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_message_seal</function></funcdef>
+        <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
+        <paramdef>uint64_t <parameter>cookie</parameter></paramdef>
+        <paramdef>uint64_t <parameter>timeout_usec</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>sd_bus_message_seal()</function> finishes the message <parameter>m</parameter>
+    and prepares it for transmission using
+    <citerefentry><refentrytitle>sd_bus_send</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+    <parameter>cookie</parameter> specifies the identifier used to match the message reply to its
+    corresponding request. <parameter>timeout_usec</parameter> specifies the maximum time in
+    microseconds to wait for a reply to arrive.</para>
+
+    <para>Note that in most scenarios, it's not necessary to call this function directly.
+    <citerefentry><refentrytitle>sd_bus_call</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_bus_call_async</refentrytitle><manvolnum>3</manvolnum></citerefentry> and
+    <citerefentry><refentrytitle>sd_bus_send</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    will seal any given messages if they have not been sealed yet.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On success, this function returns a non-negative integer. On failure, it returns a
+    negative errno-style error code.</para>
+
+    <refsect2>
+      <title>Errors</title>
+
+      <para>Returned errors may indicate the following problems:</para>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>-EINVAL</constant></term>
+
+          <listitem><para>The <parameter>m</parameter> parameter is <constant>NULL</constant>.
+          </para></listitem>
+        </varlistentry>
+
+         <varlistentry>
+          <term><constant>-EBADMSG</constant></term>
+
+          <listitem><para>The D-Bus message <parameter>m</parameter> has open containers.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENOMSG</constant></term>
+
+          <listitem><para>The D-Bus message <parameter>m</parameter> is a reply but its type
+          signature does not match the return type signature of its corresponding member in the
+          object vtable.</para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_call</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_call_async</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_send</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
index a4a732cfd152c7d97d0bee494801984350281328..8f3e8aeb8eb0e5ef9e646fb0a4a5f852812347eb 100644 (file)
@@ -50,7 +50,7 @@
   <refsect1>
     <title>Return Value</title>
 
-    <para>On success, theis functions return 0 or a positive integer. On failure, it returns a
+    <para>On success, this functions return 0 or a positive integer. On failure, it returns a
     negative errno-style error code.</para>
 
     <refsect2>
index ca3e466d7a95e3e15cba6b0126fdd77ff695fdc3..51da5ff3b89e4a81da4153e3ca33e85fc6c2ba14 100644 (file)
     member fields from <parameter>message</parameter> header. The return value will be
     <constant>NULL</constant> is <parameter>message</parameter> is <constant>NULL</constant> or the
     message is of a type that doesn't use those fields or the message doesn't have them set. See
-    <citerefentry><refentrytitle>sd_bus_message_new_method_call</refentrytitle><manvolnum>3</manvolnum></citerefentry>
-    and
+    <citerefentry><refentrytitle>sd_bus_message_new_method_call</refentrytitle><manvolnum>3</manvolnum></citerefentry> and
     <citerefentry><refentrytitle>sd_bus_message_set_destination</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     for more discussion of those values.</para>
 
-
     <para><function>sd_bus_message_set_sender()</function> sets the sender service name for the specified bus message
     object. The specified name must be a valid unique or well-known service name. This function is useful only for
     messages to send on direct connections as for connections to bus brokers the broker will fill in the destination
         <varlistentry>
           <term><constant>-EPERM</constant></term>
 
-          <listitem><para>For <function>sd_bus_message_set_destination</function> or
-          <function>sd_bus_message_set_sender</function>, the message is already
-          sealed.</para></listitem>
+          <listitem><para>For <function>sd_bus_message_set_destination()</function> and
+          <function>sd_bus_message_set_sender()</function>, the message is already sealed.</para>
+          </listitem>
         </varlistentry>
 
         <varlistentry>
index 6f22e82057f716882ed2dd071ce5e8afb4093139..43a94c83c78aad59926018b9d2b52e1b93803d06 100644 (file)
@@ -20,6 +20,8 @@
     <refname>sd_bus_message_get_expect_reply</refname>
     <refname>sd_bus_message_set_auto_start</refname>
     <refname>sd_bus_message_get_auto_start</refname>
+    <refname>sd_bus_message_set_allow_interactive_authorization</refname>
+    <refname>sd_bus_message_get_allow_interactive_authorization</refname>
 
     <refpurpose>Set and query bus message metadata</refpurpose>
   </refnamediv>
         <funcdef>int <function>sd_bus_message_get_auto_start</function></funcdef>
         <paramdef>sd_bus_message *<parameter>message</parameter></paramdef>
       </funcprototype>
-    </funcsynopsis>
 
+      <funcprototype>
+        <funcdef>int <function>sd_bus_message_set_allow_interactive_authorization</function></funcdef>
+        <paramdef>sd_bus_message *<parameter>message</parameter></paramdef>
+        <paramdef>int <parameter>b</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_message_get_allow_interactive_authorization</function></funcdef>
+        <paramdef>sd_bus_message *<parameter>message</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
   </refsynopsisdiv>
 
   <refsect1>
     <title>Description</title>
 
     <para><function>sd_bus_message_set_expect_reply()</function> sets or clears the
-    <constant>NO_REPLY_EXPECTED</constant> flag on the message <parameter>m</parameter>. This flag
-    matters only for method call messages and is used to specify that no method return or error
-    reply is expected. It is ignored for other types. Thus, for a method call message, calling
-    <programlisting>sd_bus_message_set_expect_reply(…, 0)</programlisting> sets the flag and
-    suppresses the reply.</para>
+    <constant>NO_REPLY_EXPECTED</constant> flag on the message <parameter>m</parameter>. This flag matters
+    only for method call messages and is used to specify that no method return or error reply is expected.
+    It is ignored for other types. Thus, for a method call message, calling
+    <programlisting>sd_bus_message_set_expect_reply(…, 0)</programlisting> sets the flag and suppresses the
+    reply.</para>
 
     <para><function>sd_bus_message_get_expect_reply()</function> checks if the
-    <constant>NO_REPLY_EXPECTED</constant> flag is set on the message <parameter>m</parameter>. It
-    will return positive if it is not set, and zero if it is.</para>
+    <constant>NO_REPLY_EXPECTED</constant> flag is set on the message <parameter>m</parameter>. It will
+    return positive if it is not set, and zero if it is.</para>
 
     <para><function>sd_bus_message_set_auto_start()</function> sets or clears the
-    <constant>NO_AUTO_START</constant> flag on the message <parameter>m</parameter>. When the flag
-    is set the bus must not launch an owner for the destination name in response to this message.
-    Calling
-    <programlisting>sd_bus_message_set_auto_start(…, 0)</programlisting> sets the flag.
-    </para>
+    <constant>NO_AUTO_START</constant> flag on the message <parameter>m</parameter>. When the flag is set,
+    the bus must not launch an owner for the destination name in response to this message. Calling
+    <programlisting>sd_bus_message_set_auto_start(…, 0)</programlisting> sets the flag.</para>
 
     <para><function>sd_bus_message_get_auto_start()</function> checks if the
-    <constant>NO_AUTO_START</constant> flag is set on the message <parameter>m</parameter>. It
-    will return positive if it is not set, and zero if it is.</para>
+    <constant>NO_AUTO_START</constant> flag is set on the message <parameter>m</parameter>. It will return
+    positive if it is not set, and zero if it is.</para>
+
+    <para><function>sd_bus_message_set_allow_interactive_authorization()</function> sets or clears the
+    <constant>ALLOW_INTERACTIVE_AUTHORIZATION</constant> flag on the message <parameter>m</parameter>.
+    Setting this flag informs the receiver that the caller is prepared to wait for interactive authorization
+    via polkit or a similar framework. Note that setting this flag does not guarantee that the receiver will
+    actually perform interactive authorization. Also, make sure to set a suitable message timeout when using
+    this flag since interactive authorization could potentially take a long time as it depends on user input.
+    If <parameter>b</parameter> is non-zero, the flag is set.</para>
+
+    <para><function>sd_bus_message_get_allow_interactive_authorization()</function> checks if the
+    <constant>ALLOW_INTERACTIVE_AUTHORIZATION</constant> flag is set on the message <parameter>m</parameter>.
+    It will return a positive integer if the flag is set. Otherwise, it returns zero.</para>
   </refsect1>
 
   <refsect1>
     <title>Return Value</title>
 
-    <para>On success, these functions return 0 or a positive integer. On failure, they return a
-    negative errno-style error code.</para>
+    <para>On success, these functions return a non-negative integer. On failure, they return a negative
+    errno-style error code.</para>
 
     <refsect2>
       <title>Errors</title>
         <varlistentry>
           <term><constant>-EINVAL</constant></term>
 
-          <listitem><para>The <parameter>message</parameter> parameter is
-          <constant>NULL</constant>.</para></listitem>
+          <listitem><para>The <parameter>message</parameter> parameter is <constant>NULL</constant>.
+          </para></listitem>
         </varlistentry>
 
         <varlistentry>
           <term><constant>-EPERM</constant></term>
 
-          <listitem><para>The message <parameter>message</parameter> is sealed
-          when trying to set a flag.</para>
+          <listitem>
+          <para>The message <parameter>message</parameter> is sealed when trying to set a flag.</para>
 
-          <para>The message <parameter>message</parameter> has wrong
-          type.</para>
+          <para>The message <parameter>message</parameter> has wrong type.</para>
           </listitem>
         </varlistentry>
       </variablelist>
       <citerefentry><refentrytitle>sd_bus_set_description</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     </para>
   </refsect1>
-
 </refentry>
index c12b65c983994249b4aec4f0eab478025f2cb660..340049fc972e384418d9633783a494ba56096762 100644 (file)
@@ -19,6 +19,7 @@
     <refname>sd_bus_negotiate_fds</refname>
     <refname>sd_bus_negotiate_timestamp</refname>
     <refname>sd_bus_negotiate_creds</refname>
+    <refname>sd_bus_get_creds_mask</refname>
 
     <refpurpose>Control feature negotiation on bus connections</refpurpose>
   </refnamediv>
         <paramdef>int <parameter>b</parameter></paramdef>
         <paramdef>uint64_t <parameter>mask</parameter></paramdef>
       </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_get_creds_mask</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>uint64_t *<parameter>mask</parameter></paramdef>
+      </funcprototype>
     </funcsynopsis>
   </refsynopsisdiv>
 
   <refsect1>
     <title>Description</title>
 
-    <para><function>sd_bus_negotiate_fds()</function> controls whether
-    file descriptor passing shall be negotiated for the specified bus
-    connection. It takes a bus object and a boolean, which, when true,
-    enables file descriptor passing, and, when false, disables
-    it. Note that not all transports and servers support file
-    descriptor passing. In particular, networked transports generally
-    do not support file descriptor passing. To find out whether file
-    descriptor passing is available after negotiation, use
+    <para><function>sd_bus_negotiate_fds()</function> controls whether file descriptor passing shall be
+    negotiated for the specified bus connection. It takes a bus object and a boolean, which, when true,
+    enables file descriptor passing, and, when false, disables it. Note that not all transports and servers
+    support file descriptor passing. In particular, networked transports generally do not support file
+    descriptor passing. To find out whether file descriptor passing is available after negotiation, use
     <citerefentry><refentrytitle>sd_bus_can_send</refentrytitle><manvolnum>3</manvolnum></citerefentry>
-    and pass <constant>SD_BUS_TYPE_UNIX_FD</constant>. Note that file
-    descriptor passing is always enabled for both sending and
-    receiving or for neither, but never only in one direction. By
-    default, file descriptor passing is negotiated for all
-    connections.</para>
-
-    <para><function>sd_bus_negotiate_timestamp()</function> controls whether implicit sender
-    timestamps shall be attached automatically to all incoming messages. Takes a bus object and a
-    boolean, which, when true, enables timestamping, and, when false, disables it.  Use
+    and pass <constant>SD_BUS_TYPE_UNIX_FD</constant>. Note that file descriptor passing is always enabled
+    for both sending and receiving or for neither, but never only in one direction. By default, file
+    descriptor passing is negotiated for all connections.</para>
+
+    <para><function>sd_bus_negotiate_timestamp()</function> controls whether implicit sender timestamps shall
+    be attached automatically to all incoming messages. Takes a bus object and a boolean, which, when true,
+    enables timestamping, and, when false, disables it.  Use
     <citerefentry><refentrytitle>sd_bus_message_get_monotonic_usec</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
     <citerefentry><refentrytitle>sd_bus_message_get_realtime_usec</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
     <citerefentry><refentrytitle>sd_bus_message_get_seqnum</refentrytitle><manvolnum>3</manvolnum></citerefentry>
-    to query the timestamps of incoming messages. If negotiation is disabled or not supported, these
-    calls will fail with <constant>-ENODATA</constant>. Note that currently no transports support
-    timestamping of messages. By default, message timestamping is not negotiated for
-    connections.</para>
+    to query the timestamps of incoming messages. If negotiation is disabled or not supported, these calls
+    will fail with <constant>-ENODATA</constant>. Note that currently no transports support timestamping of
+    messages. By default, message timestamping is not negotiated for connections.</para>
 
     <para><function>sd_bus_negotiate_creds()</function> controls whether and which implicit sender
-    credentials shall be attached automatically to all incoming messages. Takes a bus object and a
-    boolean indicating whether to enable or disable the credential parts encoded in the bit mask
-    value argument. Note that not all transports support attaching sender credentials to messages,
-    or do not support all types of sender credential parameters, or might suppress them under
-    certain circumstances for individual messages. Specifically, dbus1 only supports
-    <constant>SD_BUS_CREDS_UNIQUE_NAME</constant>. The sender credentials are suitable for
-    authorization decisions. By default, only <constant>SD_BUS_CREDS_WELL_KNOWN_NAMES</constant> and
-    <constant>SD_BUS_CREDS_UNIQUE_NAME</constant> are enabled. In fact, these two credential fields
-    are always sent along and cannot be turned off.</para>
-
-    <para>The <function>sd_bus_negotiate_fds()</function> function may
-    be called only before the connection has been started with
+    credentials shall be attached automatically to all incoming messages. Takes a bus object and a boolean
+    indicating whether to enable or disable the credential parts encoded in the bit mask value argument. Note
+    that not all transports support attaching sender credentials to messages, or do not support all types of
+    sender credential parameters, or might suppress them under certain circumstances for individual messages.
+    Specifically, dbus1 only supports <constant>SD_BUS_CREDS_UNIQUE_NAME</constant>. The sender credentials
+    are suitable for authorization decisions. By default, only
+    <constant>SD_BUS_CREDS_WELL_KNOWN_NAMES</constant> and <constant>SD_BUS_CREDS_UNIQUE_NAME</constant> are
+    enabled. In fact, these two credential fields are always sent along and cannot be turned off.</para>
+
+    <para><function>sd_bus_get_creds_mask()</function> returns the set of sender credentials that was
+    negotiated to be attached to all incoming messages in <parameter>mask</parameter>. This value is an
+    upper boundary only. Hence, always make sure to explicitly check which credentials are attached to a
+    specific message before using it.</para>
+
+    <para>The <function>sd_bus_negotiate_fds()</function> function may be called only before the connection
+    has been started with
     <citerefentry><refentrytitle>sd_bus_start</refentrytitle><manvolnum>3</manvolnum></citerefentry>. Both
-    <function>sd_bus_negotiate_timestamp()</function> and
-    <function>sd_bus_negotiate_creds()</function> may also be called
-    after a connection has been set up. Note that, when operating on a
-    connection that is shared between multiple components of the same
-    program (for example via
-    <citerefentry><refentrytitle>sd_bus_default</refentrytitle><manvolnum>3</manvolnum></citerefentry>),
-    it is highly recommended to only enable additional per message
-    metadata fields, but never disable them again, in order not to
-    disable functionality needed by other components.</para>
+    <function>sd_bus_negotiate_timestamp()</function> and <function>sd_bus_negotiate_creds()</function> may
+    also be called after a connection has been set up. Note that, when operating on a connection that is
+    shared between multiple components of the same program (for example via
+    <citerefentry><refentrytitle>sd_bus_default</refentrytitle><manvolnum>3</manvolnum></citerefentry>), it
+    is highly recommended to only enable additional per message metadata fields, but never disable them
+    again, in order not to disable functionality needed by other components.</para>
   </refsect1>
 
   <refsect1>
     <title>Return Value</title>
 
-    <para>On success, these functions return 0 or a
-    positive integer. On failure, they return a negative errno-style
-    error code.</para>
+    <para>On success, these functions return a non-negative integer. On failure, they return a negative
+    errno-style error code.</para>
 
     <refsect2>
       <title>Errors</title>
 
           <listitem><para>The bus connection has already been started.</para></listitem>
         </varlistentry>
+
+        <varlistentry>
+          <term><constant>-EINVAL</constant></term>
+
+          <listitem><para>An argument is invalid.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENOPKG</constant></term>
+
+          <listitem><para>The bus cannot be resolved.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ECHILD</constant></term>
+
+          <listitem><para>The bus was created in a different process.</para></listitem>
+        </varlistentry>
       </variablelist>
     </refsect2>
   </refsect1>
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_bus_start</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_bus_message_can_send</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_can_send</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_bus_message_get_monotonic_usec</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_bus_message_get_realtime_usec</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_bus_message_get_seqnum</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
index 7771a78d8b8552e708b4af24cf9fe5119a25c574..ceca3350fc44ce8f58907095135bdb46112219a1 100644 (file)
     or a related call, and then start the connection with
     <citerefentry><refentrytitle>sd_bus_start</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
 
-    <para>In most cases, it is a better idea to invoke
+    <para>In most cases, it is better to use
     <citerefentry><refentrytitle>sd_bus_default_user</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
     <citerefentry><refentrytitle>sd_bus_default_system</refentrytitle><manvolnum>3</manvolnum></citerefentry>
-    or related calls instead of the more low-level
-    <function>sd_bus_new()</function> and
-    <function>sd_bus_start()</function>. The higher-level calls not
-    only allocate a bus object but also start the connection to a
-    well-known bus in a single function invocation.</para>
+    or related calls instead of the more low-level <function>sd_bus_new()</function> and
+    <function>sd_bus_start()</function>. The higher-level functions not only allocate a bus object but also
+    start the connection to a well-known bus in a single function call.</para>
 
     <para><function>sd_bus_ref()</function> increases the reference
     counter of <parameter>bus</parameter> by one.</para>
diff --git a/man/sd_bus_query_sender_creds.xml b/man/sd_bus_query_sender_creds.xml
new file mode 100644 (file)
index 0000000..54cd817
--- /dev/null
@@ -0,0 +1,133 @@
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_bus_query_sender_creds" xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>sd_bus_query_sender_creds</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_bus_query_sender_creds</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_bus_query_sender_creds</refname>
+    <refname>sd_bus_query_sender_privilege</refname>
+
+    <refpurpose>Query bus message sender credentials/privileges</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_query_sender_creds</function></funcdef>
+        <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
+        <paramdef>uint64_t <parameter>mask</parameter></paramdef>
+        <paramdef>sd_bus_creds **<parameter>creds</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>sd_bus_error* <function>sd_bus_query_sender_privilege</function></funcdef>
+        <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
+        <paramdef>int <parameter>capability</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>sd_bus_query_sender_creds()</function> returns the credentials of the message
+    <parameter>m</parameter>. The <parameter>mask</parameter> parameter is a combo of
+    <constant index='false'>SD_BUS_CREDS_*</constant> flags that indicate which credential info the caller is
+    interested in. See
+    <citerefentry><refentrytitle>sd_bus_creds_new_from_pid</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    for a list of possible flags. First, this message checks if the requested credentials are attached to the
+    message itself. If not but the message contains the pid of the sender, this function tries to figure out
+    the missing credentials via other means (starting from the pid). If the pid isn't available but the
+    message has a sender, this function calls
+    <citerefentry><refentrytitle>sd_bus_get_name_creds</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    to get the requested credentials. If the message has no sender (when a direct connection is used), this
+    function calls
+    <citerefentry><refentrytitle>sd_bus_get_owner_creds</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    to get the requested credentials. On success, the requested credentials are stored in
+    <parameter>creds</parameter>. Ownership of the credentials object in <parameter>creds</parameter> is
+    transferred to the caller and should be freed by calling
+    <citerefentry><refentrytitle>sd_bus_creds_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+    </para>
+
+    <para><function>sd_bus_query_sender_privilege()</function> checks if the message <parameter>m</parameter>
+    has the requested privileges. If <parameter>capability</parameter> is a non-negative integer, this
+    function checks if the message has the capability with the same value. See
+    <citerefentry project='man-pages'><refentrytitle>capabilities</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+    for a list of capabilities. If <parameter>capability</parameter> is a negative integer, this function
+    returns whether the sender of the message runs as the same user as the receiver of the message, or if the
+    sender of the message runs as root and the receiver of the message does not run as root. On success and
+    if the message has the requested privileges, this function returns a positive integer. If the message
+    does not have the requested privileges, this function returns zero.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On success, these functions return a non-negative integer. On failure, they return a negative
+    errno-style error code.</para>
+
+    <refsect2>
+      <title>Errors</title>
+
+      <para>Returned errors may indicate the following problems:</para>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>-EINVAL</constant></term>
+
+          <listitem><para>The message <parameter>m</parameter> or an output parameter is
+          <constant>NULL</constant>.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENOTCONN</constant></term>
+
+          <listitem><para>The bus of <parameter>m</parameter> is not connected.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ECHILD</constant></term>
+
+          <listitem><para>The bus of <parameter>m</parameter> was created in a different process.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-EPERM</constant></term>
+
+          <listitem><para>The message <parameter>m</parameter> is not sealed.</para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_creds_new_from_pid</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_get_name_creds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_get_owner_creds</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_creds_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry project='man-pages'><refentrytitle>capabilities</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+</refentry>
index 5a6cef6badfae9bd5ac13e808226e52afea02c4b..dcf9ee299fbe79d53b134d3fd75ccbd07610efe6 100644 (file)
   <refnamediv>
     <refname>sd_bus_reply_method_error</refname>
     <refname>sd_bus_reply_method_errorf</refname>
+    <refname>sd_bus_reply_method_errorfv</refname>
     <refname>sd_bus_reply_method_errno</refname>
     <refname>sd_bus_reply_method_errnof</refname>
+    <refname>sd_bus_reply_method_errnofv</refname>
 
-    <refpurpose>Reply with an error to a method call</refpurpose>
+    <refpurpose>Reply with an error to a D-Bus method call</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
         <paramdef>sd_bus_message *<parameter>call</parameter></paramdef>
         <paramdef>const char *<parameter>name</parameter></paramdef>
         <paramdef>const char *<parameter>format</parameter></paramdef>
-        <paramdef>…</paramdef>
+        <paramdef>...</paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int sd_bus_reply_method_errorfv</funcdef>
+        <paramdef>sd_bus_message *<parameter>call</parameter></paramdef>
+        <paramdef>const char *<parameter>name</parameter></paramdef>
+        <paramdef>const char *<parameter>format</parameter></paramdef>
+        <paramdef>va_list <parameter>ap</parameter></paramdef>
       </funcprototype>
 
       <funcprototype>
         <paramdef>sd_bus_message *<parameter>call</parameter></paramdef>
         <paramdef>int <parameter>error</parameter></paramdef>
         <paramdef>const char *<parameter>format</parameter></paramdef>
-        <paramdef>…</paramdef>
+        <paramdef>...</paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int sd_bus_reply_method_errnofv</funcdef>
+        <paramdef>sd_bus_message *<parameter>call</parameter></paramdef>
+        <paramdef>int <parameter>error</parameter></paramdef>
+        <paramdef>const char *<parameter>format</parameter></paramdef>
+        <paramdef>va_list <parameter>ap</parameter></paramdef>
       </funcprototype>
     </funcsynopsis>
   </refsynopsisdiv>
   <refsect1>
     <title>Description</title>
 
-    <para>The <function>sd_bus_reply_method_error()</function> function sends an
-    error reply to the <parameter>call</parameter> message. The error structure
-    <parameter>e</parameter> specifies the error to send, and is used as described in
-    <citerefentry><refentrytitle>sd_bus_message_new_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
-    If no reply is expected to <parameter>call</parameter>, this function returns
-    success without sending reply.</para>
+    <para>The <function>sd_bus_reply_method_error()</function> function sends an error reply to the
+    <parameter>call</parameter> message. The error structure <parameter>e</parameter> specifies the
+    error to send, and is used as described in
+    <citerefentry><refentrytitle>sd_bus_message_new_method_error</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+    If no reply is expected to <parameter>call</parameter>, this function succeeds without sending a
+    reply.</para>
 
     <para>The <function>sd_bus_reply_method_errorf()</function> is to
     <function>sd_bus_reply_method_error()</function> what
   <refsect1>
     <title>Return Value</title>
 
-    <para>These functions return 0 if the error reply was successfully sent or if
-    none was expected, and a negative errno-style error code otherwise.</para>
+    <para>This function returns a non-negative integer if the error reply was successfully sent or
+    if <parameter>call</parameter> does not expect a reply. On failure, it returns a negative
+    errno-style error code.</para>
 
     <refsect2>
       <title>Errors</title>
         <varlistentry>
           <term><constant>-EINVAL</constant></term>
 
-          <listitem><para>The call message <parameter>call</parameter> is
+          <listitem><para>The input parameter <parameter>call</parameter> is
           <constant>NULL</constant>.</para>
 
-          <para>Message <parameter>call</parameter> is not a method call message.
-          </para>
+          <para>Message <parameter>call</parameter> is not a method call message.</para>
 
           <para>Message <parameter>call</parameter> is not attached to a bus.</para>
 
-          <para>The error <parameter>error</parameter> parameter to
+          <para>The error parameter <parameter>error</parameter> to
           <function>sd_bus_reply_method_error</function> is not set, see
           <citerefentry><refentrytitle>sd_bus_error_is_set</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
           </para>
         </varlistentry>
       </variablelist>
 
-      <para>In addition, any error message returned by
+      <para>In addition, any error returned by
       <citerefentry><refentrytitle>sd_bus_send</refentrytitle><manvolnum>1</manvolnum></citerefentry>
       may be returned.</para>
     </refsect2>
diff --git a/man/sd_bus_reply_method_return.xml b/man/sd_bus_reply_method_return.xml
new file mode 100644 (file)
index 0000000..a6052c6
--- /dev/null
@@ -0,0 +1,121 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_bus_reply_method_return"
+          xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>sd_bus_reply_method_return</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_bus_reply_method_return</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_bus_reply_method_return</refname>
+    <refname>sd_bus_reply_method_returnv</refname>
+
+    <refpurpose>Reply to a D-Bus method call</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>int sd_bus_reply_method_return</funcdef>
+        <paramdef>sd_bus_message *<parameter>call</parameter></paramdef>
+        <paramdef>const char *<parameter>types</parameter></paramdef>
+        <paramdef>...</paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int sd_bus_reply_method_returnv</funcdef>
+        <paramdef>sd_bus_message *<parameter>call</parameter></paramdef>
+        <paramdef>const char *<parameter>types</parameter></paramdef>
+        <paramdef>va_list <parameter>ap</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>sd_bus_reply_method_return()</function> sends a reply to the
+    <parameter>call</parameter> message. The type string <parameter>types</parameter> and the
+    arguments that follow it must adhere to the format described in
+    <citerefentry><refentrytitle>sd_bus_message_append</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+    If no reply is expected to <parameter>call</parameter>, this function succeeds without sending a
+    reply.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On success, this function returns a non-negative integer. On failure, it returns a
+    negative errno-style error code.</para>
+
+    <refsect2>
+      <title>Errors</title>
+
+      <para>Returned errors may indicate the following problems:</para>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>-EINVAL</constant></term>
+
+          <listitem><para>The input parameter <parameter>call</parameter> is
+          <constant>NULL</constant>.</para>
+
+          <para>Message <parameter>call</parameter> is not a method call message.
+          </para>
+
+          <para>Message <parameter>call</parameter> is not attached to a bus.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-EPERM</constant></term>
+
+          <listitem><para>Message <parameter>call</parameter> has been sealed.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENOTCONN</constant></term>
+
+          <listitem><para>The bus to which message <parameter>call</parameter> is attached is not
+          connected.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENOMEM</constant></term>
+
+          <listitem><para>Memory allocation failed.</para></listitem>
+        </varlistentry>
+      </variablelist>
+
+      <para>In addition, any error returned by
+      <citerefentry><refentrytitle>sd_bus_send</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+      may be returned.</para>
+    </refsect2>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_message_new_method_return</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
index 7229ef517a577051b5bfeaf78e107de75b6f1117..f8a49c006b242900886302050b24c8784c0da0fa 100644 (file)
@@ -28,6 +28,8 @@
     <funcsynopsis>
       <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
 
+      <xi:include href="sd_bus_add_match.xml" xpointer="sd_bus_message_handler_t"/>
+
       <funcprototype>
         <funcdef>int <function>sd_bus_request_name</function></funcdef>
         <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
diff --git a/man/sd_bus_send.xml b/man/sd_bus_send.xml
new file mode 100644 (file)
index 0000000..2cdf436
--- /dev/null
@@ -0,0 +1,149 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_bus_send"
+          xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>sd_bus_send</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_bus_send</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_bus_send</refname>
+    <refname>sd_bus_send_to</refname>
+
+    <refpurpose>Queue a D-Bus message for transfer</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_send</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
+        <paramdef>uint64_t *<parameter>cookie</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_send_to</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>sd_bus_message *<parameter>m</parameter></paramdef>
+        <paramdef>const char *<parameter>destination</parameter></paramdef>
+        <paramdef>uint64_t *<parameter>cookie</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>sd_bus_send()</function> queues the bus message object <parameter>m</parameter> for
+    transfer. If <parameter>bus</parameter> is <constant>NULL</constant>, the bus that
+    <parameter>m</parameter> is attached to is used. <parameter>bus</parameter> only needs to be set when the
+    message is sent to a different bus than the one it's attached to, for example when forwarding messages.
+    If the output parameter <parameter>cookie</parameter> is not <constant>NULL</constant>, it is set to the
+    message identifier. This value can later be used to match incoming replies to their corresponding
+    messages. If <parameter>cookie</parameter> is set to <constant>NULL</constant> and the message is not
+    sealed, <function>sd_bus_send()</function> assumes the message <parameter>m</parameter> doesn't expect a
+    reply and adds the necessary headers to indicate this.</para>
+
+    <para>Note that in most scenarios, <function>sd_bus_send()</function> should not be called
+    directly. Instead, use higher level functions such as
+    <citerefentry><refentrytitle>sd_bus_call_method</refentrytitle><manvolnum>3</manvolnum></citerefentry> and
+    <citerefentry><refentrytitle>sd_bus_reply_method_return</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    which call <function>sd_bus_send()</function> internally.</para>
+
+    <para><function>sd_bus_send_to()</function> is a shorthand for sending a message to a specific
+    destination. It's main use case is to simplify sending unicast signal messages (signals that only have a
+    single receiver). It's behavior is similar to calling
+    <citerefentry><refentrytitle>sd_bus_message_set_destination</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    followed by calling <function>sd_bus_send()</function>.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On success, these functions return a non-negative integer. On failure, they return a negative
+    errno-style error code.</para>
+
+    <refsect2 id='errors'>
+      <title>Errors</title>
+
+      <para>Returned errors may indicate the following problems:</para>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>-EINVAL</constant></term>
+
+          <listitem><para>The input parameter <parameter>m</parameter> is <constant>NULL</constant>.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-EOPNOTSUPP</constant></term>
+
+          <listitem><para>The bus connection does not support sending file descriptors.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ECHILD</constant></term>
+
+          <listitem><para>The bus connection was allocated in a parent process and is being reused in a child
+          process after <function>fork()</function>.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENOBUFS</constant></term>
+
+          <listitem><para>The bus connection's write queue is full.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENOTCONN</constant></term>
+
+          <listitem><para>The input parameter <parameter>bus</parameter> is
+          <constant>NULL</constant> or the bus is not connected.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ECONNRESET</constant></term>
+
+          <listitem><para>The bus connection was closed while waiting for the response.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENOMEM</constant></term>
+
+          <listitem><para>Memory allocation failed.</para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_call_method</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_message_set_destination</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_reply_method_return</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
diff --git a/man/sd_bus_set_address.xml b/man/sd_bus_set_address.xml
new file mode 100644 (file)
index 0000000..8404da5
--- /dev/null
@@ -0,0 +1,188 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_bus_set_address"
+          xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>sd_bus_set_address</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_bus_set_address</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_bus_set_address</refname>
+    <refname>sd_bus_get_address</refname>
+    <refname>sd_bus_set_exec</refname>
+
+    <refpurpose>Set or query the address of the bus connection</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_set_address</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>const char *<parameter>address</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_get_address</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>const char **<parameter>address</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_set_exec</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>const char *<parameter>path</parameter></paramdef>
+        <paramdef>char *const *<parameter>argv</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>sd_bus_set_address()</function> configures a list of addresses of bus brokers to try to
+    connect to from a subsequent
+    <citerefentry><refentrytitle>sd_bus_start</refentrytitle><manvolnum>3</manvolnum></citerefentry> call.
+    The argument is a <literal>;</literal>-separated list of addresses to try. Each item must be one of the
+    following:
+    </para>
+
+    <itemizedlist>
+      <listitem>
+        <para>A unix socket address specified as
+        <literal>unix:guid=<replaceable>guid</replaceable>,path=<replaceable>path</replaceable></literal> or
+        <literal>unix:guid=<replaceable>guid</replaceable>,abstract=<replaceable>path</replaceable></literal>.
+        Exactly one of the <varname>path=</varname> and <varname>abstract=</varname> keys must be present,
+        while <varname>guid=</varname> is optional.</para>
+      </listitem>
+
+      <listitem>
+        <para>A TCP socket address specified as
+        <literal>tcp:[guid=<replaceable>guid</replaceable>,][host=<replaceable>host</replaceable>][,port=<replaceable>port</replaceable>][,family=<replaceable>family</replaceable>]</literal>.
+        One or both of the <varname>host=</varname> and <varname>port=</varname> keys must be present, while
+        the rest is optional. <replaceable>family</replaceable> may be either <option>ipv4</option> or
+        <option>ipv6</option>.</para>
+      </listitem>
+
+      <listitem>
+        <para>An executable to spawn specified as
+        <literal>unixexec:guid=<replaceable>guid</replaceable>,path=<replaceable>path</replaceable>,argv1=<replaceable>argument</replaceable>,argv2=<replaceable>argument</replaceable>,...</literal>.
+        The <varname>path=</varname> key must be present, while <varname>guid=</varname> is optional.</para>
+      </listitem>
+
+      <listitem>
+        <para>A machine (container) to connect to specified as
+        <literal>x-machine-unix:guid=<replaceable>guid</replaceable>,machine=<replaceable>machine</replaceable>,pid=<replaceable>pid</replaceable></literal>.
+        Exactly one of the <varname>machine=</varname> and <varname>pid=</varname> keys must be present,
+        while <varname>guid=</varname> is optional. <parameter>machine</parameter> is the name of a local
+        container. See
+        <citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+        more information about the "machine" concept. <literal>machine=.host</literal> may be used to specify
+        the host machine. A connection to the standard system bus socket inside of the specified machine will
+        be created.</para>
+      </listitem>
+    </itemizedlist>
+
+    <para>In all cases, parameter <parameter>guid</parameter> is an identifier of the remote peer, in the
+    syntax accepted by
+    <citerefentry><refentrytitle>sd_id128_from_string</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+    If specified, the identifier returned by the peer after the connection is established will be checked and
+    the connection will be rejected in case of a mismatch.</para>
+
+    <para>Note that the addresses passed to <function>sd_bus_set_address()</function> may not be verified
+    immediately. If they are invalid, an error may be returned e.g. from a subsequent call to
+    <citerefentry><refentrytitle>sd_bus_start</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+    </para>
+
+    <para><function>sd_bus_get_address()</function> returns any previously set addresses. In addition to
+    being explicitly set by <function>sd_bus_set_address()</function>, the address will also be set
+    automatically by
+    <citerefentry><refentrytitle>sd_bus_open</refentrytitle><manvolnum>3</manvolnum></citerefentry> and
+    similar calls, based on environment variables or built-in defaults.</para>
+
+    <para><function>sd_bus_set_exec</function> is a shorthand function for setting a
+    <literal>unixexec</literal> address that spawns the given executable with the given arguments.
+    If <parameter>argv</parameter> is <constant>NULL</constant>, the given executable is spawned
+    without any extra arguments.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On success, these functions return a non-negative integer. On failure, they return a negative
+    errno-style error code.</para>
+
+    <refsect2>
+      <title>Errors</title>
+
+      <para>Returned errors may indicate the following problems:</para>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>-EINVAL</constant></term>
+
+          <listitem><para>The input parameters <parameter>bus</parameter> or <parameter>address</parameter> are <constant>NULL</constant>.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENOPKG</constant></term>
+
+          <listitem><para>The bus object <parameter>bus</parameter> could not be resolved.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-EPERM</constant></term>
+
+          <listitem><para>The input parameter <parameter>bus</parameter> is in a wrong state
+          (<function>sd_bus_set_address()</function> may only be called once on a newly-created bus object).</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ECHILD</constant></term>
+
+          <listitem><para>The bus object <parameter>bus</parameter> was created in a different
+          process.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENODATA</constant></term>
+
+          <listitem><para>The bus object <parameter>bus</parameter> has no address configured.</para>
+          </listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_start</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-machined.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
index 751fc0a729e672e23a6cf9726b06f641f8472ec3..64ca35644398cd21e507e30420140e7286964452 100644 (file)
@@ -20,7 +20,8 @@
     <refname>sd_bus_set_close_on_exit</refname>
     <refname>sd_bus_get_close_on_exit</refname>
 
-    <refpurpose>Control whether to close the bus connection during the event loop exit phase</refpurpose>
+    <refpurpose>Control whether to close the bus connection during the event loop exit phase
+    </refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
   <refsect1>
     <title>Description</title>
 
-    <para><function>sd_bus_set_close_on_exit()</function> may be used to enable or disable whether the bus connection
-    is automatically flushed (as in
-    <citerefentry><refentrytitle>sd_bus_flush</refentrytitle><manvolnum>3</manvolnum></citerefentry>) and closed (as in
-    <citerefentry><refentrytitle>sd_bus_close</refentrytitle><manvolnum>3</manvolnum></citerefentry>) during the exit
-    phase of the event loop. This logic only applies to bus connections that are attached to an
-    <citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry> event loop, see
-    <citerefentry><refentrytitle>sd_bus_attach_event</refentrytitle><manvolnum>3</manvolnum></citerefentry>. By default
-    this mechanism is enabled and makes sure that any pending messages that have not been written to the bus connection
-    are written out when the event loop is shutting down. In some cases this behaviour is not desirable, for example
-    when the bus connection shall remain usable until after the event loop exited. If <parameter>b</parameter> is
-    true, the feature is enabled (which is the default), otherwise disabled.</para>
-
-    <para><function>sd_bus_get_close_on_exit()</function> may be used to query the current setting of this feature. It
-    returns zero when the feature is disabled, and positive if enabled.</para>
+    <para><function>sd_bus_set_close_on_exit()</function> may be used to enable or disable whether
+    the bus connection is automatically flushed (as in
+    <citerefentry><refentrytitle>sd_bus_flush</refentrytitle><manvolnum>3</manvolnum></citerefentry>)
+    and closed (as in
+    <citerefentry><refentrytitle>sd_bus_close</refentrytitle><manvolnum>3</manvolnum></citerefentry>)
+    during the exit phase of the event loop. This logic only applies to bus connections that are
+    attached to an
+    <citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    event loop, see
+    <citerefentry><refentrytitle>sd_bus_attach_event</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+    By default this mechanism is enabled and makes sure that any pending messages that have not been
+    written to the bus connection are written out when the event loop is shutting down. In some
+    cases this behaviour is not desirable, for example when the bus connection shall remain usable
+    until after the event loop exited. If <parameter>b</parameter> is true, the feature is enabled
+    (which is the default), otherwise disabled.</para>
+
+    <para><function>sd_bus_get_close_on_exit()</function> may be used to query the current setting
+    of this feature. It returns zero when the feature is disabled, and positive if enabled.</para>
   </refsect1>
 
   <refsect1>
     <title>Return Value</title>
 
-    <para>On success, <function>sd_bus_set_close_on_exit()</function> returns 0 or a positive integer. On failure, it returns a negative errno-style
-    error code.</para>
+    <para>On success, <function>sd_bus_set_close_on_exit()</function> returns a non-negative
+    integer. On failure, it returns a negative errno-style error code.</para>
 
-    <para><function>sd_bus_get_close_on_exit()</function> returns 0 if the feature is currently turned off or a
-    positive integer if it is on. On failure, it returns a negative errno-style error code.</para>
+    <para><function>sd_bus_get_close_on_exit()</function> returns 0 if the feature is currently
+    disabled or a positive integer if it is enabled. On failure, it returns a negative errno-style
+    error code.</para>
 
     <refsect2>
       <title>Errors</title>
@@ -78,7 +84,8 @@
         <varlistentry>
           <term><constant>-ECHILD</constant></term>
 
-          <listitem><para>The bus connection has been created in a different process.</para></listitem>
+          <listitem><para>The bus connection was created in a different process.</para>
+          </listitem>
         </varlistentry>
       </variablelist>
     </refsect2>
       <citerefentry><refentrytitle>sd_event_add_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     </para>
   </refsect1>
-
 </refentry>
index edb0df205a88ba1e08de355657c5d125e65e4d70..b025112b6393e7503a8a86922344c6b0b4732c20 100644 (file)
@@ -20,7 +20,7 @@
     <refname>sd_bus_set_connected_signal</refname>
     <refname>sd_bus_get_connected_signal</refname>
 
-    <refpurpose>Control emmission of local connection establishment signal on bus connections</refpurpose>
+    <refpurpose>Control emission of local connection establishment signal on bus connections</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
index 3c5580e27cace84a2c879154dbbfaef9220b9404..bd3ec78864b251f63814fe8a44bfabc1f25f5182 100644 (file)
     <refname>sd_bus_set_description</refname>
     <refname>sd_bus_get_description</refname>
     <refname>sd_bus_set_anonymous</refname>
+    <refname>sd_bus_is_anonymous</refname>
     <refname>sd_bus_set_trusted</refname>
+    <refname>sd_bus_is_trusted</refname>
     <refname>sd_bus_set_allow_interactive_authorization</refname>
     <refname>sd_bus_get_allow_interactive_authorization</refname>
+    <refname>sd_bus_get_scope</refname>
+    <refname>sd_bus_get_tid</refname>
+    <refname>sd_bus_get_unique_name</refname>
 
     <refpurpose>Set or query properties of a bus object</refpurpose>
   </refnamediv>
         <paramdef>int <parameter>b</parameter></paramdef>
       </funcprototype>
 
+      <funcprototype>
+        <funcdef>int <function>sd_bus_is_anonymous</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+      </funcprototype>
+
       <funcprototype>
         <funcdef>int <function>sd_bus_set_trusted</function></funcdef>
         <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
         <paramdef>int <parameter>b</parameter></paramdef>
       </funcprototype>
 
+      <funcprototype>
+        <funcdef>int <function>sd_bus_is_trusted</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+      </funcprototype>
+
       <funcprototype>
         <funcdef>int <function>sd_bus_set_allow_interactive_authorization</function></funcdef>
         <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
         <funcdef>int <function>sd_bus_get_allow_interactive_authorization</function></funcdef>
         <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
       </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_get_scope</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>const char **<parameter>scope</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_get_tid</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>pid_t *<parameter>tid</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_get_unique_name</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>const char **<parameter>unique</parameter></paramdef>
+      </funcprototype>
     </funcsynopsis>
   </refsynopsisdiv>
 
   <refsect1>
     <title>Description</title>
 
-    <para><function>sd_bus_set_description()</function> sets the description string
-    that is used in logging to the specified string. The string is copied internally
-    and freed when the bus object is deallocated. The
-    <parameter>description</parameter> argument may be <constant>NULL</constant>, in
-    which case the description is unset. This function must be called before the bus
-    has been started.</para>
+    <para><function>sd_bus_set_description()</function> sets the description string that is used in
+    logging to the specified string. The string is copied internally and freed when the bus object
+    is deallocated. The <parameter>description</parameter> argument may be
+    <constant>NULL</constant>, in which case the description is unset. This function must be called
+    before the bus is started.</para>
 
-    <para><function>sd_bus_get_description()</function> returns a description string
-    in <parameter>description</parameter>. This string may have been previously set
-    with <function>sd_bus_set_description()</function> or
+    <para><function>sd_bus_get_description()</function> returns a description string in
+    <parameter>description</parameter>. This string may have been previously set with
+    <function>sd_bus_set_description()</function> or
     <citerefentry><refentrytitle>sd_bus_open_with_description</refentrytitle><manvolnum>3</manvolnum></citerefentry>
-    or similar. If not set this way, a default string like <literal>system</literal>
-    or <literal>user</literal> will be returned for the system or user buses,
-    and <constant>NULL</constant> otherwise.</para>
+    or similar. If not set this way, a default string like <literal>system</literal> or
+    <literal>user</literal> will be returned for the system or user buses, and
+    <constant>NULL</constant> otherwise.</para>
 
-    <para><function>sd_bus_set_anonymous()</function> enables or disables "anonymous
-    authentication", i.e. lack of authentication, of the bus peer. This function must
-    be called before the bus has been started. See the <ulink
-    url="view-source:https://dbus.freedesktop.org/doc/dbus-specification.html#auth-mechanisms">Authentication
-    Mechanisms</ulink> section of the D-Bus specification for details.</para>
+    <para><function>sd_bus_set_anonymous()</function> enables or disables "anonymous authentication",
+    i.e. lack of authentication, of the bus peer. This function must be called before the bus is
+    started. See the
+    <ulink url="view-source:https://dbus.freedesktop.org/doc/dbus-specification.html#auth-mechanisms">
+    Authentication Mechanisms</ulink> section of the D-Bus specification for details.</para>
+
+    <para><function>sd_bus_is_anonymous()</function> returns true if the bus connection allows
+    anonymous authentication (in the sense described in previous paragraph).</para>
 
     <para><function>sd_bus_set_trusted()</function> sets the "trusted" state on the
-    <parameter>bus</parameter> object. If true, all connections on the bus are
-    trusted and access to all privileged and unprivileged methods is granted.  This
-    function must be called before the bus has been started.</para>
+    <parameter>bus</parameter> object. If true, all connections on the bus are trusted and access to
+    all privileged and unprivileged methods is granted. This function must be called before the bus
+    is started.</para>
+
+    <para><function>sd_bus_is_trusted()</function> returns true if the bus connection is trusted (in
+    the sense described in previous paragraph).</para>
 
-    <para><function>sd_bus_set_allow_interactive_authorization()</function>
-    enables or disables interactive authorization for method calls. If true,
-    messages are marked with the
+    <para><function>sd_bus_set_allow_interactive_authorization()</function> enables or disables
+    interactive authorization for method calls. If true, messages are marked with the
     <constant>ALLOW_INTERACTIVE_AUTHORIZATION</constant> flag specified by the
-    <ulink
-    url="view-source:https://dbus.freedesktop.org/doc/dbus-specification.html">D-Bus</ulink>
-    specification, informing the receiving side that the caller is prepared to
-    wait for interactive authorization, which might take a considerable time to
-    complete. If this flag is set, the user may be queried for passwords or
-    confirmation via <ulink
-    url="http://www.freedesktop.org/wiki/Software/polkit">polkit</ulink> or a
-    similar framework.</para>
-
-    <para><function>sd_bus_get_allow_interactive_authorization()</function> returns
-    true if interactive authorization is allowed and false if not.</para>
+    <ulink url="view-source:https://dbus.freedesktop.org/doc/dbus-specification.html">D-Bus</ulink>
+    specification, informing the receiving side that the caller is prepared to wait for interactive
+    authorization, which might take a considerable time to complete. If this flag is set, the user
+    may be queried for passwords or confirmation via
+    <ulink url="http://www.freedesktop.org/wiki/Software/polkit">polkit</ulink> or a similar
+    framework.</para>
+
+    <para><function>sd_bus_get_allow_interactive_authorization()</function> returns true if
+    interactive authorization is allowed and false if not.</para>
+
+    <para><function>sd_bus_get_scope()</function> stores the scope of the given bus object in
+    <parameter>scope</parameter>. The scope of the system bus is <literal>system</literal>. The
+    scope of a user session bus is <literal>user</literal>. If the given bus object is not the
+    system or a user session bus, <function>sd_bus_get_scope()</function> returns an error.</para>
+
+    <para><function>sd_bus_get_tid()</function> stores the kernel thread id of the thread associated
+    with the given bus object in <parameter>tid</parameter>. If <parameter>bus</parameter> is a
+    default bus object obtained by calling one of the functions of the
+    <citerefentry><refentrytitle>sd_bus_default</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    family of functions, it stores the thread id of the thread the bus object was created in.
+    Otherwise, if the bus object is attached to an event loop, it stores the thread id of the
+    thread the event loop object was created in. If <parameter>bus</parameter> is not a default bus
+    object and is not attached to an event loop, <function>sd_bus_get_tid()</function> returns an
+    error.</para>
+
+    <para><function>sd_bus_get_unique_name()</function> stores the unique name of the bus object on
+    the bus in <parameter>unique</parameter>. See
+    <ulink url="https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-names-bus">
+    The D-Bus specification</ulink> for more information on bus names. Note that the caller does not
+    own the string stored in <parameter>unique</parameter> and should not free it.</para>
   </refsect1>
 
   <refsect1>
     <title>Return Value</title>
 
-    <para>On success, these functions return 0 or a positive integer. On failure, they return a negative
-    errno-style error code.</para>
+    <para>On success, these functions return a non-negative integer. On failure, they return a
+    negative errno-style error code.</para>
 
     <refsect2>
       <title>Errors</title>
 
           <listitem><para>Memory allocation failed.</para></listitem>
         </varlistentry>
-      </variablelist>
 
+        <varlistentry>
+          <term><constant>-ENODATA</constant></term>
+
+          <listitem><para>The bus object passed to <function>sd_bus_get_scope()</function> was not a
+          system or user session bus.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENXIO</constant></term>
+
+          <listitem><para>The bus object passed to <function>sd_bus_get_tid()</function> was not a
+          default bus object and is not attached to an event loop.</para></listitem>
+        </varlistentry>
+      </variablelist>
     </refsect2>
   </refsect1>
 
diff --git a/man/sd_bus_set_exit_on_disconnect.xml b/man/sd_bus_set_exit_on_disconnect.xml
new file mode 100644 (file)
index 0000000..8bd904b
--- /dev/null
@@ -0,0 +1,114 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_bus_set_exit_on_disconnect"
+          xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>sd_bus_set_exit_on_disconnect</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_bus_set_exit_on_disconnect</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_bus_set_exit_on_disconnect</refname>
+    <refname>sd_bus_get_exit_on_disconnect</refname>
+
+    <refpurpose>Control the exit behavior when the bus object disconnects</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_set_exit_on_disconnect</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>int <parameter>b</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_get_exit_on_disconnect</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>sd_bus_set_exit_on_disconnect()</function> may be used to configure the exit
+    behavior when the given bus object disconnects. If <parameter>b</parameter> is zero, no special
+    logic is executed when the bus object disconnects. If <parameter>b</parameter> is non-zero, the
+    behavior on disconnect depends on whether the bus object is attached to an event loop or not. If
+    the bus object is attached to an event loop (see
+    <citerefentry><refentrytitle>sd_bus_attach_event</refentrytitle><manvolnum>3</manvolnum></citerefentry>),
+    the event loop is closed when the bus object disconnects (as if calling
+    <citerefentry><refentrytitle>sd_event_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>).
+    Otherwise,
+    <citerefentry project='man-pages'><refentrytitle>exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    is called. The exit code passed to <function>sd_event_exit()</function> and
+    <function>exit()</function> is <constant>EXIT_FAILURE</constant>. If the bus object has already
+    disconnected when enabling the exit behavior, the exit behavior is executed immediately. By
+    default, the exit behavior is disabled.</para>
+
+    <para><function>sd_bus_get_exit_on_disconnect()</function> returns whether the exit on
+    disconnect behavior is enabled for the given bus object.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On success, <function>sd_bus_set_exit_on_disconnect()</function> returns a non-negative
+    integer. On failure, it returns a negative errno-style error code.</para>
+
+    <para><function>sd_bus_get_exit_on_disconnect()</function> returns a positive integer if the
+    exit on disconnect behavior is enabled. Otherwise, it returns zero.</para>
+
+    <refsect2>
+      <title>Errors</title>
+
+      <para>Returned errors may indicate the following problems:</para>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>-EINVAL</constant></term>
+
+          <listitem><para>A required parameter was <constant>NULL</constant>.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENOPKG</constant></term>
+
+          <listitem><para>The bus object could not be resolved.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ECHILD</constant></term>
+
+          <listitem><para>The bus connection was created in a different process.</para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_attach_event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd-event</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_event_exit</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+</refentry>
diff --git a/man/sd_bus_set_method_call_timeout.xml b/man/sd_bus_set_method_call_timeout.xml
new file mode 100644 (file)
index 0000000..006020a
--- /dev/null
@@ -0,0 +1,103 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_bus_set_method_call_timeout" xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>sd_bus_set_method_call_timeout</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_bus_set_method_call_timeout</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_bus_set_method_call_timeout</refname>
+    <refname>sd_bus_get_method_call_timeout</refname>
+
+    <refpurpose>Set or query the default D-Bus method call timeout of a bus object</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_set_method_call_timeout</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>uint64_t <parameter>usec</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_get_method_call_timeout</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>uint64_t *<parameter>ret</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>sd_bus_set_method_call_timeout()</function> sets the default D-Bus method call
+    timeout of <parameter>bus</parameter> to <parameter>usec</parameter> microseconds.</para>
+
+    <para><function>sd_bus_get_method_call_timeout()</function> queries the default D-Bus method
+    call timeout of <parameter>bus</parameter>. If no method call timeout was set using
+    <function>sd_bus_set_method_call_timeout()</function>, the timeout is read from the
+    <varname>$SYSTEMD_BUS_TIMEOUT</varname> environment variable. If this environment variable is
+    unset or does not contain a valid timeout, the implementation falls back to a predefined method
+    call timeout of 25 seconds. Note that <varname>$SYSTEMD_BUS_TIMEOUT</varname> is read once and
+    cached so callers should not rely on being able to change the default method call timeout at
+    runtime by changing the value of <varname>$SYSTEMD_BUS_TIMEOUT</varname>. Instead, call
+    <function>sd_bus_set_method_call_timeout()</function> to change the default method call timeout.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On success, these functions return a non-negative integer. On failure, they return a
+    negative errno-style error code.</para>
+
+    <refsect2>
+      <title>Errors</title>
+
+      <para>Returned errors may indicate the following problems:</para>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>-EINVAL</constant></term>
+
+          <listitem><para>The parameters <parameter>bus</parameter> or <parameter>ret</parameter>
+          are <constant>NULL</constant>.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENOPKG</constant></term>
+
+          <listitem><para>Bus object <parameter>bus</parameter> could not be resolved.</para>
+          </listitem>
+        </varlistentry>
+      </variablelist>
+
+    </refsect2>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_call</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
diff --git a/man/sd_bus_set_property.xml b/man/sd_bus_set_property.xml
new file mode 100644 (file)
index 0000000..411ccad
--- /dev/null
@@ -0,0 +1,176 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_bus_set_property"
+          xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>sd_bus_set_property</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_bus_set_property</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_bus_set_property</refname>
+    <refname>sd_bus_set_propertyv</refname>
+    <refname>sd_bus_get_property</refname>
+    <refname>sd_bus_get_property_trivial</refname>
+    <refname>sd_bus_get_property_string</refname>
+    <refname>sd_bus_get_property_strv</refname>
+
+    <refpurpose>Set or query D-Bus service properties</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_set_property</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>const char *<parameter>destination</parameter></paramdef>
+        <paramdef>const char *<parameter>path</parameter></paramdef>
+        <paramdef>const char *<parameter>interface</parameter></paramdef>
+        <paramdef>const char *<parameter>member</parameter></paramdef>
+        <paramdef>sd_bus_error *<parameter>ret_error</parameter></paramdef>
+        <paramdef>sd_bus_message **<parameter>reply</parameter></paramdef>
+        <paramdef>const char *<parameter>type</parameter></paramdef>
+        <paramdef>...</paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_set_propertyv</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>const char *<parameter>destination</parameter></paramdef>
+        <paramdef>const char *<parameter>path</parameter></paramdef>
+        <paramdef>const char *<parameter>interface</parameter></paramdef>
+        <paramdef>const char *<parameter>member</parameter></paramdef>
+        <paramdef>sd_bus_error *<parameter>ret_error</parameter></paramdef>
+        <paramdef>sd_bus_message **<parameter>reply</parameter></paramdef>
+        <paramdef>const char *<parameter>type</parameter></paramdef>
+        <paramdef>va_list <parameter>ap</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_get_property</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>const char *<parameter>destination</parameter></paramdef>
+        <paramdef>const char *<parameter>path</parameter></paramdef>
+        <paramdef>const char *<parameter>interface</parameter></paramdef>
+        <paramdef>const char *<parameter>member</parameter></paramdef>
+        <paramdef>sd_bus_error *<parameter>ret_error</parameter></paramdef>
+        <paramdef>sd_bus_message **<parameter>reply</parameter></paramdef>
+        <paramdef>const char *<parameter>type</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_get_property_trivial</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>const char *<parameter>destination</parameter></paramdef>
+        <paramdef>const char *<parameter>path</parameter></paramdef>
+        <paramdef>const char *<parameter>interface</parameter></paramdef>
+        <paramdef>const char *<parameter>member</parameter></paramdef>
+        <paramdef>sd_bus_error *<parameter>ret_error</parameter></paramdef>
+        <paramdef>char <parameter>type</parameter></paramdef>
+        <paramdef>void *<parameter>ret_ptr</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_get_property_string</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>const char *<parameter>destination</parameter></paramdef>
+        <paramdef>const char *<parameter>path</parameter></paramdef>
+        <paramdef>const char *<parameter>interface</parameter></paramdef>
+        <paramdef>const char *<parameter>member</parameter></paramdef>
+        <paramdef>sd_bus_error *<parameter>ret_error</parameter></paramdef>
+        <paramdef>char **<parameter>ret</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_get_property_strv</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>const char *<parameter>destination</parameter></paramdef>
+        <paramdef>const char *<parameter>path</parameter></paramdef>
+        <paramdef>const char *<parameter>interface</parameter></paramdef>
+        <paramdef>const char *<parameter>member</parameter></paramdef>
+        <paramdef>sd_bus_error *<parameter>ret_error</parameter></paramdef>
+        <paramdef>char ***<parameter>ret</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>These functions set or query D-Bus properties. D-Bus properties are service fields exposed
+    via the <constant>org.freedesktop.DBus.Properties</constant> interface. Under the hood, these
+    functions call methods of the <constant>org.freedesktop.DBus.Properties</constant> interface and
+    as a result their semantics are similar to
+    <citerefentry><refentrytitle>sd_bus_call_method</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+    </para>
+
+    <para><function>sd_bus_set_property()</function> sets a D-Bus property. On success, the response
+    is stored in <parameter>reply</parameter>. If setting the property fails or an internal error
+    occurs, an error is returned and an extended description of the error is optionally stored in
+    <parameter>ret_error</parameter> if it is not <constant>NULL</constant>.
+    <parameter>type</parameter> and the arguments that follow it describe the new value of the
+    property and must follow the format described in
+    <citerefentry><refentrytitle>sd_bus_message_append</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+    </para>
+
+    <para><function>sd_bus_set_propertyv()</function> is equivalent to
+    <function>sd_bus_set_property()</function>, except that it is called with a
+    <literal>va_list</literal> instead of a variable number of arguments.</para>
+
+    <para><function>sd_bus_get_property()</function> queries a D-Bus property. If retrieving the
+    property fails or an internal error occurs, an error is returned and an extended description of
+    the error is optionally stored in <parameter>ret_error</parameter> if it is not
+    <constant>NULL</constant>. On success, the property is stored in <parameter>reply</parameter>.
+    <parameter>type</parameter> describes the property type and must follow the format described in
+    <citerefentry><refentrytitle>sd_bus_message_append</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+    </para>
+
+    <para><function>sd_bus_get_property_trivial()</function>,
+    <function>sd_bus_get_property_string()</function> and
+    <function>sd_bus_get_property_strv()</function> are shorthands for
+    <function>sd_bus_get_property()</function> that are used to query basic, string and string
+    vector properties respectively. The caller is responsible for freeing the string and string
+    vector results stored in <parameter>ret</parameter> by
+    <function>sd_bus_get_property_string()</function> and
+    <function>sd_bus_get_property_strv()</function>.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On success, these functions return a non-negative integer. On failure, they return a
+    negative errno-style error code.</para>
+
+    <refsect2 id='errors'>
+      <title>Errors</title>
+
+      <para>See the
+      <citerefentry><refentrytitle>sd_bus_call_method</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+      man page for a list of possible errors</para>
+    </refsect2>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_call_method</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
diff --git a/man/sd_bus_set_server.xml b/man/sd_bus_set_server.xml
new file mode 100644 (file)
index 0000000..625dfd4
--- /dev/null
@@ -0,0 +1,193 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_bus_set_server"
+          xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>sd_bus_set_server</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_bus_set_server</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_bus_set_server</refname>
+    <refname>sd_bus_is_server</refname>
+    <refname>sd_bus_get_bus_id</refname>
+    <refname>sd_bus_set_bus_client</refname>
+    <refname>sd_bus_is_bus_client</refname>
+    <refname>sd_bus_set_monitor</refname>
+    <refname>sd_bus_is_monitor</refname>
+
+    <refpurpose>Configure connection mode for a bus object</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_set_server</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>int <parameter>b</parameter></paramdef>
+        <paramdef>sd_id128_t <parameter>id</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_is_server</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_get_bus_id</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>sd_id128_t *<parameter>id</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_set_bus_client</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>int <parameter>b</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_is_bus_client</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_set_monitor</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+        <paramdef>int <parameter>b</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_is_monitor</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>sd_bus_set_server()</function> configures the bus object as a server for direct D-Bus
+    connections. <parameter>b</parameter> enables/disables the server mode. If zero, the server mode is
+    disabled. Otherwise, the server mode is enabled. Configuring a bus object as a server is required to
+    allow establishing direct connections between two peers without going via the D-Bus daemon.
+    <parameter>id</parameter> must contain a 128-bit integer id for the server. If clients add a guid field
+    to their D-Bus address string, the server id must match this guid or the D-Bus authentication handshake
+    will fail. If no specific id is defined for the server,
+    <citerefentry><refentrytitle>sd_id128_randomize</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    can be used to generate a random id instead.</para>
+
+    <para><function>sd_bus_is_server()</function> returns whether the server mode is enabled for the given
+    bus object.</para>
+
+    <para><function>sd_bus_get_bus_id()</function> stores the D-Bus server id configured using
+    <function>sd_bus_set_server()</function> (for server bus objects) or received during D-Bus authentication
+    (for client bus objects) in <parameter>id</parameter>.</para>
+
+    <para><function>sd_bus_set_bus_client()</function> configures the bus object as a D-Bus daemon client.
+    <parameter>b</parameter> enables/disables the client mode. If zero, the client mode is disabled and the
+    bus object should connect directly to a D-Bus server. Otherwise, the client mode is enabled and the bus
+    object should connect to a D-Bus daemon. When connecting to an existing bus using any of the functions in
+    the <citerefentry><refentrytitle>sd_bus_open</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    family of functions or any of the functions in the
+    <citerefentry><refentrytitle>sd_bus_default</refentrytitle><manvolnum>3</manvolnum></citerefentry> family
+    of functions, the bus object is automatically configured as a bus client. However, when connecting to a
+    D-Bus daemon by calling
+    <citerefentry><refentrytitle>sd_bus_set_address</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    followed by
+    <citerefentry><refentrytitle>sd_bus_start</refentrytitle><manvolnum>3</manvolnum></citerefentry>, the bus
+    object should be manually configured as a bus client using <function>sd_bus_set_bus_client()</function>.
+    By default, a bus object is not configured as a D-Bus daemon client.</para>
+
+    <para><function>sd_bus_is_bus_client()</function> returns whether the client mode is enabled/disabled for
+    the given bus object.</para>
+
+    <para><function>sd_bus_set_monitor()</function> configures the bus object as a D-Bus monitor object.
+    <parameter>b</parameter> enables/disables the monitor mode. If zero, the monitor mode is disabled. If
+    non-zero, the monitor mode is enabled. When the monitor mode is enabled, no messages may be sent via the
+    bus object and it may not expose any objects on the bus. To start monitoring messages, call the
+    <function>org.freedesktop.DBus.Monitoring.BecomeMonitor</function> method of the D-Bus daemon and pass
+    a list of matches indicating which messages to intercept. See
+    <ulink url="https://dbus.freedesktop.org/doc/dbus-specification.html#bus-messages-become-monitor">
+    The D-Bus specification</ulink> for more information.</para>
+
+    <para><function>sd_bus_is_monitor()</function> returns whether the monitor mode is enabled/disabled for
+    the given bus object.</para>
+
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On success, <function>sd_bus_set_server()</function>,
+    <function>sd_bus_get_bus_id()</function>, <function>sd_bus_set_bus_client()</function> and
+    <function>sd_bus_set_monitor()</function> return a non-negative integer. On failure, they return a
+    negative errno-style error code.</para>
+
+    <para><function>sd_bus_is_server()</function>, <function>sd_bus_is_bus_client()</function> and
+    <function>sd_bus_is_monitor()</function> return a positive integer when the server or client mode is
+    enabled, respectively. Otherwise, they return zero.</para>
+
+    <refsect2>
+      <title>Errors</title>
+
+      <para>Returned errors may indicate the following problems:</para>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>-ECHILD</constant></term>
+
+          <listitem><para>The bus connection has been created in a different process.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-EPERM</constant></term>
+
+          <listitem><para>The bus connection has already been started.</para></listitem>
+        </varlistentry>
+
+         <varlistentry>
+          <term><constant>-ENOPKG</constant></term>
+
+          <listitem><para>The bus cannot be resolved.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-EINVAL</constant></term>
+
+          <listitem><para>A required parameter was <constant>NULL</constant> or
+          <parameter>b</parameter> was zero and <parameter>id</parameter> did not equal
+          <constant>SD_ID128_NULL</constant>.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENOTCONN</constant></term>
+
+          <listitem><para>The bus is not connected.</para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
index cbdc7dd1eda47d3dc1f6ec88434615e179adf439..5638cdc0a62e4cbc503e2976457ac601b6352304 100644 (file)
@@ -48,7 +48,7 @@
     socket binding for a bus connection object. If the <parameter>b</parameter> is true, the feature is enabled,
     otherwise disabled (which is the default). When enabled, and the selected bus address refers to an
     <filename>AF_UNIX</filename> socket in the file system which does not exist while the connection attempt is made an
-    <citerefentry><refentrytitle>inotify</refentrytitle><manvolnum>7</manvolnum></citerefentry> watch is installed on
+    <citerefentry project='man-pages'><refentrytitle>inotify</refentrytitle><manvolnum>7</manvolnum></citerefentry> watch is installed on
     it, waiting for the socket to appear. As soon as the socket appears the connection is made. This functionality is
     useful in particular in early-boot programs that need to run before the system bus is available, but want to
     connect to it the instant it may be connected to.</para>
     <para><function>sd_bus_get_watch_bind()</function> may be used to query the current setting of this feature. It
     returns zero when the feature is disabled, and positive if enabled.</para>
 
-    <para>Note that no timeout is applied while it is waited for the socket to appear. This means that any synchronous
-    remote operation (such as
+    <para>Note that no timeout is applied while we wait for the socket to appear. This means that any
+    synchronous remote operation (such as
     <citerefentry><refentrytitle>sd_bus_call</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
     <citerefentry><refentrytitle>sd_bus_add_match</refentrytitle><manvolnum>3</manvolnum></citerefentry> or
-    <citerefentry><refentrytitle>sd_bus_request_name</refentrytitle><manvolnum>3</manvolnum></citerefentry>), that is
-    used on a connection with this feature enabled that is not established yet might block unbounded if the socket is
-    never created. However, asynchronous remote operations (such as
+    <citerefentry><refentrytitle>sd_bus_request_name</refentrytitle><manvolnum>3</manvolnum></citerefentry>),
+    that is used on a connection with this feature enabled that hasn't been established yet, might block
+    forever if the socket is never created. However, asynchronous remote operations (such as
     <citerefentry><refentrytitle>sd_bus_send</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-    <citerefentry><refentrytitle>sd_bus_add_match_async</refentrytitle><manvolnum>3</manvolnum></citerefentry> or
-    <citerefentry><refentrytitle>sd_bus_request_match_async</refentrytitle><manvolnum>3</manvolnum></citerefentry>) do
-    not block in this case, and safely enqueue the requested operations to be dispatched the instant the connection is
-    set up.</para>
+    <citerefentry><refentrytitle>sd_bus_call_async</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_bus_add_match_async</refentrytitle><manvolnum>3</manvolnum></citerefentry>)
+    do not block in this case, and safely enqueue the requested operations to be dispatched the instant the
+    connection is set up.</para>
 
     <para>Use <citerefentry><refentrytitle>sd_bus_is_ready</refentrytitle><manvolnum>3</manvolnum></citerefentry> to
     determine whether the connection is fully established, i.e. whether the peer socket has been bound, connected to
     <para>
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>inotify</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
+      <citerefentry project='man-pages'><refentrytitle>inotify</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_bus_call</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_bus_add_match</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_bus_request_name</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
diff --git a/man/sd_bus_slot_get_bus.xml b/man/sd_bus_slot_get_bus.xml
new file mode 100644 (file)
index 0000000..26541a9
--- /dev/null
@@ -0,0 +1,90 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_bus_slot_get_bus" xmlns:xi="http://www.w3.org/2001/XInclude">
+  <refentryinfo>
+    <title>sd_bus_slot_get_bus</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_bus_slot_get_bus</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_bus_slot_get_bus</refname>
+    <refname>sd_bus_slot_get_current_handler</refname>
+    <refname>sd_bus_slot_get_current_message</refname>
+    <refname>sd_bus_slot_get_current_userdata</refname>
+
+    <refpurpose>Query information attached to a bus slot object</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+      <xi:include href="sd_bus_add_match.xml" xpointer="sd_bus_message_handler_t"/>
+
+      <funcprototype>
+        <funcdef>sd_bus *<function>sd_bus_slot_get_bus</function></funcdef>
+        <paramdef>sd_bus_slot *<parameter>slot</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>sd_bus_message_handler_t <function>sd_bus_slot_get_current_handler</function>
+        </funcdef>
+        <paramdef>sd_bus_slot *<parameter>slot</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>sd_bus_message *<function>sd_bus_slot_get_current_message</function></funcdef>
+        <paramdef>sd_bus_slot *<parameter>slot</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>void *<function>sd_bus_slot_get_current_userdata</function></funcdef>
+        <paramdef>sd_bus_slot *<parameter>slot</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>sd_bus_slot_get_bus()</function> returns the bus object that message
+    <parameter>slot</parameter> is attached to.</para>
+
+    <para><function>sd_bus_slot_get_current_handler()</function>,
+    <function>sd_bus_slot_get_current_message()</function> and
+    <function>sd_bus_slot_get_current_userdata()</function> return the current handler, message and
+    userdata respectively of the bus <parameter>slot</parameter> is attached to if we're currently
+    executing the callback associated with <parameter>slot</parameter>.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para><function>sd_bus_slot_get_bus()</function> always returns the bus object.</para>
+
+    <para>On success, <function>sd_bus_slot_get_current_handler()</function>,
+    <function>sd_bus_slot_get_current_message()</function> and
+    <function>sd_bus_slot_get_current_userdata()</function> return the requested object. On failure,
+    they return <constant>NULL</constant>.</para>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    </para>
+  </refsect1>
+
+</refentry>
index c73f3c9e29bbf6e771eeeeb4c5f543ebcee794aa..ef144ece6d4ae4ac0b5dd2fd278355161e12786e 100644 (file)
@@ -18,7 +18,6 @@
     <refname>sd_bus_slot_ref</refname>
     <refname>sd_bus_slot_unref</refname>
     <refname>sd_bus_slot_unrefp</refname>
-    <refname>sd_bus_slot_get_bus</refname>
 
     <refpurpose>Create and destroy references to a bus slot object</refpurpose>
   </refnamediv>
         <funcdef>void <function>sd_bus_slot_unrefp</function></funcdef>
         <paramdef>sd_bus_slot **<parameter>slotp</parameter></paramdef>
       </funcprototype>
-
-      <funcprototype>
-        <funcdef>sd_bus *<function>sd_bus_slot_get_bus</function></funcdef>
-        <paramdef>sd_bus_slot *<parameter>m</parameter></paramdef>
-      </funcprototype>
     </funcsynopsis>
   </refsynopsisdiv>
 
     execute no operation if the passed in bus object address is
     <constant>NULL</constant>. <function>sd_bus_slot_unrefp()</function> will first dereference
     its argument, which must not be <constant>NULL</constant>, and will execute no operation if
-    <emphasis>that</emphasis> is <constant>NULL</constant>.
-    </para>
-
-    <para><function>sd_bus_slot_get_bus()</function> returns the bus object that message
-    <parameter>slot</parameter> is attached to.</para>
+    <emphasis>that</emphasis> is <constant>NULL</constant>.</para>
   </refsect1>
 
   <refsect1>
@@ -85,8 +75,6 @@
     <para><function>sd_bus_slot_ref()</function> always returns the argument.</para>
 
     <para><function>sd_bus_slot_unref()</function> always returns <constant>NULL</constant>.</para>
-
-    <para><function>sd_bus_slot_get_bus()</function> always returns the bus object.</para>
   </refsect1>
 
   <xi:include href="libsystemd-pkgconfig.xml" />
@@ -99,7 +87,6 @@
       <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_bus_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_bus_message_new</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>sd_bus_slot_new_signal</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd_bus_call_method_async</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     </para>
   </refsect1>
index c6fe72e37b5ff541488db1cb523793d3678de9d2..c2a0876e212506cd0f3d8b9e8476e21874ba418a 100644 (file)
@@ -23,7 +23,7 @@
     <refname>sd_bus_track_get_destroy_callback</refname>
     <refname>sd_bus_destroy_t</refname>
 
-    <refpurpose>Define the callback function for resource cleanup.</refpurpose>
+    <refpurpose>Define the callback function for resource cleanup</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
index f63907aa405f48702cede38f0db8b19bd5b065d6..ecfc07951415536cbc86328e9a43b6d77b2da213 100644 (file)
@@ -19,7 +19,7 @@
     <refname>sd_bus_slot_set_floating</refname>
     <refname>sd_bus_slot_get_floating</refname>
 
-    <refpurpose>Control whether a bus slot object is "floating".</refpurpose>
+    <refpurpose>Control whether a bus slot object is "floating"</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
diff --git a/man/sd_bus_start.xml b/man/sd_bus_start.xml
new file mode 100644 (file)
index 0000000..0be07f4
--- /dev/null
@@ -0,0 +1,124 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_bus_start"
+          xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>sd_bus_start</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_bus_start</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_bus_start</refname>
+
+    <refpurpose>Initiate a bus connection to the D-bus broker daemon
+    </refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-bus.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>int <function>sd_bus_start</function></funcdef>
+        <paramdef>sd_bus *<parameter>bus</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>sd_bus_start()</function> connects an existing bus connection object to the D-Bus
+    broker daemon, usually
+    <citerefentry project='die-net'><refentrytitle>dbus-daemon</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+    or
+    <citerefentry project='mankier'><refentrytitle>dbus-broker</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
+    The mechanism to use for the connection must be configured before the call to
+    <function>sd_bus_start()</function>, using one of
+    <citerefentry><refentrytitle>sd_bus_set_address</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_bus_set_fd</refentrytitle><manvolnum>3</manvolnum></citerefentry>, or
+    <citerefentry><refentrytitle>sd_bus_set_exec</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+    <function>sd_bus_start()</function> will open the connection socket or spawn the executable as
+    needed, and asynchronously start a <function>org.freedesktop.DBus.Hello()</function> call. The
+    answer to the Hello call will be processed later from
+    <citerefentry><refentrytitle>sd_bus_process</refentrytitle><manvolnum>3</manvolnum></citerefentry>. If
+    opening of the connection or queuing of the asynchronous call fail, the connection will be closed with
+    <citerefentry><refentrytitle>sd_bus_close</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
+
+    <para>In most cases, it is better to use
+    <citerefentry><refentrytitle>sd_bus_default_user</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_bus_default_system</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    or related calls instead of the more low-level <function>sd_bus_new()</function> and
+    <function>sd_bus_start()</function>. The higher-level functions not only allocate a bus object but also
+    start the connection to a well-known bus in a single function call.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On success, this function returns a non-negative integer. On failure, it returns a negative
+    errno-style error code.</para>
+
+    <refsect2 id='errors'>
+      <title>Errors</title>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>-EINVAL</constant></term>
+
+          <listitem><para>The input parameter <parameter>bus</parameter> is <constant>NULL</constant>.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENOPKG</constant></term>
+
+          <listitem><para>Bus object <parameter>bus</parameter> could not be resolved.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-EPERM</constant></term>
+
+          <listitem><para>The input parameter <parameter>bus</parameter> is in a wrong state
+          (<function>sd_bus_start()</function> may only be called once on a newly-created bus object).</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ECHILD</constant></term>
+
+          <listitem><para>The bus object <parameter>bus</parameter> was created in a different
+          process.</para>
+          </listitem>
+        </varlistentry>
+      </variablelist>
+
+      <para>In addition, other connection-related errors may be returned. See
+      <citerefentry><refentrytitle>sd_bus_send</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>
+    </refsect2>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_default</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd_bus_call_async</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
index 3db39c9d06bfdeaf824e4966e294045f279f8e64..0e572c40ee46a60a2ab6e35437dd4b53ed083738 100644 (file)
     automatically as the code block is left:</para>
 
     <programlisting>{
-        __attribute__((cleanup(sd_event_unrefp)) sd_event *event = NULL;
+        __attribute__((cleanup(sd_event_unrefp))) sd_event *event = NULL;
         int r;
         …
         r = sd_event_default(&amp;event);
index 2ffca9ec365bcec45c80619611c4da955763f292..3df926b8634f02728fa71ed3078e850fa177c8fb 100644 (file)
@@ -21,7 +21,7 @@
     <refname>sd_event_source_get_destroy_callback</refname>
     <refname>sd_event_destroy_t</refname>
 
-    <refpurpose>Define the callback function for resource cleanup.</refpurpose>
+    <refpurpose>Define the callback function for resource cleanup</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
diff --git a/man/sd_hwdb_get.xml b/man/sd_hwdb_get.xml
new file mode 100644 (file)
index 0000000..dbaaf62
--- /dev/null
@@ -0,0 +1,157 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_hwdb_get" xmlns:xi="http://www.w3.org/2001/XInclude">
+  <refentryinfo>
+    <title>sd_hwdb_get</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_hwdb_get</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_hwdb_get</refname>
+    <refname>sd_hwdb_seek</refname>
+    <refname>sd_hwdb_enumerate</refname>
+    <refname>SD_HWDB_FOREACH_PROPERTY</refname>
+
+    <refpurpose>Seek to a location in hwdb or access entries</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-hwdb.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>int <function>sd_hwdb_get</function></funcdef>
+        <paramdef>sd_hwdb *<parameter>hwdb</parameter></paramdef>
+        <paramdef>const char *<parameter>modalias</parameter></paramdef>
+        <paramdef>const char *<parameter>key</parameter></paramdef>
+        <paramdef>const char **<parameter>value</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_hwdb_seek</function></funcdef>
+        <paramdef>sd_hwdb *<parameter>hwdb</parameter></paramdef>
+        <paramdef>const char *<parameter>modalias</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_hwdb_enumerate</function></funcdef>
+        <paramdef>sd_hwdb *<parameter>hwdb</parameter></paramdef>
+        <paramdef>const char **<parameter>key</parameter></paramdef>
+        <paramdef>const char **<parameter>value</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef><function>SD_HWDB_FOREACH_PROPERTY</function></funcdef>
+        <paramdef>hwdb</paramdef>
+        <paramdef>modalias</paramdef>
+        <paramdef>key</paramdef>
+        <paramdef>value</paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>sd_hwdb_get()</function> queries the <parameter>hwdb</parameter> object created earlier
+    with <citerefentry><refentrytitle>sd_hwdb_new</refentrytitle><manvolnum>3</manvolnum></citerefentry> for
+    entries matching the specified string <parameter>modalias</parameter>, and returns the value
+    corresponding to the key <parameter>key</parameter>. The value is returned as a
+    <constant>NUL</constant>-terminated string in <parameter>value</parameter>. It must not be modified by
+    the caller and is valid as long as a reference to <parameter>hwdb</parameter> is kept. When multiple
+    patterns in the database match <parameter>modalias</parameter>, the one with the highest priority is
+    used. See <citerefentry><refentrytitle>hwdb</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
+    details.</para>
+
+    <para><function>sd_hwdb_seek()</function> selects entries matching the specified string
+    <parameter>modalias</parameter>. Subsequent queries with <function>sd_hwdb_enumerate()</function> will
+    access the key-value pairs for that string.</para>
+
+    <para><function>sd_hwdb_enumerate()</function> returns (in turn) all the key-value pairs defined for the
+    string used with <function>sd_hwdb_seek()</function>. Each pair is returned as
+    <constant>NUL</constant>-terminated strings in <parameter>key</parameter> and
+    <parameter>value</parameter>. The strings must not be modified by the caller and are valid as long as a
+    reference to <parameter>hwdb</parameter> is kept. When multiple patterns in the database match
+    <parameter>modalias</parameter>, the combination of all matching key-value pairs is used. See
+    <citerefentry><refentrytitle>hwdb</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
+    details.</para>
+
+    <para>The <function>SD_HWDB_FOREACH_PROPERTY</function> macro combines
+    <function>sd_hwdb_seek()</function> and <function>sd_hwdb_enumerate()</function>. No error handling is
+    performed and iteration simply stops on error. See the example below.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On success, <function>sd_hwdb_get()</function> and <function>sd_hwdb_seek()</function> return a
+    non-negative integer. On failure, they return a negative errno-style error code.</para>
+
+    <para><function>sd_hwdb_enumerate()</function> returns a positive integer if another key-value pair was found or zero if
+    all entries have already been enumerated. On failure, it returns a negative errno-style error code.
+    </para>
+
+    <refsect2>
+      <title>Errors</title>
+
+      <para>Returned errors may indicate the following problems:</para>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>-EINVAL</constant></term>
+
+          <listitem><para>A parameter is <constant>NULL</constant>.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENOENT</constant></term>
+
+          <listitem><para>An entry for the specified <parameter>modalias</parameter> was not found.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-EAGAIN</constant></term>
+
+          <listitem><para><function>sd_hwdb_seek()</function> was not called before
+          <function>sd_hwdb_enumerate()</function>.</para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>Examples</title>
+
+    <example>
+      <title>Look up hwdb entries for a USB device</title>
+
+      <programlisting><xi:include href="hwdb-usb-device.c" parse="text" /></programlisting>
+
+      <para>The effect is similar to calling <command>systemd-hwdb query usb:v046DpC534</command>.
+      </para>
+    </example>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-udevd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd-hwdb</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-hwdb</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
diff --git a/man/sd_hwdb_new.xml b/man/sd_hwdb_new.xml
new file mode 100644 (file)
index 0000000..8f1c01b
--- /dev/null
@@ -0,0 +1,121 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_hwdb_new" xmlns:xi="http://www.w3.org/2001/XInclude">
+  <refentryinfo>
+    <title>sd_hwdb_new</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_hwdb_new</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_hwdb_new</refname>
+    <refname>sd_hwdb_ref</refname>
+    <refname>sd_hwdb_unref</refname>
+
+    <refpurpose>Create a new hwdb object and create or destroy references to it</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-hwdb.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>int <function>sd_hwdb_new</function></funcdef>
+        <paramdef>sd_hwdb **<parameter>hwdb</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>sd_hwdb* <function>sd_hwdb_ref</function></funcdef>
+        <paramdef>sd_hwdb *<parameter>hwdb</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>sd_hwdb* <function>sd_hwdb_unref</function></funcdef>
+        <paramdef>sd_hwdb *<parameter>hwdb</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>sd_hwdb_new()</function> creates a new hwdb object to access the binary hwdb
+    database. Upon initialization, the file containing the binary representation of the hardware database is
+    located and opened. The new object is returned in <parameter>hwdb</parameter>.</para>
+
+    <para>The <parameter>hwdb</parameter> object is reference counted. <function>sd_hwdb_ref()</function> and
+    <function>sd_hwdb_unref()</function> may be used to get a new reference or destroy an existing reference
+    to an object. The caller must dispose of the reference acquired with <function>sd_hwdb_new()</function>
+    by calling <function>sd_hwdb_unref()</function> when done with the object.</para>
+
+    <para>Use
+    <citerefentry><refentrytitle>sd_hwdb_seek</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+    <citerefentry><refentrytitle>sd_hwdb_get</refentrytitle><manvolnum>3</manvolnum></citerefentry>, and
+    <citerefentry><refentrytitle>sd_hwdb_enumerate</refentrytitle><manvolnum>3</manvolnum></citerefentry> to
+    access entries.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On success, <function>sd_hwdb_new()</function> returns a non-negative integer. On
+    failure, it returns a negative errno-style error code.</para>
+
+    <para><function>sd_hwdb_ref()</function> always returns the argument.
+    </para>
+
+    <para><function>sd_hwdb_unref()</function> always returns <constant>NULL</constant>.
+    </para>
+
+    <refsect2>
+      <title>Errors</title>
+
+      <para>Returned errors may indicate the following problems:</para>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>-ENOENT</constant></term>
+
+          <listitem><para>The binary hardware database file could not be located. See
+          <citerefentry><refentrytitle>systemd-hwdb</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+          for more information.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-EINVAL</constant></term>
+
+          <listitem><para>The located binary hardware database file is in an incompatible format.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENOMEM</constant></term>
+
+          <listitem><para>Memory allocation failed.</para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-udevd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>sd-hwdb</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-hwdb</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
index e665f7343f9ae3d6f3584982c99692c20782dc97..bb85d8839f4daa6de6567347aa3aeb5e91a5598c 100644 (file)
@@ -66,7 +66,7 @@
     file. This function caches the machine ID internally to make retrieving the machine ID a cheap operation. This ID
     may be used wherever a unique identifier for the local system is needed. However, it is recommended to use this ID
     as-is only in trusted environments. In untrusted environments it is recommended to derive an application specific
-    ID from this machine ID, in an irreversable (cryptographically secure) way. To make this easy
+    ID from this machine ID, in an irreversible (cryptographically secure) way. To make this easy
     <function>sd_id128_get_machine_app_specific()</function> is provided, see below.</para>
 
     <para><function>sd_id128_get_machine_app_specific()</function> is similar to
index 0a0030e3007fdba6b5ba987bb5368a99b18896ab..209f5deaa1dde53b989b7aa11e6361c4ca647a5f 100644 (file)
@@ -18,6 +18,7 @@
   <refnamediv>
     <refname>sd_journal_get_data</refname>
     <refname>sd_journal_enumerate_data</refname>
+    <refname>sd_journal_enumerate_available_data</refname>
     <refname>sd_journal_restart_data</refname>
     <refname>SD_JOURNAL_FOREACH_DATA</refname>
     <refname>sd_journal_set_data_threshold</refname>
         <paramdef>size_t *<parameter>length</parameter></paramdef>
       </funcprototype>
 
+      <funcprototype>
+        <funcdef>int <function>sd_journal_enumerate_available_data</function></funcdef>
+        <paramdef>sd_journal *<parameter>j</parameter></paramdef>
+        <paramdef>const void **<parameter>data</parameter></paramdef>
+        <paramdef>size_t *<parameter>length</parameter></paramdef>
+      </funcprototype>
+
       <funcprototype>
         <funcdef>void <function>sd_journal_restart_data</function></funcdef>
         <paramdef>sd_journal *<parameter>j</parameter></paramdef>
   <refsect1>
     <title>Description</title>
 
-    <para><function>sd_journal_get_data()</function> gets the data
-    object associated with a specific field from the current journal
-    entry. It takes four arguments: the journal context object, a
-    string with the field name to request, plus a pair of pointers to
-    pointer/size variables where the data object and its size shall be
-    stored in. The field name should be an entry field name.
-    Well-known field names are listed in
-    <citerefentry><refentrytitle>systemd.journal-fields</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
-    The returned data is in a read-only memory map and is only valid
-    until the next invocation of
-    <function>sd_journal_get_data()</function> or
-    <function>sd_journal_enumerate_data()</function>, or the read
-    pointer is altered. Note that the data returned will be prefixed
-    with the field name and '='. Also note that, by default, data fields
-    larger than 64K might get truncated to 64K. This threshold may be
-    changed and turned off with
-    <function>sd_journal_set_data_threshold()</function> (see
-    below).</para>
+    <para><function>sd_journal_get_data()</function> gets the data object associated with a specific field
+    from the current journal entry. It takes four arguments: the journal context object, a string with the
+    field name to request, plus a pair of pointers to pointer/size variables where the data object and its
+    size shall be stored in. The field name should be an entry field name. Well-known field names are listed in
+    <citerefentry><refentrytitle>systemd.journal-fields</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
+    but any field can be specified. The returned data is in a read-only memory map and is only valid until
+    the next invocation of <function>sd_journal_get_data()</function>,
+    <function>sd_journal_enumerate_data()</function>,
+    <function>sd_journal_enumerate_available_data()</function>, or when the read pointer is altered. Note
+    that the data returned will be prefixed with the field name and <literal>=</literal>. Also note that, by
+    default, data fields larger than 64K might get truncated to 64K. This threshold may be changed and turned
+    off with <function>sd_journal_set_data_threshold()</function> (see below).</para>
 
     <para><function>sd_journal_enumerate_data()</function> may be used
     to iterate through all fields of the current entry. On each
     format as with <function>sd_journal_get_data()</function> and also
     follows the same life-time semantics.</para>
 
+    <para><function>sd_journal_enumerate_available_data()</function> is similar to
+    <function>sd_journal_enumerate_data()</function>, but silently skips any fields which may be valid, but
+    are too large or not supported by current implementation.</para>
+
     <para><function>sd_journal_restart_data()</function> resets the
     data enumeration index to the beginning of the entry. The next
     invocation of <function>sd_journal_enumerate_data()</function>
     will return the first field of the entry again.</para>
 
-    <para>Note that the <function>SD_JOURNAL_FOREACH_DATA()</function>
-    macro may be used as a handy wrapper around
-    <function>sd_journal_restart_data()</function> and
-    <function>sd_journal_enumerate_data()</function>.</para>
+    <para>Note that the <function>SD_JOURNAL_FOREACH_DATA()</function> macro may be used as a handy wrapper
+    around <function>sd_journal_restart_data()</function> and
+    <function>sd_journal_enumerate_available_data()</function>.</para>
 
     <para>Note that these functions will not work before
     <citerefentry><refentrytitle>sd_journal_next</refentrytitle><manvolnum>3</manvolnum></citerefentry>
   <refsect1>
     <title>Return Value</title>
 
-    <para><function>sd_journal_get_data()</function> returns 0 on
-    success or a negative errno-style error code. If the current entry
-    does not include the specified field, -ENOENT is returned. If
-    <citerefentry><refentrytitle>sd_journal_next</refentrytitle><manvolnum>3</manvolnum></citerefentry>
-    has not been called at least once, -EADDRNOTAVAIL is returned.
-    <function>sd_journal_enumerate_data()</function> returns a
-    positive integer if the next field has been read, 0 when no more
-    fields are known, or a negative errno-style error code.
-    <function>sd_journal_restart_data()</function> returns nothing.
-    <function>sd_journal_set_data_threshold()</function> and
-    <function>sd_journal_get_threshold()</function> return 0 on
-    success or a negative errno-style error code.</para>
+    <para><function>sd_journal_get_data()</function> returns 0 on success or a negative errno-style error
+    code. <function>sd_journal_enumerate_data()</function> and
+    <function>sd_journal_enumerate_available_data()</function> return a positive integer if the next field
+    has been read, 0 when no more fields remain, or a negative errno-style error code.
+    <function>sd_journal_restart_data()</function> doesn't return anything.
+    <function>sd_journal_set_data_threshold()</function> and <function>sd_journal_get_threshold()</function>
+    return 0 on success or a negative errno-style error code.</para>
+
+    <refsect2>
+      <title>Errors</title>
+
+      <para>Returned errors may indicate the following problems:</para>
+
+      <variablelist>
+        <varlistentry id='EINVAL'>
+          <term><constant>-EINVAL</constant></term>
+
+          <listitem><para>One of the required parameters is <constant>NULL</constant> or invalid.
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry id='ECHILD'>
+          <term><constant>-ECHILD</constant></term>
+
+          <listitem><para>The journal object was created in a different process.</para></listitem>
+        </varlistentry>
+
+        <varlistentry id='EADDRNOTAVAIL'>
+          <term><constant>-EADDRNOTAVAIL</constant></term>
+
+          <listitem><para>The read pointer is not positioned at a valid entry;
+          <citerefentry><refentrytitle>sd_journal_next</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+          or a related call has not been called at least once.</para></listitem>
+        </varlistentry>
+
+        <varlistentry id='ENOENT'>
+          <term><constant>-ENOENT</constant></term>
+
+          <listitem><para>The current entry does not include the specified field.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry id='ENOMEM'>
+          <term><constant>-ENOMEM</constant></term>
+
+          <listitem><para>Memory allocation failed.</para></listitem>
+        </varlistentry>
+
+        <varlistentry id='ENOBUFS'>
+          <term><constant>-ENOBUFS</constant></term>
+
+          <listitem><para>A compressed entry is too large.</para></listitem>
+        </varlistentry>
+
+        <varlistentry id='E2BIG'>
+          <term><constant>-E2BIG</constant></term>
+
+          <listitem><para>The data field is too large for this computer architecture (e.g. above 4 GB on a
+          32-bit architecture).</para></listitem>
+        </varlistentry>
+
+        <varlistentry id='EPROTONOSUPPORT'>
+          <term><constant>-EPROTONOSUPPORT</constant></term>
+
+          <listitem><para>The journal is compressed with an unsupported method or the journal uses an
+          unsupported feature.</para></listitem>
+        </varlistentry>
+
+        <varlistentry id='EBADMSG'>
+          <term><constant>-EBADMSG</constant></term>
+
+          <listitem><para>The journal is corrupted (possibly just the entry being iterated over).
+          </para></listitem>
+        </varlistentry>
+
+        <varlistentry id='EIO'>
+          <term><constant>-EIO</constant></term>
+
+          <listitem><para>An I/O error was reported by the kernel.</para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
   </refsect1>
 
   <refsect1>
index 7e6e7d4b9d29d1238bc4e859cc17fd2d3e3098eb..02fdc12473e57176acb2adb03b923f11fd15fb4c 100644 (file)
@@ -21,7 +21,7 @@
   <refnamediv>
     <refname>sd_journal_has_runtime_files</refname>
     <refname>sd_journal_has_persistent_files</refname>
-    <refpurpose>Query availability of runtime or persistent journal files.</refpurpose>
+    <refpurpose>Query availability of runtime or persistent journal files</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
index 9884beae1a348718b9a3aec64cb5025b873c7a01..bdece26ccca971d85f6af06a22443f8c2a135f95 100644 (file)
@@ -22,6 +22,7 @@
     <refname>sd_journal_open_directory_fd</refname>
     <refname>sd_journal_open_files</refname>
     <refname>sd_journal_open_files_fd</refname>
+    <refname>sd_journal_open_namespace</refname>
     <refname>sd_journal_close</refname>
     <refname>sd_journal</refname>
     <refname>SD_JOURNAL_LOCAL_ONLY</refname>
index f6973087f3a0a4e2a16b14a4864f33ade4e4e4c6..84adab5c7b22bcfb9e66aca34a480ab19be73139 100644 (file)
     <refname>sd_journal_sendv</refname>
     <refname>sd_journal_perror</refname>
     <refname>SD_JOURNAL_SUPPRESS_LOCATION</refname>
+    <refname>sd_journal_print_with_location</refname>
+    <refname>sd_journal_printv_with_location</refname>
+    <refname>sd_journal_send_with_location</refname>
+    <refname>sd_journal_sendv_with_location</refname>
+    <refname>sd_journal_perror_with_location</refname>
+
     <refpurpose>Submit log entries to the journal</refpurpose>
   </refnamediv>
 
         <paramdef>const char *<parameter>message</parameter></paramdef>
       </funcprototype>
 
+      <funcprototype>
+        <funcdef>int <function>sd_journal_print_with_location</function></funcdef>
+        <paramdef>const char *<parameter>file</parameter></paramdef>
+        <paramdef>const char *<parameter>line</parameter></paramdef>
+        <paramdef>const char *<parameter>func</parameter></paramdef>
+        <paramdef>int <parameter>priority</parameter></paramdef>
+        <paramdef>const char *<parameter>format</parameter></paramdef>
+        <paramdef>…</paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_journal_printv_with_location</function></funcdef>
+        <paramdef>int <parameter>priority</parameter></paramdef>
+        <paramdef>const char *<parameter>file</parameter></paramdef>
+        <paramdef>const char *<parameter>line</parameter></paramdef>
+        <paramdef>const char *<parameter>func</parameter></paramdef>
+        <paramdef>const char *<parameter>format</parameter></paramdef>
+        <paramdef>va_list <parameter>ap</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_journal_send_with_location</function></funcdef>
+        <paramdef>const char *<parameter>file</parameter></paramdef>
+        <paramdef>const char *<parameter>line</parameter></paramdef>
+        <paramdef>const char *<parameter>func</parameter></paramdef>
+        <paramdef>const char *<parameter>format</parameter></paramdef>
+        <paramdef>…</paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_journal_sendv_with_location</function></funcdef>
+        <paramdef>const char *<parameter>file</parameter></paramdef>
+        <paramdef>const char *<parameter>line</parameter></paramdef>
+        <paramdef>const char *<parameter>func</parameter></paramdef>
+        <paramdef>const struct iovec *<parameter>iov</parameter></paramdef>
+        <paramdef>int <parameter>n</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_journal_perror_with_location</function></funcdef>
+        <paramdef>const char *<parameter>file</parameter></paramdef>
+        <paramdef>const char *<parameter>line</parameter></paramdef>
+        <paramdef>const char *<parameter>func</parameter></paramdef>
+        <paramdef>const char *<parameter>message</parameter></paramdef>
+      </funcprototype>
     </funcsynopsis>
   </refsynopsisdiv>
 
@@ -136,11 +187,20 @@ sd_journal_send("MESSAGE=Hello World, this is PID %lu!", (unsigned long) getpid(
                 "PRIORITY=%i", LOG_INFO,
                 NULL);</programlisting>
 
-    <para>Note that these calls implicitly add fields for the source
-    file, function name and code line where invoked. This is
-    implemented with macros. If this is not desired, it can be turned
-    off by defining SD_JOURNAL_SUPPRESS_LOCATION before including
-    <filename>sd-journal.h</filename>.</para>
+    <para>Note that these calls implicitly add fields for the source file, function name and code line where
+    invoked. This is implemented with macros. If this is not desired, it can be turned off by defining
+    <constant>SD_JOURNAL_SUPPRESS_LOCATION</constant> before including <filename>sd-journal.h</filename>.
+    </para>
+
+    <para><function>sd_journal_print_with_location</function>,
+    <function>sd_journal_printv_with_location</function>, <function>sd_journal_send_with_location</function>,
+    <function>sd_journal_sendv_with_location</function>, and
+    <function>sd_journal_perror_with_location</function> are similar to their counterparts without
+    <literal>_with_location</literal>, but accept additional parameters to explicitly set the source file
+    name, function, and line. Those arguments must contain valid journal entries including the variable name,
+    e.g. <literal>CODE_FILE=src/foo.c</literal>, <literal>CODE_LINE=666</literal>,
+    <literal>CODE_FUNC=myfunc</literal>. These variants are primarily useful when writing custom wrappers,
+    for example in bindings for a different language.</para>
 
     <para><citerefentry project='man-pages'><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     and <function>sd_journal_print()</function> may
@@ -163,9 +223,9 @@ sd_journal_send("MESSAGE=Hello World, this is PID %lu!", (unsigned long) getpid(
   <refsect1>
     <title>Return Value</title>
 
-    <para>The five calls return 0 on success or a negative errno-style error code. The <citerefentry
-    project='man-pages'><refentrytitle>errno</refentrytitle><manvolnum>3</manvolnum></citerefentry> variable itself is
-    not altered.</para>
+    <para>The ten functions return 0 on success or a negative errno-style error code. The
+    <citerefentry project='man-pages'><refentrytitle>errno</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    variable itself is not altered.</para>
 
     <para>If
     <citerefentry><refentrytitle>systemd-journald</refentrytitle><manvolnum>8</manvolnum></citerefentry>
@@ -178,15 +238,17 @@ sd_journal_send("MESSAGE=Hello World, this is PID %lu!", (unsigned long) getpid(
 
     <xi:include href="threads-aware.xml" xpointer="safe"/>
 
-    <para><function>sd_journal_sendv()</function> is "async signal safe" in the meaning of <citerefentry
-    project='man-pages'><refentrytitle>signal-safety</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
+    <para><function>sd_journal_sendv()</function> and <function>sd_journal_sendv_with_location()</function>
+    are "async signal safe" in the meaning of
+    <citerefentry project='man-pages'><refentrytitle>signal-safety</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
     </para>
 
     <para><function>sd_journal_print</function>,
     <function>sd_journal_printv</function>,
-    <function>sd_journal_send</function>, and
-    <function>sd_journal_perror</function> are
-    not async signal safe.</para>
+    <function>sd_journal_send</function>,
+    <function>sd_journal_perror</function>,
+    and their counterparts with <literal>_with_location</literal>
+    are not async signal safe.</para>
   </refsect1>
 
   <xi:include href="libsystemd-pkgconfig.xml" />
index 1bf83968dd3c16710122f7223c416c2dcb1474af..88beaa6460cd4f05904a656de68395f6db583481 100644 (file)
@@ -18,6 +18,7 @@
   <refnamediv>
     <refname>sd_journal_query_unique</refname>
     <refname>sd_journal_enumerate_unique</refname>
+    <refname>sd_journal_enumerate_available_unique</refname>
     <refname>sd_journal_restart_unique</refname>
     <refname>SD_JOURNAL_FOREACH_UNIQUE</refname>
     <refpurpose>Read unique data fields from the journal</refpurpose>
         <paramdef>const char *<parameter>field</parameter></paramdef>
       </funcprototype>
 
+      <funcprototype>
+        <funcdef>int <function>sd_journal_enumerate_available_unique</function></funcdef>
+        <paramdef>sd_journal *<parameter>j</parameter></paramdef>
+        <paramdef>const void **<parameter>data</parameter></paramdef>
+        <paramdef>size_t *<parameter>length</parameter></paramdef>
+      </funcprototype>
+
       <funcprototype>
         <funcdef>int <function>sd_journal_enumerate_unique</function></funcdef>
         <paramdef>sd_journal *<parameter>j</parameter></paramdef>
   <refsect1>
     <title>Description</title>
 
-    <para><function>sd_journal_query_unique()</function> queries the
-    journal for all unique values the specified field can take. It
-    takes two arguments: the journal to query and the field name to
-    look for. Well-known field names are listed on
-    <citerefentry><refentrytitle>systemd.journal-fields</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
-    Field names must be specified without a trailing '='. After this
-    function has been executed successfully the field values may be
-    queried using <function>sd_journal_enumerate_unique()</function>.
-    Invoking this call a second time will change the field name being
-    queried and reset the enumeration index to the first field value
-    that matches.</para>
-
-    <para><function>sd_journal_enumerate_unique()</function> may be
-    used to iterate through all data fields which match the previously
-    selected field name as set with
-    <function>sd_journal_query_unique()</function>. On each invocation
-    the next field data matching the field name is returned. The order
-    of the returned data fields is not defined. It takes three
-    arguments: the journal context object, plus a pair of pointers to
-    pointer/size variables where the data object and its size shall be
-    stored in. The returned data is in a read-only memory map and is
-    only valid until the next invocation of
-    <function>sd_journal_enumerate_unique()</function>. Note that the
-    data returned will be prefixed with the field name and '='. Note
-    that this call is subject to the data field size threshold as
-    controlled by
-    <function>sd_journal_set_data_threshold()</function>.</para>
+    <para><function>sd_journal_query_unique()</function> queries the journal for all unique values the
+    specified field can take. It takes two arguments: the journal to query and the field name to look
+    for. Well-known field names are listed on
+    <citerefentry><refentrytitle>systemd.journal-fields</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
+    but any field can be specified. Field names must be specified without a trailing
+    <literal>=</literal>. After this function has been executed successfully the field values may be queried
+    using <function>sd_journal_enumerate_unique()</function> and
+    <function>sd_journal_enumerate_available_unique()</function>. Invoking one of those calls will change the
+    field name being queried and reset the enumeration index to the first field value that matches.</para>
+
+    <para><function>sd_journal_enumerate_unique()</function> may be used to iterate through all data fields
+    which match the previously selected field name as set with
+    <function>sd_journal_query_unique()</function>. On each invocation the next field data matching the field
+    name is returned. The order of the returned data fields is not defined. It takes three arguments: the
+    journal object, plus a pair of pointers to pointer/size variables where the data object and its size
+    shall be stored. The returned data is in a read-only memory map and is only valid until the next
+    invocation of <function>sd_journal_enumerate_unique()</function>. Note that the data returned will be
+    prefixed with the field name and <literal>=</literal>. Note that this call is subject to the data field
+    size threshold as controlled by <function>sd_journal_set_data_threshold()</function> and only the initial
+    part of the field up to the threshold is returned. An error is returned for fields which cannot be
+    retrieved. See the error list below for details.</para>
+
+    <para><function>sd_journal_enumerate_available_unique()</function> is similar to
+    <function>sd_journal_enumerate_unique()</function>, but silently skips any fields which may be valid, but
+    are too large or not supported by current implementation.</para>
 
     <para><function>sd_journal_restart_unique()</function> resets the
     data enumeration index to the beginning of the list. The next
     will return the first field data matching the field name
     again.</para>
 
-    <para>Note that the
-    <function>SD_JOURNAL_FOREACH_UNIQUE()</function> macro may be used
-    as a handy wrapper around
-    <function>sd_journal_restart_unique()</function> and
-    <function>sd_journal_enumerate_unique()</function>.</para>
+    <para>Note that the <function>SD_JOURNAL_FOREACH_UNIQUE()</function> macro may be used as a handy wrapper
+    around <function>sd_journal_restart_unique()</function> and
+    <function>sd_journal_enumerate_available_unique()</function>.</para>
 
     <para>Note that these functions currently are not influenced by
     matches set with <function>sd_journal_add_match()</function> but
   <refsect1>
     <title>Return Value</title>
 
-    <para><function>sd_journal_query_unique()</function> returns 0 on
-    success or a negative errno-style error code.
-    <function>sd_journal_enumerate_unique()</function> returns a
-    positive integer if the next field data has been read, 0 when no
-    more fields are known, or a negative errno-style error code.
-    <function>sd_journal_restart_unique()</function> returns
-    nothing.</para>
+    <para><function>sd_journal_query_unique()</function> returns 0 on success or a negative errno-style error
+    code. <function>sd_journal_enumerate_unique()</function> and and
+    <function>sd_journal_query_available_unique()</function> return a positive integer if the next field data
+    has been read, 0 when no more fields remain, or a negative errno-style error code.
+    <function>sd_journal_restart_unique()</function> doesn't return anything.</para>
+
+    <refsect2>
+      <title>Errors</title>
+
+      <para>Returned errors may indicate the following problems:</para>
+
+      <variablelist>
+        <xi:include href="sd_journal_get_data.xml" xpointer="EINVAL"/>
+        <xi:include href="sd_journal_get_data.xml" xpointer="ECHILD"/>
+        <xi:include href="sd_journal_get_data.xml" xpointer="EADDRNOTAVAIL"/>
+        <xi:include href="sd_journal_get_data.xml" xpointer="ENOENT"/>
+        <xi:include href="sd_journal_get_data.xml" xpointer="ENOBUFS"/>
+        <xi:include href="sd_journal_get_data.xml" xpointer="E2BIG"/>
+        <xi:include href="sd_journal_get_data.xml" xpointer="EPROTONOSUPPORT"/>
+        <xi:include href="sd_journal_get_data.xml" xpointer="EBADMSG"/>
+        <xi:include href="sd_journal_get_data.xml" xpointer="EIO"/>
+      </variablelist>
+    </refsect2>
   </refsect1>
 
   <refsect1>
   <refsect1>
     <title>Examples</title>
 
-    <para>Use the <function>SD_JOURNAL_FOREACH_UNIQUE</function> macro
-    to iterate through all values a field of the journal can take. The
-    following example lists all unit names referenced in the
-    journal:</para>
+    <para>Use the <function>SD_JOURNAL_FOREACH_UNIQUE</function> macro to iterate through all values a field
+    of the journal can take (and which can be accessed on the given architecture and are not compressed with
+    an unsupported mechanism). The following example lists all unit names referenced in the journal:</para>
 
     <programlisting><xi:include href="journal-iterate-unique.c" parse="text" /></programlisting>
   </refsect1>
index be1c843ee097a8a28fda34a80ea9fb249978bdd7..d1c83e2d20080a3f8e025f4e370588a0378c4e55 100644 (file)
     code block is left:</para>
 
     <programlisting>{
-  __attribute__((cleanup(sd_login_monitor_unrefp)) sd_login_monitor *m = NULL;
+  __attribute__((cleanup(sd_login_monitor_unrefp))) sd_login_monitor *m = NULL;
   int r;
   …
   r = sd_login_monitor_default(&amp;m);
index db6cf0dbc09b30c8a3244d419507e39b22f2f44d..cd259c863f9fa5e6a8125ba6b7e83028ff1430c9 100644 (file)
@@ -19,7 +19,7 @@
     <refname>sd_machine_get_class</refname>
     <refname>sd_machine_get_ifindices</refname>
     <refpurpose>Determine the class and network interface indices of a
-    locally running virtual machine or container.</refpurpose>
+    locally running virtual machine or container</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
index 3046ca88ee728c703d7b411406db05b92c564ecd..87b12c4bdff8b16ac28ffb67ac00a4421e371f75 100644 (file)
@@ -22,6 +22,7 @@
     <refname>sd_pid_notify</refname>
     <refname>sd_pid_notifyf</refname>
     <refname>sd_pid_notify_with_fds</refname>
+    <refname>sd_notify_barrier</refname>
     <refpurpose>Notify service manager about start-up completion and other service status changes</refpurpose>
   </refnamediv>
 
         <paramdef>const int *<parameter>fds</parameter></paramdef>
         <paramdef>unsigned <parameter>n_fds</parameter></paramdef>
       </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_notify_barrier</function></funcdef>
+        <paramdef>int <parameter>unset_environment</parameter></paramdef>
+        <paramdef>uint64_t <parameter>timeout</parameter></paramdef>
+      </funcprototype>
     </funcsynopsis>
   </refsynopsisdiv>
 
         in a <citerefentry><refentrytitle>memfd_create</refentrytitle><manvolnum>2</manvolnum></citerefentry> memory
         file descriptor. Note that the service manager will accept messages for a service only if its
         <varname>FileDescriptorStoreMax=</varname> setting is non-zero (defaults to zero, see
-        <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>). If file
-        descriptors sent are pollable (see
+        <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>). If
+        <varname>FDPOLL=0</varname> is not set and the file descriptors sent are pollable (see
         <citerefentry><refentrytitle>epoll_ctl</refentrytitle><manvolnum>2</manvolnum></citerefentry>), then any
         <constant>EPOLLHUP</constant> or <constant>EPOLLERR</constant> event seen on them will result in their
         automatic removal from the store. Multiple arrays of file descriptors may be sent in separate messages, in
         submitted name does not follow these restrictions, it is ignored.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term>FDPOLL=0</term>
+
+        <listitem><para>When used in combination with <varname>FDSTORE=1</varname>, disables polling of the stored
+        file descriptors regardless of whether or not they are pollable. As this option disables automatic cleanup
+        of the stored file descriptors on EPOLLERR and EPOLLHUP, care must be taken to ensure proper manual cleanup.
+        Use of this option is not generally recommended except for when automatic cleanup has unwanted behavior such
+        as prematurely discarding file descriptors from the store.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term>BARRIER=1</term>
+
+        <listitem><para>Tells the service manager that the client is explicitly requesting synchronization by means of
+        closing the file descriptor sent with this command. The service manager guarantees that the processing of a <varname>
+        BARRIER=1</varname> command will only happen after all previous notification messages sent before this command
+        have been processed. Hence, this command accompanied with a single file descriptor can be used to synchronize
+        against reception of all previous status messages. Note that this command cannot be mixed with other notifications,
+        and has to be sent in a separate message to the service manager, otherwise all assignments will be ignored. Note that
+        sending 0 or more than 1 file descriptor with this command is a violation of the protocol.</para></listitem>
+      </varlistentry>
     </variablelist>
 
     <para>It is recommended to prefix variable names that are not
     attribute the message to the unit, and thus will ignore it, even if
     <varname>NotifyAccess=</varname><option>all</option> is set for it.</para>
 
+    <para>Hence, to eliminate all race conditions involving lookup of the client's unit and attribution of notifications
+    to units correctly, <function>sd_notify_barrier()</function> may be used. This call acts as a synchronization point
+    and ensures all notifications sent before this call have been picked up by the service manager when it returns
+    successfully. Use of <function>sd_notify_barrier()</function> is needed for clients which are not invoked by the
+    service manager, otherwise this synchronization mechanism is unnecessary for attribution of notifications to the
+    unit.</para>
+
     <para><function>sd_notifyf()</function> is similar to
     <function>sd_notify()</function> but takes a
     <function>printf()</function>-like format string plus
     to the service manager on messages that do not expect them (i.e.
     without <literal>FDSTORE=1</literal>) they are immediately closed
     on reception.</para>
+
+    <para><function>sd_notify_barrier()</function> allows the caller to
+    synchronize against reception of previously sent notification messages
+    and uses the <literal>BARRIER=1</literal> command. It takes a relative
+    <varname>timeout</varname> value in microseconds which is passed to
+    <citerefentry><refentrytitle>ppoll</refentrytitle><manvolnum>2</manvolnum>
+    </citerefentry>. A value of UINT64_MAX is interpreted as infinite timeout.
+    </para>
   </refsect1>
 
   <refsect1>
 
       <programlisting>sd_pid_notify_with_fds(0, 0, "FDSTORE=1\nFDNAME=foobar", &amp;fd, 1);</programlisting>
     </example>
+
+    <example>
+      <title>Eliminating race conditions</title>
+
+      <para>When the client sending the notifications is not spawned
+      by the service manager, it may exit too quickly and the service
+      manager may fail to attribute them correctly to the unit. To
+      prevent such races, use <function>sd_notify_barrier()</function>
+      to synchronize against reception of all notifications sent before
+      this call is made.</para>
+
+      <programlisting>sd_notify(0, "READY=1");
+      /* set timeout to 5 seconds */
+      sd_notify_barrier(0, 5 * 1000000);
+      </programlisting>
+    </example>
   </refsect1>
 
   <refsect1>
diff --git a/man/sd_path_lookup.xml b/man/sd_path_lookup.xml
new file mode 100644 (file)
index 0000000..9316090
--- /dev/null
@@ -0,0 +1,215 @@
+<?xml version='1.0'?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<refentry id="sd_path_lookup" xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>sd_path_lookup</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_path_lookup</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_path_lookup</refname>
+    <refname>sd_path_lookup_strv</refname>
+
+    <refpurpose>Query well-known file system paths</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-path.h&gt;</funcsynopsisinfo>
+
+      <!-- note: individual constants are not added as <refname>s, there's just too many -->
+
+      <funcsynopsisinfo><token>enum</token> {
+        <constant>SD_PATH_TEMPORARY</constant>,
+        <constant>SD_PATH_TEMPORARY_LARGE</constant>,
+
+        <constant>SD_PATH_SYSTEM_BINARIES</constant>,
+        <constant>SD_PATH_SYSTEM_INCLUDE</constant>,
+        <constant>SD_PATH_SYSTEM_LIBRARY_PRIVATE</constant>,
+        <constant>SD_PATH_SYSTEM_LIBRARY_ARCH</constant>,
+        <constant>SD_PATH_SYSTEM_SHARED</constant>,
+        <constant>SD_PATH_SYSTEM_CONFIGURATION_FACTORY</constant>,
+        <constant>SD_PATH_SYSTEM_STATE_FACTORY</constant>,
+
+        <constant>SD_PATH_SYSTEM_CONFIGURATION</constant>,
+        <constant>SD_PATH_SYSTEM_RUNTIME</constant>,
+        <constant>SD_PATH_SYSTEM_RUNTIME_LOGS</constant>,
+        <constant>SD_PATH_SYSTEM_STATE_PRIVATE</constant>,
+        <constant>SD_PATH_SYSTEM_STATE_LOGS</constant>,
+        <constant>SD_PATH_SYSTEM_STATE_CACHE</constant>,
+        <constant>SD_PATH_SYSTEM_STATE_SPOOL</constant>,
+
+        <constant>SD_PATH_USER_BINARIES</constant>,
+        <constant>SD_PATH_USER_LIBRARY_PRIVATE</constant>,
+        <constant>SD_PATH_USER_LIBRARY_ARCH</constant>,
+        <constant>SD_PATH_USER_SHARED</constant>,
+
+        <constant>SD_PATH_USER_CONFIGURATION</constant>,
+        <constant>SD_PATH_USER_RUNTIME</constant>,
+        <constant>SD_PATH_USER_STATE_CACHE</constant>,
+
+        <constant>SD_PATH_USER</constant>,
+        <constant>SD_PATH_USER_DOCUMENTS</constant>,
+        <constant>SD_PATH_USER_MUSIC</constant>,
+        <constant>SD_PATH_USER_PICTURES</constant>,
+        <constant>SD_PATH_USER_VIDEOS</constant>,
+        <constant>SD_PATH_USER_DOWNLOAD</constant>,
+        <constant>SD_PATH_USER_PUBLIC</constant>,
+        <constant>SD_PATH_USER_TEMPLATES</constant>,
+        <constant>SD_PATH_USER_DESKTOP</constant>,
+
+        <constant>SD_PATH_SEARCH_BINARIES</constant>,
+        <constant>SD_PATH_SEARCH_BINARIES_DEFAULT</constant>,
+        <constant>SD_PATH_SEARCH_LIBRARY_PRIVATE</constant>,
+        <constant>SD_PATH_SEARCH_LIBRARY_ARCH</constant>,
+        <constant>SD_PATH_SEARCH_SHARED</constant>,
+        <constant>SD_PATH_SEARCH_CONFIGURATION_FACTORY</constant>,
+        <constant>SD_PATH_SEARCH_STATE_FACTORY</constant>,
+        <constant>SD_PATH_SEARCH_CONFIGURATION</constant>,
+
+        <constant>SD_PATH_SYSTEMD_UTIL</constant>,
+        <constant>SD_PATH_SYSTEMD_SYSTEM_UNIT</constant>,
+        <constant>SD_PATH_SYSTEMD_SYSTEM_PRESET</constant>,
+        <constant>SD_PATH_SYSTEMD_USER_UNIT</constant>,
+        <constant>SD_PATH_SYSTEMD_USER_PRESET</constant>,
+        <constant>SD_PATH_SYSTEMD_SYSTEM_CONF</constant>,
+        <constant>SD_PATH_SYSTEMD_USER_CONF</constant>,
+        <constant>SD_PATH_SYSTEMD_SEARCH_SYSTEM_UNIT</constant>,
+        <constant>SD_PATH_SYSTEMD_SEARCH_USER_UNIT</constant>,
+        <constant>SD_PATH_SYSTEMD_SYSTEM_GENERATOR</constant>,
+        <constant>SD_PATH_SYSTEMD_USER_GENERATOR</constant>,
+        <constant>SD_PATH_SYSTEMD_SEARCH_SYSTEM_GENERATOR</constant>,
+        <constant>SD_PATH_SYSTEMD_SEARCH_USER_GENERATOR</constant>,
+        <constant>SD_PATH_SYSTEMD_SLEEP</constant>,
+        <constant>SD_PATH_SYSTEMD_SHUTDOWN</constant>,
+
+        <constant>SD_PATH_TMPFILES</constant>,
+        <constant>SD_PATH_SYSUSERS</constant>,
+        <constant>SD_PATH_SYSCTL</constant>,
+        <constant>SD_PATH_BINFMT</constant>,
+        <constant>SD_PATH_MODULES_LOAD</constant>,
+        <constant>SD_PATH_CATALOG</constant>,
+
+        <constant>SD_PATH_SYSTEMD_SEARCH_NETWORK</constant>,
+};</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>int <function>sd_path_lookup</function></funcdef>
+        <paramdef>uint64_t <parameter>type</parameter></paramdef>
+        <paramdef>const char *<parameter>suffix</parameter></paramdef>
+        <paramdef>char **<parameter>paths</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_path_lookup_strv</function></funcdef>
+        <paramdef>uint64_t <parameter>type</parameter></paramdef>
+        <paramdef>const char *<parameter>suffix</parameter></paramdef>
+        <paramdef>char ***<parameter>paths</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>sd_path_lookup()</function> and <function>sd_bus_path_lookup_strv()</function> return a
+    single path or set of file system paths specified by the argument <parameter>type</parameter>. In case of
+    <function>sd_path_lookup()</function> a single <constant>NUL</constant>-terminated string is returned.
+    When <parameter>type</parameter> specifies a set of paths, they are concatenated using
+    <literal>:</literal> as a separator (as is traditionally done for e.g. <varname>$PATH</varname> or
+    <varname>$LD_LIBRARY_PATH</varname>). In case of <function>sd_path_lookup_strv()</function> a
+    <constant>NULL</constant>-terminated array of strings is returned (strv). If suffix
+    <parameter>suffix</parameter> is given, it is concatenated to each of the paths after a slash
+    (<literal>/</literal>). All returned paths are absolute.</para>
+
+    <para>For paths which refer to user directories, the relevant XDG standard is followed, with support for
+    environment variables like <varname>$XDG_DOCUMENTS_DIR</varname>, <varname>$XDG_DESKTOP_DIR</varname>,
+    ..., and explicit configuration in <filename>/etc/xdg/user-dirs.conf</filename> or
+    <filename>${XDG_CONFIG_HOME}/user-dirs.dirs</filename>. See
+    <ulink url="https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html">
+      XDG Base Directory Specification</ulink> for details.</para>
+
+    <para><citerefentry><refentrytitle>systemd-path</refentrytitle><manvolnum>1</manvolnum></citerefentry> is
+    a wrapper around <function>sd_path_lookup()</function> and allows the same set of paths to be queried.
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On success, <function>sd_path_lookup()</function> and <function>sd_path_lookup_strv()</function>
+    return a non-negative integer. On failure, a negative errno-style error number is returned by either
+    function.</para>
+
+    <para>The returned string or string array (strv) must be
+    <citerefentry project='man-pages'><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry>'d by the
+    caller.</para>
+
+    <refsect2 id='errors'>
+      <title>Errors</title>
+
+      <para>Returned errors may indicate the following problems:</para>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>-EOPNOTSUPP</constant></term>
+
+          <listitem><para>Unknown identifier <parameter>type</parameter>.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-EINVAL</constant></term>
+
+          <listitem><para>Output argument is <constant>NULL</constant>.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENXIO</constant></term>
+
+          <listitem><para>Query failed because of an undefined environment variable (e.g. for
+          <constant>SD_PATH_USER_RUNTIME</constant> when <varname>$XDG_RUNTIME_DIR</varname> is not
+          defined).</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENOMEM</constant></term>
+
+          <listitem><para>Memory allocation failed.</para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+  </refsect1>
+
+  <refsect1>
+    <title>Examples</title>
+
+    <refsect2>
+      <title>Look up the location of ~/Documents</title>
+
+      <programlisting><xi:include href="path-documents.c" parse="text" /></programlisting>
+      <para>Note that the default answer of <filename index='false'>$HOME/Documents</filename> may be
+      overridden by <filename index='false'>user-dirs.conf</filename> or
+      <varname>$XDG_DOCUMENTS_DIR</varname>.</para>
+    </refsect2>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para>
+      <citerefentry><refentrytitle>systemd-path</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
index e9d7a8eb696899aa4119f66233c7b9ff78a58541..9c16d5bc9c01c6841f5eebfc62727ab950af78d0 100644 (file)
@@ -35,7 +35,7 @@
     <refname>sd_peer_get_cgroup</refname>
     <refpurpose>Determine the owner uid of the user unit or session,
     or the session, user unit, system unit, container/VM or slice that
-    a specific PID or socket peer belongs to.</refpurpose>
+    a specific PID or socket peer belongs to</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
index 2dba6803f1e48962435207aeb4da7aa453f09c29..cf70b35785a58255fb56ad9f40c3e7042e355fa9 100644 (file)
@@ -19,7 +19,6 @@
   <refnamediv>
     <refname>sd_seat_get_active</refname>
     <refname>sd_seat_get_sessions</refname>
-    <refname>sd_seat_can_multi_session</refname>
     <refname>sd_seat_can_tty</refname>
     <refname>sd_seat_can_graphical</refname>
     <refpurpose>Determine state of a specific seat</refpurpose>
         <paramdef>unsigned int *<parameter>n_uids</parameter></paramdef>
       </funcprototype>
 
-      <funcprototype>
-        <funcdef>int <function>sd_seat_can_multi_session</function></funcdef>
-        <paramdef>const char *<parameter>seat</parameter></paramdef>
-      </funcprototype>
-
       <funcprototype>
         <funcdef>int <function>sd_seat_can_tty</function></funcdef>
         <paramdef>const char *<parameter>seat</parameter></paramdef>
     <constant>NULL</constant> may be returned and should be considered
     equivalent to an empty array.</para>
 
-    <para><function>sd_seat_can_multi_session()</function> may be used
-    to determine whether a specific seat is capable of multi-session,
-    i.e. allows multiple login sessions in parallel (with only one
-    being active at a time).</para>
-
     <para><function>sd_seat_can_tty()</function> may be used to
     determine whether a specific seat provides TTY functionality, i.e.
     is useful as a text console.</para>
 
     <para> On success, <function>sd_seat_get_active()</function> returns 0 or a positive integer. On success,
     <function>sd_seat_get_sessions()</function> returns the number of entries in the session identifier
-    array. If the test succeeds, <function>sd_seat_can_multi_session</function>,
+    array. If the test succeeds,
     <function>sd_seat_can_tty</function> and <function>sd_seat_can_graphical</function> return a positive
     integer, if it fails 0. On failure, these calls return a negative errno-style error code.</para>
 
 
   <xi:include href="libsystemd-pkgconfig.xml" />
 
+  <refsect1>
+    <title>History</title>
+
+    <para>In the past, <function>sd_seat_can_multi_session()</function> was used to check whether the seat
+    supports multiple sessions. All seats support that now, so that function has been deprecated and always
+    returns true.</para>
+  </refsect1>
+
   <refsect1>
     <title>See Also</title>
 
index a58c76d85fa7da19863361beaf8f05bfd6b9eddc..ee8cc7bc0a11b050127416007fd3a22ba6e03fab 100644 (file)
@@ -13,7 +13,7 @@
 
     <para>Configuration files are read from directories in <filename>/etc/</filename>,
     <filename>/run/</filename>, <filename>/usr/local/lib/</filename>, and <filename>/usr/lib/</filename>, in
-    order of precedence, as listed in the SYNOPSIS section above. Files must have the the
+    order of precedence, as listed in the SYNOPSIS section above. Files must have the
     <literal>.conf</literal> extension. Files in <filename>/etc/</filename> override files with the same name
     in <filename>/run/</filename>, <filename>/usr/local/lib/</filename>, and
     <filename>/usr/lib/</filename>. Files in <filename>/run/</filename> override files with the same name
diff --git a/man/standard-specifiers.xml b/man/standard-specifiers.xml
new file mode 100644 (file)
index 0000000..3efbb6d
--- /dev/null
@@ -0,0 +1,62 @@
+<?xml version="1.0"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+
+<tbody>
+  <row id='b'>
+    <entry><literal>%b</literal></entry>
+    <entry>Boot ID</entry>
+    <entry>The boot ID of the running system, formatted as string. See <citerefentry><refentrytitle>random</refentrytitle><manvolnum>4</manvolnum></citerefentry> for more information.</entry>
+  </row>
+  <row id='a'>
+    <entry><literal>%a</literal></entry>
+    <entry>Architecture</entry>
+    <entry>A short string identifying the architecture of the local system. A string such as <constant>x86</constant>, <constant>x86-64</constant> or <constant>arm64</constant>. See the architectures defined for <varname>ConditionArchitecture=</varname> in <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry> for a full list.</entry>
+  </row>
+  <row id='B'>
+    <entry><literal>%B</literal></entry>
+    <entry>Operating system build ID</entry>
+    <entry>The operating system build identifier of the running system, as read from the <varname>BUILD_ID=</varname> field of <filename>/etc/os-release</filename>. If not set, resolves to an empty string. See <citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry> for more information.</entry>
+  </row>
+  <row id='H'>
+    <entry><literal>%H</literal></entry>
+    <entry>Host name</entry>
+    <entry>The hostname of the running system.</entry>
+  </row>
+  <row id='l'>
+    <entry><literal>%l</literal></entry>
+    <entry>Short host name</entry>
+    <entry>The hostname of the running system, truncated at the first dot to remove any domain component.</entry>
+  </row>
+  <row id='m'>
+    <entry><literal>%m</literal></entry>
+    <entry>Machine ID</entry>
+    <entry>The machine ID of the running system, formatted as string. See <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry> for more information.</entry>
+  </row>
+  <row id='o'>
+    <entry><literal>%o</literal></entry>
+    <entry>Operating system ID</entry>
+    <entry>The operating system identifier of the running system, as read from the <varname>ID=</varname> field of <filename>/etc/os-release</filename>. See <citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry> for more information.</entry>
+  </row>
+  <row id='v'>
+    <entry><literal>%v</literal></entry>
+    <entry>Kernel release</entry>
+    <entry>Identical to <command>uname -r</command> output.</entry>
+  </row>
+  <row id='w'>
+    <entry><literal>%w</literal></entry>
+    <entry>Operating system version ID</entry>
+    <entry>The operating system version identifier of the running system, as read from the <varname>VERSION_ID=</varname> field of <filename>/etc/os-release</filename>. If not set, resolves to an empty string. See <citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry> for more information.</entry>
+  </row>
+  <row id='W'>
+    <entry><literal>%W</literal></entry>
+    <entry>Operating system variant ID</entry>
+    <entry>The operating system variant identifier of the running system, as read from the <varname>VARIANT_ID=</varname> field of <filename>/etc/os-release</filename>. If not set, resolves to an empty string. See <citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry> for more information.</entry>
+  </row>
+  <row id='percent'>
+    <entry><literal>%%</literal></entry>
+    <entry>Single percent sign</entry>
+    <entry>Use <literal>%%</literal> in place of <literal>%</literal> to specify a single percent sign.</entry>
+  </row>
+</tbody>
index f6713d3981ac47cf4bd17ad9d71103c9c56fb464..70504510f9ca6354889df72f6ab06f0cf2c4ae6e 100644 (file)
@@ -32,7 +32,7 @@ key.middle/part/with/dots.foo = 123
 -key.that.will.not.fail = value
 key.pattern.*.with.glob = whatever
 -key.pattern.excluded.with.glob
-key.pattern.overriden.with.glob = custom
+key.pattern.overridden.with.glob = custom
 </programlisting>
   </refsynopsisdiv>
 
@@ -63,18 +63,18 @@ key.pattern.overriden.with.glob = custom
     <literal>net.ipv4.conf.enp3s0/200.forwarding</literal> or
     <literal>net/ipv4/conf/enp3s0.200/forwarding</literal> may be used to refer to
     <filename>/proc/sys/net/ipv4/conf/enp3s0.200/forwarding</filename>. A glob
-    <citerefentry><refentrytitle>glob</refentrytitle><manvolnum>7</manvolnum></citerefentry> pattern may be
+    <citerefentry project='man-pages'><refentrytitle>glob</refentrytitle><manvolnum>7</manvolnum></citerefentry> pattern may be
     used to write the same value to all matching keys. Keys for which an explicit pattern exists will be
     excluded from any glob matching. In addition, a key may be explicitly excluded from being set by any
     matching glob patterns by specifying the key name prefixed with a <literal>-</literal> character and not
     followed by <literal>=</literal>, see SYNOPSIS.</para>
 
     <para>Any access permission errors and attempts to write variables not present on the local system are
-    logged, but do not cause the service to fail. Debug log level is used, which means that the message will
-    not show up at all by default. Moreover, if a variable assignment is prefixed with a single
-    <literal>-</literal> character, any failure to set the variable will be logged at debug level, but will
-    not cause the service to fail. All other errors when setting variables are logged with higher priority
-    and cause the service to return failure at the end (other variables are still processed).</para>
+    logged at debug level and do not cause the service to fail. Moreover, if a variable assignment is
+    prefixed with a single <literal>-</literal> character, failure to set the variable for other reasons will
+    be logged at debug level and will not cause the service to fail. In other cases, errors when setting
+    variables are logged with higher priority and cause the service to return failure at the end (after
+    processing other variables).</para>
 
     <para>The settings configured with <filename>sysctl.d</filename> files will be applied early on boot. The
     network interface-specific options will also be applied individually for each network interface as it
index 3f3615156724ee4ea23cdec99ea8fdafd1d0ab51..506f9ca68f870b4c87928a9833c0c29b4ac00a6e 100644 (file)
@@ -301,6 +301,30 @@ Sun 2017-02-26 20:57:49 EST  2h 3min left  Sun 2017-02-26 11:56:36 EST  6h ago
             generally redundant and reproducible on the next invocation of the unit).</para>
           </listitem>
         </varlistentry>
+        <varlistentry>
+          <term><command>freeze <replaceable>PATTERN</replaceable>…</command></term>
+
+          <listitem>
+            <para>Freeze one or more units specified on the
+            command line using cgroup freezer</para>
+
+            <para>Freezing the unit will cause all processes contained within the cgroup corresponding to the unit
+            to be suspended. Being suspended means that unit's processes won't be scheduled to run on CPU until thawed.
+            Note that this command is supported only on systems that use unified cgroup hierarchy. Unit is automatically
+            thawed just before we execute a job against the unit, e.g. before the unit is stopped.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term><command>thaw <replaceable>PATTERN</replaceable>…</command></term>
+
+          <listitem>
+            <para>Thaw (unfreeze) one or more units specified on the
+            command line.</para>
+
+            <para>This is the inverse operation to the <command>freeze</command> command and resumes the execution of
+            processes in the unit's cgroup.</para>
+          </listitem>
+        </varlistentry>
         <varlistentry>
           <term><command>is-active <replaceable>PATTERN</replaceable>…</command></term>
 
@@ -386,7 +410,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
 
             <para>The "Loaded:" line in the output will show <literal>loaded</literal> if the unit has been loaded into
             memory. Other possible values for "Loaded:" include: <literal>error</literal> if there was a problem
-            loading it, <literal>not-found</literal> if not unit file was found for this unit,
+            loading it, <literal>not-found</literal> if no unit file was found for this unit,
             <literal>bad-setting</literal> if an essential unit file setting could not be parsed and
             <literal>masked</literal> if the unit file has been masked. Along with showing the path to the unit file,
             this line will also show the enablement state.  Enabled commands start at boot.  See the full table of
@@ -556,7 +580,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
 
           <listitem>
             <para>Enable one or more units or unit instances. This will create a set of symlinks, as encoded in the
-            <literal>[Install]</literal> sections of the indicated unit files. After the symlinks have been created,
+            [Install] sections of the indicated unit files. After the symlinks have been created,
             the system manager configuration is reloaded (in a way equivalent to <command>daemon-reload</command>), in
             order to ensure the changes are taken into account immediately. Note that this does
             <emphasis>not</emphasis> have the effect of also starting any of the units being enabled. If this is
@@ -579,7 +603,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
             <option>--quiet</option>.
             </para>
 
-            <para>Note that this operation creates only the symlinks suggested in the <literal>[Install]</literal>
+            <para>Note that this operation creates only the symlinks suggested in the [Install]
             section of the unit files. While this command is the recommended way to manipulate the unit configuration
             directory, the administrator is free to make additional changes manually by placing or removing symlinks
             below this directory. This is particularly useful to create configurations that deviate from the suggested
@@ -619,7 +643,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
             <para>This command expects valid unit names only, it does not accept paths to unit files.</para>
 
             <para>In addition to the units specified as arguments, all units are disabled that are listed in the
-            <varname>Also=</varname> setting contained in the <literal>[Install]</literal> section of any of the unit
+            <varname>Also=</varname> setting contained in the [Install] section of any of the unit
             files being operated on.</para>
 
             <para>This command implicitly reloads the system manager configuration after completing the operation. Note
@@ -642,7 +666,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
           <listitem>
             <para>Reenable one or more units, as specified on the command line. This is a combination of
             <command>disable</command> and <command>enable</command> and is useful to reset the symlinks a unit file is
-            enabled with to the defaults configured in its <literal>[Install]</literal> section. This command expects
+            enabled with to the defaults configured in its [Install] section. This command expects
             a unit name only, it does not accept paths to unit files.</para>
           </listitem>
         </varlistentry>
@@ -727,6 +751,11 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
                   <row>
                     <entry><literal>linked-runtime</literal></entry>
                   </row>
+                  <row>
+                    <entry><literal>alias</literal></entry>
+                    <entry>The name is an alias (symlink to another unit file).</entry>
+                    <entry>0</entry>
+                  </row>
                   <row>
                     <entry><literal>masked</literal></entry>
                     <entry morerows='1'>Completely disabled, so that any start operation on it fails (permanently in <filename>/etc/systemd/system/</filename> or transiently in <filename>/run/systemd/systemd/</filename>).</entry>
@@ -737,17 +766,17 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
                   </row>
                   <row>
                     <entry><literal>static</literal></entry>
-                    <entry>The unit file is not enabled, and has no provisions for enabling in the <literal>[Install]</literal> unit file section.</entry>
+                    <entry>The unit file is not enabled, and has no provisions for enabling in the [Install] unit file section.</entry>
                     <entry>0</entry>
                   </row>
                   <row>
                     <entry><literal>indirect</literal></entry>
-                    <entry>The unit file itself is not enabled, but it has a non-empty <varname>Also=</varname> setting in the <literal>[Install]</literal> unit file section, listing other unit files that might be enabled, or it has an alias under a different name through a symlink that is not specified in <varname>Also=</varname>. For template unit file, an instance different than the one specified in <varname>DefaultInstance=</varname> is enabled.</entry>
+                    <entry>The unit file itself is not enabled, but it has a non-empty <varname>Also=</varname> setting in the [Install] unit file section, listing other unit files that might be enabled, or it has an alias under a different name through a symlink that is not specified in <varname>Also=</varname>. For template unit files, an instance different than the one specified in <varname>DefaultInstance=</varname> is enabled.</entry>
                     <entry>0</entry>
                   </row>
                   <row>
                     <entry><literal>disabled</literal></entry>
-                    <entry>The unit file is not enabled, but contains an <literal>[Install]</literal> section with installation instructions.</entry>
+                    <entry>The unit file is not enabled, but contains an [Install] section with installation instructions.</entry>
                     <entry>&gt; 0</entry>
                   </row>
                   <row>
@@ -1065,7 +1094,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
           </listitem>
         </varlistentry>
 
-        <varlistentry>
+        <varlistentry id='log-level'>
           <term><command>log-level</command> [<replaceable>LEVEL</replaceable>]</term>
 
           <listitem><para>If no argument is given, print the current log level of the manager. If an
@@ -1264,7 +1293,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
           </listitem>
         </varlistentry>
         <varlistentry>
-          <term><command>reboot</command> <optional><replaceable>arg</replaceable></optional></term>
+          <term><command>reboot</command></term>
 
           <listitem>
             <para>Shut down and reboot the system. This is mostly equivalent to <command>systemctl start reboot.target
@@ -1280,11 +1309,9 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
             <command>systemctl</command> itself, and the system manager is not contacted. This means the command should
             succeed even when the system manager has crashed.</para>
 
-            <para>If the optional argument <replaceable>arg</replaceable> is given, it will be passed as the optional
+            <para>If the switch <option>--reboot-argument=</option> is given, it will be passed as the optional
             argument to the <citerefentry><refentrytitle>reboot</refentrytitle><manvolnum>2</manvolnum></citerefentry>
-            system call. The value is architecture and firmware specific. As an example, <literal>recovery</literal>
-            might be used to trigger system recovery, and <literal>fota</literal> might be used to trigger a
-            <quote>firmware over the air</quote> update.</para>
+            system call.</para>
           </listitem>
         </varlistentry>
 
@@ -1500,6 +1527,17 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>-P</option></term>
+
+        <listitem>
+          <para>Equivalent to <option>--value</option> <option>--property=</option>, i.e. shows the
+          value of the property without the property name or <literal>=</literal>. Note that using
+          <option>-P</option> once will also affect all properties listed with
+          <option>-p</option>/<option>--property=</option>.</para>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>-a</option></term>
         <term><option>--all</option></term>
@@ -1625,9 +1663,8 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
         <term><option>--value</option></term>
 
         <listitem>
-          <para>When printing properties with <command>show</command>,
-          only print the value, and skip the property name and
-          <literal>=</literal>.</para>
+          <para>When printing properties with <command>show</command>, only print the value, and skip the
+          property name and <literal>=</literal>. Also see option <option>-P</option> above.</para>
         </listitem>
       </varlistentry>
 
@@ -2059,8 +2096,8 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
 
         <listitem>
           <para>When used with the <command>reboot</command> command, indicate to the system's boot loader to show the
-          boot loader menu on the following boot. Takes a time value as parameter — indicating the menu time-out. Pass
-          zero in order to disable the menu time-out. Note that not all boot loaders support this
+          boot loader menu on the following boot. Takes a time value as parameter — indicating the menu timeout. Pass
+          zero in order to disable the menu timeout. Note that not all boot loaders support this
           functionality.</para>
         </listitem>
       </varlistentry>
@@ -2076,6 +2113,16 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--reboot-argument=</option></term>
+
+        <listitem>
+          <para>This switch is used with <command>reboot</command>. The value is architecture and firmware specific. As an example, <literal>recovery</literal>
+            might be used to trigger system recovery, and <literal>fota</literal> might be used to trigger a
+            <quote>firmware over the air</quote> update.</para>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>--plain</option></term>
 
index 1106c36d0c86664cc6d40ff974bf3f65046328b6..3d6de1710d3946d9d48d7ec251b19f3e5cefbd12 100644 (file)
@@ -509,9 +509,9 @@ NAutoVTs=8
       <para>This command will load unit files and print warnings if any errors are detected. Files specified
       on the command line will be loaded, but also any other units referenced by them. The full unit search
       path is formed by combining the directories for all command line arguments, and the usual unit load
-      paths (variable <varname>$SYSTEMD_UNIT_PATH</varname> is supported, and may be used to replace or
+      paths. The variable <varname>$SYSTEMD_UNIT_PATH</varname> is supported, and may be used to replace or
       augment the compiled in set of unit load paths; see
-      <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>).  All
+      <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>. All
       units files present in the directories containing the command line arguments will be used in preference
       to the other paths.</para>
 
@@ -669,7 +669,7 @@ Service b@0.service not loaded, b.socket cannot be started.
         <command>dot</command> command (see above), this selects which
         relationships are shown in the dependency graph. Both options
         require a
-        <citerefentry project='die-net'><refentrytitle>glob</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+        <citerefentry project='man-pages'><refentrytitle>glob</refentrytitle><manvolnum>7</manvolnum></citerefentry>
         pattern as an argument, which will be matched against the
         left-hand and the right-hand, respectively, nodes of a
         relationship.</para>
@@ -700,9 +700,9 @@ Service b@0.service not loaded, b.socket cannot be started.
       <varlistentry>
         <term><option>--man=no</option></term>
 
-        <listitem><para>Do not invoke man to verify the existence of
-        man pages listed in <varname>Documentation=</varname>.
-        </para></listitem>
+        <listitem><para>Do not invoke
+        <citerefentry project='man-pages'><refentrytitle>man</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+        to verify the existence of man pages listed in <varname>Documentation=</varname>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
index 0c5144f6b89a87bc2eecdb1df3602cb17f43701d..8275838bce0f3cba40a6241d754796eb05b0fd72 100644 (file)
@@ -17,7 +17,7 @@
 
   <refnamediv>
     <refname>systemd-bless-boot-generator</refname>
-    <refpurpose>Pull <filename>systemd-bless-boot.service</filename> into the initial boot transaction when boot counting is in effect.</refpurpose>
+    <refpurpose>Pull <filename>systemd-bless-boot.service</filename> into the initial boot transaction when boot counting is in effect</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
index 4a45560a8cddeab8c3e253f5b383bd6c346e9a00..1787d7216fa4c5c325a5901442109eae6670692e 100644 (file)
     <para>
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+      <citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>7</manvolnum></citerefentry>
     </para>
   </refsect1>
 
index 8509fa633ed0ee1f32a06b68b613077ffe80bd0b..e92fdd63c0119753a420082c0573a6eafe433390 100644 (file)
@@ -45,7 +45,7 @@
     <title>See Also</title>
     <para>
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+      <citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>7</manvolnum></citerefentry>
     </para>
   </refsect1>
 
index ee513ed40983f47c9b00c11ebf29e70c8dc9806e..ee6ad1d533304f107871ab84972a85f6f108b9e8 100644 (file)
@@ -91,7 +91,7 @@
 
       <listitem><para>The boot manager optionally reads a random seed from the ESP partition, combines it
       with a 'system token' stored in a persistent EFI variable and derives a random seed to use by the OS as
-      entropy pool initializaton, providing a full entropy pool during early boot.</para></listitem>
+      entropy pool initialization, providing a full entropy pool during early boot.</para></listitem>
     </itemizedlist>
 
     <para><citerefentry><refentrytitle>bootctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
 
       <varlistentry>
         <term><keycap>↵</keycap> (Enter)</term>
+        <term><keycap>→</keycap> (Right)</term>
         <listitem><para>Boot selected entry</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><keycap>h</keycap></term>
         <term><keycap>?</keycap></term>
+        <term><keycap>F1</keycap></term>
         <listitem><para>Show a help screen</para></listitem>
       </varlistentry>
 
         is maintained persistently, while <varname>LoaderConfigTimeoutOneShot</varname> is a one-time override which is
         read once (in which case it takes precedence over <varname>LoaderConfigTimeout</varname>) and then
         removed. <varname>LoaderConfigTimeout</varname> may be manipulated with the
-        <keycap>t</keycap>/<keycap>T</keycap> keys, see above.)</para></listitem>
+        <keycap>t</keycap>/<keycap>T</keycap> keys, see above.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         boots. <citerefentry><refentrytitle>bootctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
         <option>set-default</option> and <option>set-oneshot</option> commands make use of these variables. The boot
         loader modifies <varname>LoaderEntryDefault</varname> on request, when the <keycap>d</keycap> key is used, see
-        above.)</para></listitem>
+        above.</para></listitem>
       </varlistentry>
 
       <varlistentry>
       <varlistentry>
         <term><varname>LoaderSystemToken</varname></term>
 
-        <listitem><para>A binary random data field, that is used for generating the random see to pass to the
-        OS (see above). Note that this random data is generally only generated once, during OS installation,
-        and is then never updated again.</para></listitem>
+        <listitem><para>A binary random data field, that is used for generating the random seed to pass to
+        the OS (see above). Note that this random data is generally only generated once, during OS
+        installation, and is then never updated again.</para></listitem>
       </varlistentry>
     </variablelist>
 
     considered 'good' from then on.</para>
 
     <para>The boot menu takes the 'tries left' counter into account when sorting the menu entries: entries in 'bad'
-    state are ordered at the end of the list, and entries in 'good' or 'indeterminate' at the beginning. The user can
+    state are ordered at the beginning of the list, and entries in 'good' or 'indeterminate' at the end. The user can
     freely choose to boot any entry of the menu, including those already marked 'bad'. If the menu entry to boot is
-    automatically determined, this means that 'good' or 'indeterminate' entries are generally preferred (as the top item of
-    the menu is the one booted by default), and 'bad' entries will only be considered if there are no 'good' or
+    automatically determined, this means that 'good' or 'indeterminate' entries are generally preferred (as the bottom
+    item of the menu is the one booted by default), and 'bad' entries will only be considered if there are no 'good' or
     'indeterminate' entries left.</para>
 
     <para>The <citerefentry><refentrytitle>kernel-install</refentrytitle><manvolnum>8</manvolnum></citerefentry> kernel
index 0324a6744097f77fe6d4e047019481368d69012b..47051b9cef307d7cc62e0ef12c17723fab538489 100644 (file)
     <para>At early boot and when the system manager configuration is reloaded, <filename>/etc/crypttab</filename> is
     translated into <filename>systemd-cryptsetup@.service</filename> units by
     <citerefentry><refentrytitle>systemd-cryptsetup-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
+
+    <para>In order to unlock a volume a password or binary key is
+    required. <filename>systemd-cryptsetup@.service</filename> tries to acquire a suitable password or binary
+    key via the following mechanisms, tried in order:</para>
+
+    <orderedlist>
+      <listitem><para>If a key file is explicitly configured (via the third column in
+      <filename>/etc/crypttab</filename>), a key read from it is used. If a PKCS#11 token is configured
+      (using the <varname>pkcs11-uri=</varname> option) the key is decrypted before use.</para></listitem>
+
+      <listitem><para>If no key file is configured explicitly this way, a key file is automatically loaded
+      from <filename>/etc/cryptsetup-keys.d/<replaceable>volume</replaceable>.key</filename> and
+      <filename>/run/cryptsetup-keys.d/<replaceable>volume</replaceable>.key</filename>, if present. Here
+      too, if a PKCS#11 token is configured, any key found this way is decrypted before
+      use.</para></listitem>
+
+      <listitem><para>If the <varname>try-empty-password</varname> option is specified it is then attempted
+      to unlock the volume with an empty password.</para></listitem>
+
+      <listitem><para>The kernel keyring is then checked for a suitable cached password from previous
+      attempts.</para></listitem>
+
+      <listitem><para>Finally, the user is queried for a password, possibly multiple times.</para></listitem>
+    </orderedlist>
+
+    <para>If no suitable key may be acquired via any of the mechanisms describes above, volume activation fails.</para>
   </refsect1>
 
   <refsect1>
index d599ac20f13f9deec96f970d2d355d25604633f8..77bdd80f32ab440e1a614fa58a7ee04ebb73e40b 100644 (file)
             <entry><varname>wsl</varname></entry>
             <entry><ulink url="https://docs.microsoft.com/en-us/windows/wsl/about">Windows Subsystem for Linux</ulink></entry>
           </row>
+
+          <row>
+            <entry><varname>proot</varname></entry>
+            <entry><ulink url="https://proot-me.github.io/">proot</ulink> userspace chroot/bind mount emulation</entry>
+          </row>
         </tbody>
       </tgroup>
     </table>
index 4c88bd5e666611f6fd18c3452d611b04e8a55883..708db01b4a61ca6c82709073fcfea7011a341f30 100644 (file)
@@ -46,7 +46,7 @@
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd.environment-generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+      <citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>
     </para>
   </refsect1>
 
index 560649f22c4b2ea49cf6161c10d3c402778711ca..0976394b662e2de92994745c0085bd0ea97c414b 100644 (file)
@@ -54,7 +54,7 @@
 
       <listitem><para>The system time zone</para></listitem>
 
-      <listitem><para>The system host name</para></listitem>
+      <listitem><para>The system hostname</para></listitem>
 
       <listitem><para>The machine ID of the system</para></listitem>
 
         </para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--image=<replaceable>path</replaceable></option></term>
+        <listitem><para>Takes a path to a disk image file or block device node. If specified all operations
+        are applied to file system in the indicated disk image. This is similar to <option>--root=</option>
+        but operates on file systems stored in disk images or block devices. The disk image should either
+        contain just a file system or a set of file systems within a GPT partition table, following the
+        <ulink url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partitions
+        Specification</ulink>. For further information on supported disk images, see
+        <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
+        switch of the same name.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>--locale=<replaceable>LOCALE</replaceable></option></term>
         <term><option>--locale-messages=<replaceable>LOCALE</replaceable></option></term>
         <term><option>--hostname=<replaceable>HOSTNAME</replaceable></option></term>
 
         <listitem><para>Sets the system hostname. The argument should
-        be a host name, compatible with DNS. This controls the
+        be a hostname, compatible with DNS. This controls the
         <citerefentry><refentrytitle>hostname</refentrytitle><manvolnum>5</manvolnum></citerefentry>
         configuration file.</para></listitem>
       </varlistentry>
       <varlistentry>
         <term><option>--root-password=<replaceable>PASSWORD</replaceable></option></term>
         <term><option>--root-password-file=<replaceable>PATH</replaceable></option></term>
+        <term><option>--root-password-hashed=<replaceable>HASHED_PASSWORD</replaceable></option></term>
 
-        <listitem><para>Sets the password of the system's root user.
-        This creates a
+        <listitem><para>Sets the password of the system's root user. This creates/modifies the
+        <citerefentry project='die-net'><refentrytitle>passwd</refentrytitle><manvolnum>5</manvolnum></citerefentry> and
+        <citerefentry project='die-net'><refentrytitle>shadow</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+        files. This setting exists in three forms: <option>--root-password=</option> accepts the password to
+        set directly on the command line, <option>--root-password-file=</option> reads it from a file and
+        <option>--root-password-hashed=</option> accepts an already hashed password on the command line. See
         <citerefentry project='die-net'><refentrytitle>shadow</refentrytitle><manvolnum>5</manvolnum></citerefentry>
-        file. This setting exists in two forms:
-        <option>--root-password=</option> accepts the password to set
-        directly on the command line, and
-        <option>--root-password-file=</option> reads it from a file.
-        Note that it is not recommended to specify passwords on the
-        command line, as other users might be able to see them simply
-        by invoking
-        <citerefentry project='die-net'><refentrytitle>ps</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para></listitem>
+        for more information on the format of the hashed password. Note that it is not recommended to specify
+        plaintext passwords on the command line, as other users might be able to see them simply by invoking
+        <citerefentry project='die-net'><refentrytitle>ps</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--root-shell=<replaceable>SHELL</replaceable></option></term>
+
+        <listitem><para>Sets the shell of the system's root user. This creates/modifies the
+        <citerefentry project='die-net'><refentrytitle>passwd</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+        file.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--kernel-command-line=<replaceable>CMDLINE</replaceable></option></term>
+
+        <listitem><para>Sets the system's kernel command line. This controls the
+        <filename>/etc/kernel/cmdline</filename> file which is used by
+        <citerefentry><refentrytitle>kernel-install</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+        </para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><option>--prompt-timezone</option></term>
         <term><option>--prompt-hostname</option></term>
         <term><option>--prompt-root-password</option></term>
+        <term><option>--prompt-root-shell</option></term>
 
         <listitem><para>Prompt the user interactively for a specific
         basic setting. Note that any explicit configuration settings
         <option>--prompt-keymap</option>,
         <option>--prompt-timezone</option>,
         <option>--prompt-hostname</option>,
-        <option>--prompt-root-password</option> in combination.</para>
+        <option>--prompt-root-password</option>,
+        <option>--prompt-root-shell</option> in combination.</para>
         </listitem>
       </varlistentry>
 
         <term><option>--copy-keymap</option></term>
         <term><option>--copy-timezone</option></term>
         <term><option>--copy-root-password</option></term>
+        <term><option>--copy-root-shell</option></term>
 
         <listitem><para>Copy a specific basic setting from the host.
         This only works in combination with <option>--root=</option>
         <option>--copy-locale</option>,
         <option>--copy-keymap</option>,
         <option>--copy-timezone</option>,
-        <option>--copy-root-password</option> in combination.</para>
+        <option>--copy-root-password</option>,
+        <option>--copy-root-shell</option> in combination.</para>
         </listitem>
       </varlistentry>
 
         <option>--root=</option>.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--force</option></term>
+
+        <listitem><para>systemd-firstboot doesn't modify existing files unless <option>--force</option>
+        is specified. For modifications to <filename>/etc/passwd</filename> and
+        <filename>/etc/shadow</filename>, systemd-firstboot only modifies the entry of the
+        <literal>root</literal> user instead of overwriting the entire file.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--delete-root-password</option></term>
+
+        <listitem><para>Removes the password of the system's root user, enabling login as root without a
+        password unless the root account is locked. Note that this is extremely insecure and hence this
+        option should not be used lightly.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--welcome=</option></term>
+
+        <listitem><para>Takes a boolean argument. By default when prompting the user for configuration
+        options a brief welcome text is shown before the first question is asked. Pass false to this option
+        to turn off the welcome text.</para></listitem>
+      </varlistentry>
+
       <xi:include href="standard-options.xml" xpointer="help" />
       <xi:include href="standard-options.xml" xpointer="version" />
     </variablelist>
index ab332588581b9c1ee39cefbe0591f276eb926b0e..59f18968608d74f9e30171947e35595637eec6a6 100644 (file)
         automatically populate <filename>/etc</filename>, and also <filename>/var</filename> in case of
         <literal>systemd.volatile=yes</literal>.</para></listitem>
       </varlistentry>
+
+      <varlistentry>
+        <term><varname>systemd.swap</varname></term>
+
+        <listitem><para>Takes a boolean argument or enables the option if specified
+        without an argument. If disabled, causes the generator to ignore
+        any swap devices configured in <filename>/etc/fstab</filename>.
+        Defaults to enabled.</para></listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 
index b0fa617d637899aabde433fdf129b3badbd5a955..78fdacccabac49abf0e8d06c57365b86ba66d569 100644 (file)
@@ -19,7 +19,7 @@
     <refname>systemd-gpt-auto-generator</refname>
     <refpurpose>Generator for automatically discovering and mounting root, <filename>/home/</filename>,
     <filename>/srv/</filename>, <filename>/var/</filename> and <filename>/var/tmp/</filename> partitions, as
-    well as discovering and enabling swap partitions, based on GPT partition type GUIDs.</refpurpose>
+    well as discovering and enabling swap partitions, based on GPT partition type GUIDs</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
index f79c2be2db1a2eda44731f3785baad8720ead9d4..ab8bcab8cb852a5ecb5f5dfbd762b9666e3b99f8 100644 (file)
     <citerefentry><refentrytitle>userdbctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
   </refsect1>
 
+  <refsect1>
+    <title>Key Management</title>
+
+    <para>User records are cryptographically signed with a public/private key pair (the signature is part of
+    the JSON record itself). For a user to be permitted to log in locally the public key matching the
+    signature of their user record must be installed. For a user record to be modified locally the private
+    key matching the signature must be installed locally, too. The keys are stored in the
+    <filename>/var/lib/systemd/home/</filename> directory:</para>
+
+    <variablelist>
+
+      <varlistentry>
+        <term><filename>/var/lib/systemd/home/local.private</filename></term>
+
+        <listitem><para>The private key of the public/private key pair used for local records. Currently,
+        only a single such key may be installed.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><filename>/var/lib/systemd/home/local.public</filename></term>
+
+        <listitem><para>The public key of the public/private key pair used for local records. Currently,
+        only a single such key may be installed.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><filename>/var/lib/systemd/home/*.public</filename></term>
+
+        <listitem><para>Additional public keys. Any users whose user records are signed with any of these keys
+        are permitted to log in locally. An arbitrary number of keys may be installed this
+        way.</para></listitem>
+      </varlistentry>
+    </variablelist>
+
+    <para>All key files listed above are in PEM format.</para>
+
+    <para>In order to migrate a home directory from a host <literal>foobar</literal> to another host
+    <literal>quux</literal> it is hence sufficient to copy
+    <filename>/var/lib/systemd/home/local.public</filename> from the host <literal>foobar</literal> to
+    <literal>quux</literal>, maybe calling the file on the destination
+    <filename>/var/lib/systemd/home/foobar.public</filename>, reflecting the origin of the key. If the user
+    record should be modifiable on <literal>quux</literal> the pair
+    <filename>/var/lib/systemd/home/local.public</filename> and
+    <filename>/var/lib/systemd/home/local.private</filename> need to be copied from <literal>foobar</literal>
+    to <literal>quux</literal>, and placed under the identical paths there, as currently only a single
+    private key is supported per host. Note of course that the latter means that user records
+    generated/signed before the key pair is copied in, lose their validity.</para>
+  </refsect1>
+
   <refsect1>
     <title>See Also</title>
     <para>
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>homed.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>homectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>userdbctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+      <citerefentry><refentrytitle>pam_systemd_home</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>userdbctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>org.freedesktop.home1</refentrytitle><manvolnum>5</manvolnum></citerefentry>
     </para>
   </refsect1>
 </refentry>
index 19bd4c06f2fe9dcd6183889a846ce30c4395b994..1aa32a61cedc3b9a0291d2349e482fed407d1b3f 100644 (file)
@@ -18,7 +18,7 @@
   <refnamediv>
     <refname>systemd-hostnamed.service</refname>
     <refname>systemd-hostnamed</refname>
-    <refpurpose>Host name bus mechanism</refpurpose>
+    <refpurpose>Daemon to control system hostname from programs</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
   <refsect1>
     <title>Description</title>
 
-    <para><filename>systemd-hostnamed</filename> is a system service
-    that may be used as a mechanism to change the system's hostname.
-    <filename>systemd-hostnamed</filename> is automatically activated
-    on request and terminates itself when it is unused.</para>
+    <para><filename>systemd-hostnamed.service</filename> is a system service that may be used to change the
+    system's hostname and related machine metadata from user programs. It is automatically activated on
+    request and terminates itself when unused.</para>
+
+    <para>It currently offers access to five variables:
+    <itemizedlist>
+      <listitem><para>The current hostname (Example: <literal>dhcp-192-168-47-11</literal>)</para>
+      </listitem>
+
+      <listitem><para>The static (configured) hostname (Example:
+      <literal>lennarts-computer</literal>)</para></listitem>
+
+      <listitem><para>The pretty hostname (Example: <literal>Lennart's Computer</literal>)</para>
+      </listitem>
+
+      <listitem><para>A suitable icon name for the local host (Example:
+      <literal>computer-laptop</literal>)</para></listitem>
+
+      <listitem><para>A chassis type (Example: <literal>tablet</literal>)</para>
+      </listitem>
+    </itemizedlist></para>
 
     <para>The tool
     <citerefentry><refentrytitle>hostnamectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
     is a command line client to this service.</para>
 
-    <para>See the <ulink
-    url="https://www.freedesktop.org/wiki/Software/systemd/hostnamed">
-    developer documentation</ulink> for information about the APIs
-    <filename>systemd-hostnamed</filename> provides.</para>
+    <para>See
+    <citerefentry><refentrytitle>org.freedesktop.hostname1</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    and
+    <citerefentry><refentrytitle>org.freedesktop.LogControl1</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    for a description of the D-Bus API.</para>
   </refsect1>
 
   <refsect1>
index 355e5b0b98190a2a0fb53380a84b4e32b38d25cd..fa3c3a812f05e035fad9c5c80d1d67194c8b249d 100644 (file)
     <command>pull-raw</command>, <command>pull-tar</command>, <command>import-raw</command>,
     <command>import-tar</command>, <command>export-raw</command>, and <command>export-tar</command> commands.</para>
 
-    <para>See the
-    <ulink url="https://www.freedesktop.org/wiki/Software/systemd/importd">
-    importd D-Bus API Documentation</ulink> for information about the
-    APIs <filename>systemd-importd</filename> provides.</para>
+    <para>See
+    <citerefentry><refentrytitle>org.freedesktop.import1</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    and
+    <citerefentry><refentrytitle>org.freedesktop.LogControl1</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    for a description of the D-Bus API.</para>
   </refsect1>
 
   <refsect1>
index 0345936fce0a2cbce2ed69a639f7432ab85cb174..ea93efd3f0153f6147455435f94e521ed8bf28cc 100644 (file)
@@ -3,7 +3,7 @@
   "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
 <!-- SPDX-License-Identifier: LGPL-2.1+ -->
 
-<refentry id="systemd-initctl.service">
+<refentry id="systemd-initctl.service" conditional='HAVE_SYSV_COMPAT'>
 
   <refentryinfo>
     <title>systemd-initctl.service</title>
index 633b4cb55ba60f1029d15e3c1c96fd4f120c89c7..a7c50f382f0fe8a538259ad434a99de081aeefd1 100644 (file)
       <varlistentry>
         <term><option>--cert=</option></term>
 
-        <listitem><para>Specify the path to a file containing a server
-        certificate in PEM format. This option switches
-        <command>systemd-journal-gatewayd</command> into HTTPS mode
-        and must be used together with
+        <listitem><para>Specify the path to a file or <constant>AF_UNIX</constant> stream socket to read the
+        server certificate from. The certificate must be in PEM format. This option switches
+        <command>systemd-journal-gatewayd</command> into HTTPS mode and must be used together with
         <option>--key=</option>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><option>--key=</option></term>
 
-        <listitem><para>Specify the path to a file containing a server
-        key in PEM format corresponding to the certificate specified
-        with <option>--cert=</option>.</para></listitem>
+        <listitem><para>Specify the path to a file or <constant>AF_UNIX</constant> stream socket to read the
+        server key corresponding to the certificate specified with <option>--cert=</option> from. The key
+        must be in PEM format.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><option>--trust=</option></term>
 
-        <listitem><para>Specify the path to a file containing a
-        CA certificate in PEM format.</para></listitem>
+        <listitem><para>Specify the path to a file or <constant>AF_UNIX</constant> stream socket to read a CA
+        certificate from. The certificate must be in PEM format.</para></listitem>
       </varlistentry>
 
       <varlistentry>
     </para>
 
     <para>where
-      <option>cursor</option> is a cursor string,
-      <option>num_skip</option> is an integer,
-      <option>num_entries</option> is an unsigned integer.
+      <replaceable>cursor</replaceable> is a cursor string,
+      <replaceable>num_skip</replaceable> is an integer,
+      <replaceable>num_entries</replaceable> is an unsigned integer.
     </para>
 
     <para>Range defaults to all available events.</para>
index b28092d18c324044792ad4820ed3bee9f848c9e5..1db0128f746d780315e425096ca51bc91f7c9f0f 100644 (file)
       <varlistentry>
         <term><option>--key=</option></term>
 
-        <listitem><para>
-          Takes a path to a SSL key file in PEM format.
-          Defaults to <filename>&CERTIFICATE_ROOT;/private/journal-remote.pem</filename>.
-          This option can be used with <option>--listen-https=</option>.
-        </para></listitem>
+        <listitem><para> Takes a path to a SSL key file in PEM format. Defaults to
+        <filename>&CERTIFICATE_ROOT;/private/journal-remote.pem</filename>. This option can be used with
+        <option>--listen-https=</option>. If the path refers to an <constant>AF_UNIX</constant> stream socket
+        in the file system a connection is made to it and the key read from it.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><option>--cert=</option></term>
 
-        <listitem><para>
-          Takes a path to a SSL certificate file in PEM format.
-          Defaults to <filename>&CERTIFICATE_ROOT;/certs/journal-remote.pem</filename>.
-          This option can be used with <option>--listen-https=</option>.
-        </para></listitem>
+        <listitem><para> Takes a path to a SSL certificate file in PEM format. Defaults to
+        <filename>&CERTIFICATE_ROOT;/certs/journal-remote.pem</filename>. This option can be used with
+        <option>--listen-https=</option>. If the path refers to an <constant>AF_UNIX</constant> stream socket
+        in the file system a connection is made to it and the certificate read from it.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><option>--trust=</option></term>
 
-        <listitem><para>
-          Takes a path to a SSL CA certificate file in PEM format,
-          or <option>all</option>. If <option>all</option> is set,
-          then certificate checking will be disabled.
-          Defaults to <filename>&CERTIFICATE_ROOT;/ca/trusted.pem</filename>.
-          This option can be used with <option>--listen-https=</option>.
-        </para></listitem>
+        <listitem><para> Takes a path to a SSL CA certificate file in PEM format, or <option>all</option>. If
+        <option>all</option> is set, then certificate checking will be disabled. Defaults to
+        <filename>&CERTIFICATE_ROOT;/ca/trusted.pem</filename>. This option can be used with
+        <option>--listen-https=</option>. If the path refers to an <constant>AF_UNIX</constant> stream socket
+        in the file system a connection is made to it and the certificate read from it.</para></listitem>
       </varlistentry>
 
       <varlistentry>
index 1b8b0be6770b51dbf03ff69f48e365327c2ce88b..174bef803f45401f3d231f5df968abea3b9d262d 100644 (file)
         <term><option>--key=</option></term>
 
         <listitem><para>
-          Takes a path to a SSL key file in PEM format.
+          Takes a path to a SSL key file in PEM format, or <option>-</option>.
+          If <option>-</option> is set, then client certificate authentication checking
+          will be disabled.
           Defaults to <filename>&CERTIFICATE_ROOT;/private/journal-upload.pem</filename>.
         </para></listitem>
       </varlistentry>
         <term><option>--cert=</option></term>
 
         <listitem><para>
-          Takes a path to a SSL certificate file in PEM format.
+          Takes a path to a SSL certificate file in PEM format, or <option>-</option>.
+          If <option>-</option> is set, then client certificate authentication checking
+          will be disabled.
           Defaults to <filename>&CERTIFICATE_ROOT;/certs/journal-upload.pem</filename>.
         </para></listitem>
       </varlistentry>
         <term><option>--trust=</option></term>
 
         <listitem><para>
-          Takes a path to a SSL CA certificate file in PEM format,
-          or <option>all</option>. If <option>all</option> is set,
-          then certificate checking will be disabled.
+          Takes a path to a SSL CA certificate file in PEM format, or <option>-</option>/<option>all</option>.
+          If <option>-</option>/<option>all</option> is set, then certificate checking will be disabled.
           Defaults to <filename>&CERTIFICATE_ROOT;/ca/trusted.pem</filename>.
         </para></listitem>
       </varlistentry>
@@ -264,7 +267,7 @@ openssl ca -batch -config ca.conf -notext -in $CLIENT.csr -out $CLIENT.pem
       those files can be specified using
       <varname>TrustedCertificateFile=</varname>,
       <varname>ServerCertificateFile=</varname>,
-      <varname>ServerKeyFile=</varname>, in
+      and <varname>ServerKeyFile=</varname> in
       <filename>/etc/systemd/journal-remote.conf</filename> and
       <filename>/etc/systemd/journal-upload.conf</filename>,
       respectively. The default locations can be queried by using
index f4ce4e4fe2079ac8d58461f3e54af71f3f33165d..6b7a0bc4b998a60846de4291e17b943a218de368 100644 (file)
@@ -106,18 +106,19 @@ systemd-tmpfiles --create --prefix /var/log/journal</programlisting>
     errors. In order to react gracefully in this case it is recommended that programs logging to standard output/error
     ignore such errors. If the <constant>SIGPIPE</constant> UNIX signal handler is not blocked or turned off, such
     write attempts will also result in such process signals being generated, see
-    <citerefentry><refentrytitle>signal</refentrytitle><manvolnum>7</manvolnum></citerefentry>. To mitigate this issue,
-    systemd service manager explicitly turns off the <constant>SIGPIPE</constant> signal for all invoked processes by
-    default (this may be changed for each unit individually via the <varname>IgnoreSIGPIPE=</varname> option, see
+    <citerefentry project='man-pages'><refentrytitle>signal</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
+    To mitigate this issue, systemd service manager explicitly turns off the <constant>SIGPIPE</constant>
+    signal for all invoked processes by default (this may be changed for each unit individually via the
+    <varname>IgnoreSIGPIPE=</varname> option, see
     <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
-    details). After the standard output/standard error streams have been terminated they may not be recovered until the
-    services they are associated with are restarted. Note that during normal operation,
-    <filename>systemd-journald.service</filename> stores copies of the file descriptors for those streams in the
-    service manager. If <filename>systemd-journald.service</filename> is restarted using <command>systemctl
-    restart</command> or equivalent operation instead of a pair of separate <command>systemctl stop</command> and
-    <command>systemctl start</command> commands (or equivalent operations), these stream connections are not terminated
-    and survive the restart. It is thus safe to restart <filename>systemd-journald.service</filename>, but stopping it
-    is not recommended.</para>
+    details). After the standard output/standard error streams have been terminated they may not be recovered
+    until the services they are associated with are restarted. Note that during normal operation,
+    <filename>systemd-journald.service</filename> stores copies of the file descriptors for those streams in
+    the service manager. If <filename>systemd-journald.service</filename> is restarted using
+    <command>systemctl restart</command> or equivalent operation instead of a pair of separate
+    <command>systemctl stop</command> and <command>systemctl start</command> commands (or equivalent
+    operations), these stream connections are not terminated and survive the restart. It is thus safe to
+    restart <filename>systemd-journald.service</filename>, but stopping it is not recommended.</para>
 
     <para>Note that the log record metadata for records transferred via such standard output/error streams reflect the
     metadata of the peer the stream was originally created for. If the stream connection is passed on to other
index f620aade11dda15432dbb32e489fad6d8685d6b9..49eeeffee6e368e55c977d2dfc07eb06f50ef418 100644 (file)
@@ -29,7 +29,7 @@
   <refsect1>
     <title>Description</title>
 
-    <para><filename>systemd-localed</filename> is a system service
+    <para><filename>systemd-localed.service</filename> is a system service
     that may be used as mechanism to change the system locale
     settings, as well as the console key mapping and default X11 key
     mapping. <filename>systemd-localed</filename> is automatically
     <citerefentry project='man-pages'><refentrytitle>localectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
     is a command line client to this service.</para>
 
-    <para>See the <ulink
-    url="https://www.freedesktop.org/wiki/Software/systemd/localed">
-    developer documentation</ulink> for information about the APIs
-    <filename>systemd-localed</filename> provides.</para>
+    <para>See
+    <citerefentry><refentrytitle>org.freedesktop.locale1</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    and
+    <citerefentry><refentrytitle>org.freedesktop.LogControl1</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    for a description of the D-Bus API.</para>
   </refsect1>
 
   <refsect1>
index a66c53d076acd252e2ec13e40d84081adf5d02ae..bbfcad4af6b354303b00cdcec9d27dc9d9e1930f 100644 (file)
     for information about the basic concepts of logind
     such as users, sessions and seats.</para>
 
-    <para>See the <ulink
-    url="https://www.freedesktop.org/wiki/Software/systemd/logind">
-    logind D-Bus API Documentation</ulink> for information about the
-    APIs <filename>systemd-logind</filename> provides.</para>
+    <para>See
+    <citerefentry><refentrytitle>org.freedesktop.login1</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    and
+    <citerefentry><refentrytitle>org.freedesktop.LogControl1</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    for information about the D-Bus APIs <filename>systemd-logind</filename> provides.</para>
 
     <para>For more information on the inhibition logic see the <ulink
     url="https://www.freedesktop.org/wiki/Software/systemd/inhibit">Inhibitor
     Lock Developer Documentation</ulink>.</para>
+
+    <para>If you are interested in writing a display manager that makes use of logind, please have look at
+    <ulink url="https://www.freedesktop.org/wiki/Software/systemd/writing-display-managers">Writing Display
+    Managers</ulink>.
+    If you are interested in writing a desktop environment that makes use of logind, please have look at
+    <ulink url="http://www.freedesktop.org/wiki/Software/systemd/writing-desktop-environments">Writing
+    Desktop Environments</ulink>.</para>
   </refsect1>
 
   <refsect1>
       <citerefentry><refentrytitle>systemd-user-sessions.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>loginctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>logind.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>pam_systemd</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+      <citerefentry><refentrytitle>pam_systemd</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd-login</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     </para>
   </refsect1>
index 8771434e5a2f3b79999beddd657e4ed01a299179..a976c606bd8545649de9ea4878d4174a2aed1e1f 100644 (file)
   <refsect1>
     <title>Description</title>
 
-    <para><command>systemd-machined</command> is a system service that
-    keeps track of virtual machines and containers, and processes
-    belonging to them.</para>
+    <para><command>systemd-machined</command> is a system service that keeps track of locally running virtual
+    machines and containers.</para>
+
+    <para><command>systemd-machined</command> is useful for registering and keeping track of both OS
+    containers (containers that share the host kernel but run a full init system of their own and behave in
+    most regards like a full virtual operating system rather than just one virtualized app) and full virtual
+    machines (virtualized hardware running normal operating systems and possibly different kernels).</para>
+
+    <para><command>systemd-machined</command> should <emphasis>not</emphasis> be used for registering/keeping
+    track of application sandbox containers. A <emphasis>machine</emphasis> in the context of
+    <command>systemd-machined</command> is supposed to be an abstract term covering both OS containers and
+    full virtual machines, but not application sandboxes.</para>
+
+    <para>Machines registered with machined are exposed in various ways in the system. For example:
+    <itemizedlist>
+      <listitem><para>Tools like
+      <citerefentry project='man-pages'><refentrytitle>ps</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+      will show to which machine a specific process belongs in a column of
+      its own, and so will
+      <ulink url="https://help.gnome.org/users/gnome-system-monitor/">gnome-system-monitor</ulink> or
+      <citerefentry><refentrytitle>systemd-cgls</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
+      </listitem>
+
+      <listitem><para>systemd's various tools
+      (<citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>journalctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>loginctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>hostnamectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>timedatectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>localectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>machinectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>, ...)
+      support the <option>-M</option> switch to operate on local containers instead of the host system.
+      </para></listitem>
+
+      <listitem><para><command>systemctl list-machines</command> will show the system state of all local
+      containers, connecting to the container's init system for that.</para></listitem>
+
+      <listitem><para>systemctl's <option>--recursive</option> switch has the effect of not only showing the
+      locally running services, but recursively showing the services of all registered containers.</para></listitem>
+
+      <listitem><para>The <command>machinectl</command> command provides access to a number of useful
+      operations on registered containers, such as introspecting them, rebooting, shutting them down, and
+      getting a login prompt on them.</para></listitem>
+
+      <listitem><para>The
+      <citerefentry><refentrytitle>sd-bus</refentrytitle><manvolnum>3</manvolnum></citerefentry> library
+      exposes the
+      <citerefentry><refentrytitle>sd_bus_open_system_machine</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+      call to connect to the system bus of any registered container.</para></listitem>
+
+      <listitem><para>The
+      <citerefentry><refentrytitle>nss-mymachines</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+      module makes sure all registered containers can be resolved via normal glibc
+      <citerefentry project='man-pages'><refentrytitle>gethostbyname</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+      or
+      <citerefentry project='man-pages'><refentrytitle>getaddrinfo</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+      calls.</para></listitem>
+    </itemizedlist></para>
 
     <para>See
     <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>
     for some examples on how to run containers with OS tools.</para>
 
-    <para>Use
-    <citerefentry><refentrytitle>nss-mymachines</refentrytitle><manvolnum>8</manvolnum></citerefentry>
-    to make the names of local containers known to
-    <command>systemd-machined</command> locally resolvable as host
-    names.</para>
+    <para>If you are interested in writing a VM or container manager that makes use of machined, please have
+    look at <ulink url="https://www.freedesktop.org/wiki/Software/systemd/writing-vm-managers">Writing
+    Virtual Machine or Container Managers</ulink>. Also see the <ulink
+    url="https://www.freedesktop.org/wiki/Software/systemd/ControlGroupInterface/">New Control Group
+    Interfaces</ulink>.</para>
+
+    <para>The daemon provides both a C library interface
+    (which is shared with <citerefentry><refentrytitle>systemd-logind.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>)
+    as well as a D-Bus interface.
+    The library interface may be used to introspect and watch the state of virtual machines/containers.
+    The bus interface provides the same but in addition may also be used to register or terminate
+    machines.
+    For more information please consult
+    <citerefentry><refentrytitle>sd-login</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    and
+    <citerefentry><refentrytitle>org.freedesktop.machine1</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+    and
+    <citerefentry><refentrytitle>org.freedesktop.LogControl1</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    </para>
+
+    <para>A small companion daemon
+    <citerefentry><refentrytitle>systemd-importd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    is also available, which implements importing, exporting, and downloading of container and VM images.
+    </para>
 
-    <para>See the
-    <ulink url="https://www.freedesktop.org/wiki/Software/systemd/machined">
-    machined D-Bus API Documentation</ulink> for information about the
-    APIs <filename>systemd-machined</filename> provides.</para>
+    <para>For each container registered with <filename>systemd-machined.service</filename> that employs user
+    namespacing, users/groups are synthesized for the used UIDs/GIDs. These are made available to the system
+    using the <ulink url="https://systemd.io/USER_GROUP_API">User/Group Record Lookup API via
+    Varlink</ulink>, and thus may be resolved with
+    <citerefentry><refentrytitle>userdbctl</refentrytitle><manvolnum>1</manvolnum></citerefentry> or the
+    usual glibc NSS calls.</para>
   </refsect1>
 
   <refsect1>
index ca9edef367067a781ebe21bf575e8a3ba6840e17..e960609c6cf812cc08b9ae571a9635a3ef3606c2 100644 (file)
   <refsect1>
     <title>Description</title>
 
-    <para><filename>systemd-modules-load.service</filename> is an
-    early boot service that loads kernel modules based on static
-    configuration.</para>
+    <para><filename>systemd-modules-load.service</filename> is an early boot service that loads kernel
+    modules. It reads static configuration from files in <filename>/usr</filename> and
+    <filename>/etc</filename>, but also runtime configuration from <filename>/run</filename> and the kernel
+    command line (see below).</para>
 
     <para>See
-    <citerefentry><refentrytitle>modules-load.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>
-    for information about the configuration of this service.</para>
-
+    <citerefentry><refentrytitle>modules-load.d</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
+    information about the configuration format of this service and paths where configuration files can be
+    created.</para>
   </refsect1>
 
   <refsect1>
index a6e6dd9b483bd7baede8d74b7c73caeb0318a8c6..f1089eed3b0f3d6ed58fff63be41dca84979c290 100644 (file)
     <replaceable>WHERE</replaceable>.</para>
 
     <para>In many ways, <command>systemd-mount</command> is similar to the lower-level
-    <citerefentry project='man-pages'><refentrytitle>mount</refentrytitle><manvolnum>8</manvolnum></citerefentry> command, however instead
-    of executing the mount operation directly and immediately, <command>systemd-mount</command> schedules it through
-    the service manager job queue, so that it may pull in further dependencies (such as parent mounts, or a file system
-    checker to execute a priori), and may make use of the auto-mounting logic.</para>
+    <citerefentry project='man-pages'><refentrytitle>mount</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    command, however instead of executing the mount operation directly and immediately,
+    <command>systemd-mount</command> schedules it through the service manager job queue, so that it may pull
+    in further dependencies (such as parent mounts, or a file system checker to execute a priori), and may
+    make use of the auto-mounting logic.</para>
 
     <para>The command takes either one or two arguments. If only one argument is specified it should refer to
     a block device or regular file containing a file system (e.g. <literal>/dev/sdb1</literal> or
     label and other metadata, and is mounted to a directory below <filename>/run/media/system/</filename>
     whose name is generated from the file system label. In this mode the block device or image file must
     exist at the time of invocation of the command, so that it may be probed. If the device is found to be a
-    removable block device (e.g. a USB stick) an automount point instead of a regular mount point is created
+    removable block device (e.g. a USB stick), an automount point is created instead of a regular mount point
     (i.e. the <option>--automount=</option> option is implied, see below).</para>
 
-    <para>If two arguments are specified the first indicates the mount source (the <replaceable>WHAT</replaceable>) and
-    the second indicates the path to mount it on (the <replaceable>WHERE</replaceable>). In this mode no probing of the
-    source is attempted, and a backing device node doesn't have to exist yet. However, if this mode is combined with
-    <option>--discover</option>, device node probing for additional metadata is enabled, and – much like in the
-    single-argument case discussed above – the specified device has to exist at the time of invocation of the
-    command.</para>
+    <para>If two arguments are specified, the first indicates the mount source (the
+    <replaceable>WHAT</replaceable>) and the second indicates the path to mount it on (the
+    <replaceable>WHERE</replaceable>). In this mode no probing of the source is attempted, and a backing
+    device node doesn't have to exist. However, if this mode is combined with <option>--discover</option>,
+    device node probing for additional metadata is enabled, and – much like in the single-argument case
+    discussed above – the specified device has to exist at the time of invocation of the command.</para>
 
     <para>Use the <option>--list</option> command to show a terse table of all local, known block devices with file
     systems that may be mounted with this command.</para>
index fcb3c69ffbfd611609fa7f7f22fae7337bad47d1..4a3f78a9e57915e18ef427a19487baced68ed262 100644 (file)
@@ -96,7 +96,7 @@
     <para>
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>dracut</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+      <citerefentry project='man-pages'><refentrytitle>dracut</refentrytitle><manvolnum>8</manvolnum></citerefentry>
     </para>
   </refsect1>
 
index 94a7bfd5ef6f50c8fd20f5f329a34c881758c52a..2913bb953df0f68ae86dd61679202aa4c0fe1357 100644 (file)
@@ -92,7 +92,7 @@
       <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd-networkd-wait-online.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>systemd-networkd-generator.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+      <citerefentry><refentrytitle>systemd-network-generator.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
     </para>
   </refsect1>
 
index 89060680f5efa1132446b4442d4fbb4a01b1ecea..6d583003baca6c1e531da3e91011f17ac0b446f7 100644 (file)
     off the process, i.e. on all processes that match <varname>NotifyAccess=</varname><option>main</option> or
     <varname>NotifyAccess=</varname><option>exec</option>. Conversely, if an auxiliary process of the unit sends an
     <function>sd_notify()</function> message and immediately exits, the service manager might not be able to properly
-    attribute the message to the unit, and thus will ignore it, even if
-    <varname>NotifyAccess=</varname><option>all</option> is set for it.</para>
-
-    <para><command>systemd-notify</command> will first attempt to invoke <function>sd_notify()</function> pretending to
-    have the PID of the invoking process. This will only succeed when invoked with sufficient privileges. On failure,
-    it will then fall back to invoking it under its own PID. This behaviour is useful in order that when the tool is
-    invoked from a shell script the shell process — and not the <command>systemd-notify</command> process — appears as
-    sender of the message, which in turn is helpful if the shell process is the main process of a service, due to the
-    limitations of <varname>NotifyAccess=</varname><option>all</option> described above.</para>
+    attribute the message to the unit, and thus will ignore it, even if <varname>NotifyAccess=</varname><option>all
+    </option> is set for it. When <option>--no-block</option> is used, all synchronization for reception of notifications
+    is disabled, and hence the aforementioned race may occur if the invoking process is not the service manager or spawned
+    by the service manager.</para>
+
+    <para>Hence, <command>systemd-notify</command> will first attempt to invoke <function>sd_notify()</function>
+    pretending to have the PID of the invoking process. This will only succeed when invoked with sufficient privileges.
+    On failure, it will then fall back to invoking it under its own PID. This behaviour is useful in order that when
+    the tool is invoked from a shell script the shell process — and not the <command>systemd-notify</command> process
+    — appears as sender of the message, which in turn is helpful if the shell process is the main process of a service,
+    due to the limitations of <varname>NotifyAccess=</varname><option>all</option>. Use the <option>--pid=</option>
+    switch to tweak this behaviour.</para>
+
   </refsect1>
 
   <refsect1>
       <varlistentry>
         <term><option>--pid=</option></term>
 
-        <listitem><para>Inform the init system about the main PID of
-        the daemon. Takes a PID as argument. If the argument is
-        omitted, the PID of the process that invoked
-        <command>systemd-notify</command> is used. This is equivalent
-        to <command>systemd-notify MAINPID=$PID</command>. For details
-        about the semantics of this option see
+        <listitem><para>Inform the service manager about the main PID of the daemon. Takes a PID as
+        argument. If the argument is specified as <literal>auto</literal> or omitted, the PID of the process
+        that invoked <command>systemd-notify</command> is used, except if that's the service manager. If the
+        argument is specified as <literal>self</literal>, the PID of the <command>systemd-notify</command>
+        command itself is used, and if <literal>parent</literal> is specified the calling process' PID is
+        used — even if it is the service manager. This is equivalent to <command>systemd-notify
+        MAINPID=$PID</command>. For details about the semantics of this option see
         <citerefentry><refentrytitle>sd_notify</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para></listitem>
       </varlistentry>
 
         with systemd.  </para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--no-block</option></term>
+
+        <listitem><para>Do not synchronously wait for the requested operation to finish.
+        Use of this option is only recommended when <command>systemd-notify</command>
+        is spawned by the service manager, or when the invoking process is directly spawned
+        by the service manager and has enough privileges to allow <command>systemd-notify
+        </command> to send the notification on its behalf. Sending notifications with
+        this option set is prone to race conditions in all other cases.</para></listitem>
+      </varlistentry>
+
       <xi:include href="standard-options.xml" xpointer="help" />
       <xi:include href="standard-options.xml" xpointer="version" />
     </variablelist>
index b269b9917034381d033f13a184a88c8ba216ca80..69558ac85cbb5a5deb66063b1a2ab54cf32f0e41 100644 (file)
@@ -1,8 +1,8 @@
 <?xml version='1.0'?>
 <!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
   "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
-<!ENTITY fedora_latest_version "31">
-<!ENTITY fedora_cloud_release "1.9">
+<!ENTITY fedora_latest_version "32">
+<!ENTITY fedora_cloud_release "1.6">
 ]>
 <!-- SPDX-License-Identifier: LGPL-2.1+ -->
 
         all subdirectories and subvolumes below it, but excluding any sub-mounts. May not be specified
         together with <option>--image=</option> or <option>--ephemeral</option>.</para>
 
-        <para>Note that this switch leaves host name, machine ID and
+        <para>Note that this switch leaves hostname, machine ID and
         all other settings that could identify the instance
         unmodified.</para></listitem>
       </varlistentry>
         <listitem><para>If specified, the container is run with a temporary snapshot of its file system that is removed
         immediately when the container terminates. May not be specified together with
         <option>--template=</option>.</para>
-        <para>Note that this switch leaves host name, machine ID and all other settings that could identify
+        <para>Note that this switch leaves hostname, machine ID and all other settings that could identify
         the instance unmodified. Please note that — as with <option>--template=</option> — taking the
         temporary snapshot is more efficient on file systems that support subvolume snapshots or 'reflinks'
         natively (<literal>btrfs</literal> or new <literal>xfs</literal>) than on more traditional file
         hash partitions are set up if the root hash for them is specified using the <option>--root-hash=</option>
         option.</para>
 
+        <para>Single file system images (i.e. file systems without a surrounding partition table) can be opened using
+        dm-verity if the integrity data is passed using the <option>--root-hash=</option> and
+        <option>--verity-data=</option> (and optionally <option>--root-hash-sig=</option>) options.</para>
+
         <para>Any other partitions, such as foreign partitions or swap partitions are not mounted. May not be specified
         together with <option>--directory=</option>, <option>--template=</option>.</para></listitem>
       </varlistentry>
         project='man-pages'><refentrytitle>xattr</refentrytitle><manvolnum>7</manvolnum></citerefentry>), then the root
         hash is read from it, also as formatted hexadecimal characters. If the extended file attribute is not found (or
         is not supported by the underlying file system), but a file with the <filename>.roothash</filename> suffix is
-        found next to the image file, bearing otherwise the same name, the root hash is read from it and automatically
-        used, also as formatted hexadecimal characters.</para></listitem>
+        found next to the image file, bearing otherwise the same name (except if the image has the
+        <filename>.raw</filename> suffix, in which case the root hash file must not have it in its name), the root hash
+        is read from it and automatically used, also as formatted hexadecimal characters.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--root-hash-sig=</option></term>
+
+        <listitem><para>Takes a PKCS7 formatted binary signature of the <option>--root-hash=</option> option as a path
+        to a DER encoded signature file or as an ASCII base64 string encoding of the DER encoded signature, prefixed
+        by <literal>base64:</literal>. The dm-verity volume will only be opened if the signature of the root hash hex
+        string is valid and done by a public key present in the kernel keyring. If this option is not specified, but a
+        file with the <filename>.roothash.p7s</filename> suffix is found next to the image file, bearing otherwise the
+        same name (except if the image has the <filename>.raw</filename> suffix, in which case the signature file must
+        not have it in its name), the signature is read from it and automatically used.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--verity-data=</option></term>
+
+        <listitem><para>Takes the path to a data integrity (dm-verity) file. This option enables data integrity checks
+        using dm-verity, if a root-hash is passed and if the used image itself does not contains the integrity data.
+        The integrity data must be matched by the root hash. If this option is not specified, but a file with the
+        <filename>.verity</filename> suffix is found next to the image file, bearing otherwise the same name (except if
+        the image has the <filename>.raw</filename> suffix, in which case the verity data file must not have it in its name),
+        the verity data is read from it and automatically used.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><option>-u</option></term>
         <term><option>--user=</option></term>
 
-        <listitem><para>After transitioning into the container, change
-        to the specified user-defined in the container's user
-        database. Like all other systemd-nspawn features, this is not
-        a security feature and provides protection against accidental
-        destructive operations only.</para></listitem>
+        <listitem><para>After transitioning into the container, change to the specified user defined in the
+        container's user database. Like all other systemd-nspawn features, this is not a security feature and
+        provides protection against accidental destructive operations only.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         With option <option>yes</option> systemd-nspawn waits for the
         <literal>READY=1</literal> message from the init process in the container
         before sending its own to systemd. For more details about notifications
-        see <citerefentry><refentrytitle>sd_notify</refentrytitle><manvolnum>3</manvolnum></citerefentry>).</para></listitem>
+        see <citerefentry><refentrytitle>sd_notify</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para></listitem>
       </varlistentry>
     </variablelist>
 
       <varlistentry>
         <term><option>--no-new-privileges=</option></term>
 
-        <listitem><para>Takes a boolean argument. Specifies the value of the <constant>PR_SET_NO_NEW_PRIVS</constant>
-        flag for the container payload. Defaults to off. When turned on the payload code of the container cannot
-        acquire new privileges, i.e. the "setuid" file bit as well as file system capabilities will not have an effect
-        anymore. See <citerefentry
-        project='man-pages'><refentrytitle>prctl</refentrytitle><manvolnum>2</manvolnum></citerefentry> for details
-        about this flag. </para></listitem>
+        <listitem><para>Takes a boolean argument. Specifies the value of the
+        <constant>PR_SET_NO_NEW_PRIVS</constant> flag for the container payload. Defaults to off. When turned
+        on the payload code of the container cannot acquire new privileges, i.e. the "setuid" file bit as
+        well as file system capabilities will not have an effect anymore. See <citerefentry
+        project='man-pages'><refentrytitle>prctl</refentrytitle><manvolnum>2</manvolnum></citerefentry> for
+        details about this flag. </para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><option>--system-call-filter=</option></term>
-
-        <listitem><para>Alter the system call filter applied to containers. Takes a space-separated list of system call
-        names or group names (the latter prefixed with <literal>@</literal>, as listed by the
-        <command>syscall-filter</command> command of
+        <term><option>--system-call-filter=</option></term> <listitem><para>Alter the system call filter
+        applied to containers. Takes a space-separated list of system call names or group names (the latter
+        prefixed with <literal>@</literal>, as listed by the <command>syscall-filter</command> command of
         <citerefentry><refentrytitle>systemd-analyze</refentrytitle><manvolnum>1</manvolnum></citerefentry>). Passed
-        system calls will be permitted. The list may optionally be prefixed by <literal>~</literal>, in which case all
-        listed system calls are prohibited. If this command line option is used multiple times the configured lists are
-        combined. If both a positive and a negative list (that is one system call list without and one with the
-        <literal>~</literal> prefix) are configured, the negative list takes precedence over the positive list. Note
-        that <command>systemd-nspawn</command> always implements a system call whitelist (as opposed to a blacklist),
-        and this command line option hence adds or removes entries from the default whitelist, depending on the
-        <literal>~</literal> prefix. Note that the applied system call filter is also altered implicitly if additional
-        capabilities are passed using the <command>--capabilities=</command>.</para></listitem>
+        system calls will be permitted. The list may optionally be prefixed by <literal>~</literal>, in which
+        case all listed system calls are prohibited. If this command line option is used multiple times the
+        configured lists are combined. If both a positive and a negative list (that is one system call list
+        without and one with the <literal>~</literal> prefix) are configured, the negative list takes
+        precedence over the positive list. Note that <command>systemd-nspawn</command> always implements a
+        system call allow list (as opposed to a deny list!), and this command line option hence adds or
+        removes entries from the default allow list, depending on the <literal>~</literal> prefix. Note that
+        the applied system call filter is also altered implicitly if additional capabilities are passed using
+        the <command>--capabilities=</command>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
       <varlistentry>
         <term><option>--resolv-conf=</option></term>
 
-        <listitem><para>Configures how <filename>/etc/resolv.conf</filename> inside of the container (i.e. DNS
-        configuration synchronization from host to container) shall be handled. Takes one of <literal>off</literal>,
-        <literal>copy-host</literal>, <literal>copy-static</literal>, <literal>bind-host</literal>,
-        <literal>bind-static</literal>, <literal>delete</literal> or <literal>auto</literal>. If set to
-        <literal>off</literal> the <filename>/etc/resolv.conf</filename> file in the container is left as it is
-        included in the image, and neither modified nor bind mounted over. If set to <literal>copy-host</literal>, the
-        <filename>/etc/resolv.conf</filename> file from the host is copied into the container. Similar, if
-        <literal>bind-host</literal> is used, the file is bind mounted from the host into the container. If set to
-        <literal>copy-static</literal> the static <filename>resolv.conf</filename> file supplied with
-        <citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry> is
-        copied into the container, and correspondingly <literal>bind-static</literal> bind mounts it there. If set to
-        <literal>delete</literal> the <filename>/etc/resolv.conf</filename> file in the container is deleted if it
-        exists. Finally, if set to <literal>auto</literal> the file is left as it is if private networking is turned on
-        (see <option>--private-network</option>). Otherwise, if <filename>systemd-resolved.service</filename> is
-        connectible its static <filename>resolv.conf</filename> file is used, and if not the host's
-        <filename>/etc/resolv.conf</filename> file is used. In the latter cases the file is copied if the image is
-        writable, and bind mounted otherwise. It's recommended to use <literal>copy</literal> if the container shall be
-        able to make changes to the DNS configuration on its own, deviating from the host's settings. Otherwise
-        <literal>bind</literal> is preferable, as it means direct changes to <filename>/etc/resolv.conf</filename> in
-        the container are not allowed, as it is a read-only bind mount (but note that if the container has enough
-        privileges, it might simply go ahead and unmount the bind mount anyway). Note that both if the file is bind
-        mounted and if it is copied no further propagation of configuration is generally done after the one-time early
-        initialization (this is because the file is usually updated through copying and renaming). Defaults to
+        <listitem><para>Configures how <filename>/etc/resolv.conf</filename> inside of the container shall be
+        handled (i.e. DNS configuration synchronization from host to container). Takes one of
+        <literal>off</literal>, <literal>copy-host</literal>, <literal>copy-static</literal>,
+        <literal>copy-uplink</literal>, <literal>copy-stub</literal>, <literal>replace-host</literal>,
+        <literal>replace-static</literal>, <literal>replace-uplink</literal>,
+        <literal>replace-stub</literal>, <literal>bind-host</literal>, <literal>bind-static</literal>,
+        <literal>bind-uplink</literal>, <literal>bind-stub</literal>, <literal>delete</literal> or
+        <literal>auto</literal>.</para>
+
+        <para>If set to <literal>off</literal> the <filename>/etc/resolv.conf</filename> file in the
+        container is left as it is included in the image, and neither modified nor bind mounted over.</para>
+
+        <para>If set to <literal>copy-host</literal>, the <filename>/etc/resolv.conf</filename> file from the
+        host is copied into the container, unless the file exists already and is not a regular file (e.g. a
+        symlink). Similar, if <literal>replace-host</literal> is used the file is copied, replacing any
+        existing inode, including symlinks. Similar, if <literal>bind-host</literal> is used, the file is
+        bind mounted from the host into the container.</para>
+
+        <para>If set to <literal>copy-static</literal>, <literal>replace-static</literal> or
+        <literal>bind-static</literal> the static <filename>resolv.conf</filename> file supplied with
+        <citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+        (specifically: <filename>/usr/lib/systemd/resolv.conf</filename>) is copied or bind mounted into the
+        container.</para>
+
+        <para>If set to <literal>copy-uplink</literal>, <literal>replace-uplink</literal> or
+        <literal>bind-uplink</literal> the uplink <filename>resolv.conf</filename> file managed by
+        <filename>systemd-resolved.service</filename> (specifically:
+        <filename>/run/systemd/resolve/resolv.conf</filename>) is copied or bind mounted into the
+        container.</para>
+
+        <para>If set to <literal>copy-stub</literal>, <literal>replace-stub</literal> or
+        <literal>bind-stub</literal> the stub <filename>resolv.conf</filename> file managed by
+        <filename>systemd-resolved.service</filename> (specifically:
+        <filename>/run/systemd/resolve/stub-resolv.conf</filename>) is copied or bind mounted into the
+        container.</para>
+
+        <para>If set to <literal>delete</literal> the <filename>/etc/resolv.conf</filename> file in the
+        container is deleted if it exists.</para>
+
+        <para>Finally, if set to <literal>auto</literal> the file is left as it is if private networking is
+        turned on (see <option>--private-network</option>). Otherwise, if
+        <filename>systemd-resolved.service</filename> is running its stub <filename>resolv.conf</filename>
+        file is used, and if not the host's <filename>/etc/resolv.conf</filename> file. In the latter cases
+        the file is copied if the image is writable, and bind mounted otherwise.</para>
+
+        <para>It's recommended to use <literal>copy-…</literal> or <literal>replace-…</literal> if the
+        container shall be able to make changes to the DNS configuration on its own, deviating from the
+        host's settings. Otherwise <literal>bind</literal> is preferable, as it means direct changes to
+        <filename>/etc/resolv.conf</filename> in the container are not allowed, as it is a read-only bind
+        mount (but note that if the container has enough privileges, it might simply go ahead and unmount the
+        bind mount anyway). Note that both if the file is bind mounted and if it is copied no further
+        propagation of configuration is generally done after the one-time early initialization (this is
+        because the file is usually updated through copying and renaming). Defaults to
         <literal>auto</literal>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><option>--timezone=</option></term>
 
-        <listitem><para>Configures how <filename>/etc/localtime</filename> inside of the container (i.e. local timezone
-        synchronization from host to container) shall be handled. Takes one of <literal>off</literal>,
-        <literal>copy</literal>, <literal>bind</literal>, <literal>symlink</literal>, <literal>delete</literal> or
-        <literal>auto</literal>. If set to <literal>off</literal> the <filename>/etc/localtime</filename> file in the
-        container is left as it is included in the image, and neither modified nor bind mounted over. If set to
-        <literal>copy</literal> the <filename>/etc/localtime</filename> file of the host is copied into the
-        container. Similar, if <literal>bind</literal> is used, it is bind mounted from the host into the container. If
-        set to <literal>symlink</literal> a symlink from <filename>/etc/localtime</filename> in the container is
-        created pointing to the matching the timezone file of the container that matches the timezone setting on the
-        host. If set to <literal>delete</literal> the file in the container is deleted, should it exist. If set to
-        <literal>auto</literal> and the <filename>/etc/localtime</filename> file of the host is a symlink, then
-        <literal>symlink</literal> mode is used, and <literal>copy</literal> otherwise, except if the image is
-        read-only in which case <literal>bind</literal> is used instead. Defaults to
+        <listitem><para>Configures how <filename>/etc/localtime</filename> inside of the container
+        (i.e. local timezone synchronization from host to container) shall be handled. Takes one of
+        <literal>off</literal>, <literal>copy</literal>, <literal>bind</literal>, <literal>symlink</literal>,
+        <literal>delete</literal> or <literal>auto</literal>. If set to <literal>off</literal> the
+        <filename>/etc/localtime</filename> file in the container is left as it is included in the image, and
+        neither modified nor bind mounted over. If set to <literal>copy</literal> the
+        <filename>/etc/localtime</filename> file of the host is copied into the container. Similarly, if
+        <literal>bind</literal> is used, the file is bind mounted from the host into the container. If set to
+        <literal>symlink</literal>, a symlink is created pointing from <filename>/etc/localtime</filename> in
+        the container to the timezone file in the container that matches the timezone setting on the host. If
+        set to <literal>delete</literal>, the file in the container is deleted, should it exist. If set to
+        <literal>auto</literal> and the <filename>/etc/localtime</filename> file of the host is a symlink,
+        then <literal>symlink</literal> mode is used, and <literal>copy</literal> otherwise, except if the
+        image is read-only in which case <literal>bind</literal> is used instead. Defaults to
         <literal>auto</literal>.</para></listitem>
       </varlistentry>
 
 
       <para>This installs a minimal Fedora distribution into the
       directory <filename index="false">/var/lib/machines/f&fedora_latest_version;</filename>
-      and then boots an OS in a namespace container in it. Because the installation
+      and then boots that OS in a namespace container. Because the installation
       is located underneath the standard <filename>/var/lib/machines/</filename>
       directory, it is also possible to start the machine using
       <command>systemd-nspawn -M f&fedora_latest_version;</command>.</para>
 
       <para>This installs a minimal Debian unstable distribution into
       the directory <filename>~/debian-tree/</filename> and then
-      spawns a shell in a namespace container in it.</para>
+      spawns a shell from this image in a namespace container.</para>
 
       <para><command>debootstrap</command> supports
       <ulink url="https://www.debian.org">Debian</ulink>,
index 47916da5219d6e658eb55b5d135dcf36d78249e3..335a3b3d183e1b67d3e14cebb6140e61390ef8c5 100644 (file)
       <citerefentry><refentrytitle>pstore.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
       </para>
     </refsect2>
+
+    <refsect2>
+      <title>Controlling kernel parameters</title>
+
+      <para> The kernel has two parameters,
+      <filename>/sys/module/kernel/parameters/crash_kexec_post_notifiers</filename> and
+      <filename>/sys/module/printk/parameters/always_kmsg_dump</filename>,
+      that control writes into pstore.
+      The crash_kexec_post_notifiers parameter enables the kernel to write
+      dmesg (including stack trace) into pstore upon a panic or crash, and
+      printk.always_kmsg_dump parameter enables the kernel to write dmesg
+      upon a normal shutdown (shutdown, reboot, halt). These kernel
+      parameters are managed via the
+      <citerefentry><refentrytitle>tmpfiles.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+      mechanism, specifically the file <filename>/usr/lib/tmpfiles/systemd-pstore.conf</filename>.
+      </para>
+    </refsect2>
+
   </refsect1>
 
   <refsect1>
index 28783a15e9266099b41c59735fc9e741ae670de2..a9e322425f5dfe2fdafa597249def2446afb6a61 100644 (file)
@@ -44,7 +44,7 @@
     <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>, with
     its <command>bootctl random-seed</command> functionality.</para>
 
-    <para>When loading the random seed from disk its file is immediately updated with a new seed retrieved
+    <para>When loading the random seed from disk, the file is immediately updated with a new seed retrieved
     from the kernel, in order to ensure no two boots operate with the same random seed. This new seed is
     retrieved synchronously from the kernel, which means the service will not complete start-up until the
     random pool is fully initialized. On entropy-starved systems this may take a while. This functionality is
index f55be4f32803c7e33a197c876c3a59239d5b2461..ffa88baf67fe48f50754be3428fb6e62cc235e2c 100644 (file)
     available but not yet used. Specifically the following use cases are among those covered:</para>
 
     <itemizedlist>
-      <listitem><para>The root partition may be grown to cover the whole available disk space</para></listitem>
-      <listitem><para>A <filename>/home/</filename>, swap or <filename>/srv</filename> partition can be added in</para></listitem>
-      <listitem><para>A second (or third, …) root partition may be added in, to cover A/B style setups
+      <listitem><para>The root partition may be grown to cover the whole available disk space.</para></listitem>
+      <listitem><para>A <filename>/home/</filename>, swap or <filename>/srv/</filename> partition can be
+      added.</para></listitem>
+      <listitem><para>A second (or third, …) root partition may be added, to cover A/B style setups
       where a second version of the root file system is alternatingly used for implementing update
       schemes. The deployed image would carry only a single partition ("A") but on first boot a second
       partition ("B") for this purpose is automatically created.</para></listitem>
@@ -69,7 +70,7 @@
 
     <orderedlist>
       <listitem><para>The <filename>repart.d/*.conf</filename> configuration files are loaded and parsed,
-      and ordered by filename (without the directory suffix). </para></listitem>
+      and ordered by filename (without the directory prefix).</para></listitem>
 
       <listitem><para>The partition table already existing on the block device is loaded and
       parsed.</para></listitem>
     </orderedlist>
 
     <para>As exception to the normally strictly incremental operation, when called in a special "factory
-    reset" mode <command>systemd-repart</command> may also be used to erase select existing partitions to
+    reset" mode, <command>systemd-repart</command> may also be used to erase existing partitions to
     reset an installation back to vendor defaults. This mode of operation is used when either the
     <option>--factory-reset=yes</option> switch is passed on the tool's command line, or the
     <option>systemd.factory_reset=yes</option> option specified on the kernel command line, or the
     <varname>FactoryReset</varname> EFI variable (vendor UUID
     <constant>8cf2644b-4b0b-428f-9387-6d876050dc67</constant>) is set to "yes". It alters the algorithm above
-    slightly: between the 3rd and the 4th step above the any partition marked explicitly via the
+    slightly: between the 3rd and the 4th step above any partition marked explicitly via the
     <varname>FactoryReset=</varname> boolean is deleted, and the algorithm restarted, thus immediately
     re-creating these partitions anew empty.</para>
 
     also be set explicitly, formatted as UUID via the <option>--seed=</option> option. By hashing these UUIDs
     from a common seed images prepared with this tool become reproducible and the result of the algorithm
     above deterministic.</para>
+
+    <para>The positional argument should specify the block device to operate on. Instead of a block device
+    node path a regular file may be specified too, in which case the command operates on it like it would if
+    a loopback block device node was specified with the file attached. If <option>--empty=create</option> is
+    specified the specified path is created as regular file, which is useful for generating disk images from
+    scratch.</para>
   </refsect1>
 
   <refsect1>
       <varlistentry>
         <term><option>--empty=</option></term>
         <listitem><para>Takes one of <literal>refuse</literal>, <literal>allow</literal>,
-        <literal>require</literal> or <literal>force</literal>. Controls how to operate on block devices that
-        are entirely empty, i.e. carry no partition table/disk label yet. If this switch is not specified the
-        implied default is <literal>refuse</literal>.</para>
+        <literal>require</literal>, <literal>force</literal> or <literal>create</literal>. Controls how to
+        operate on block devices that are entirely empty, i.e. carry no partition table/disk label yet. If
+        this switch is not specified the implied default is <literal>refuse</literal>.</para>
 
         <para>If <literal>refuse</literal> <command>systemd-repart</command> requires that the block device
         it shall operate on already carries a partition table and refuses operation if none is found. If
         exists so far, and refuse operation if one already exists. If <literal>force</literal> it will create
         a fresh partition table unconditionally, erasing the disk fully in effect. If
         <literal>force</literal> no existing partitions will be taken into account or survive the
-        operation. Hence: use with care, this is a great way to lose all your data.</para></listitem>
+        operation. Hence: use with care, this is a great way to lose all your data. If
+        <literal>create</literal> a new loopback file is create under the path passed via the device node
+        parameter, of the size indicated with <option>--size=</option>, see below.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         the implied default. Controls whether to issue the <constant>BLKDISCARD</constant> I/O control
         command on the space taken up by any added partitions or on the space in between them. Usually, it's
         a good idea to issue this request since it tells the underlying hardware that the covered blocks
-        shall be considered empty, improving performance.</para></listitem>
+        shall be considered empty, improving performance. If operating on a regular file instead of a block
+        device node, a sparse file is generated.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--size=</option></term>
+
+        <listitem><para>Takes a size in bytes, using the usual K, M, G, T suffixes. If used the specified
+        device node path must refer to a regular file, which is then grown to the specified size if smaller,
+        before any change is made to the partition table. This is not supported if the specified node is a
+        block device. This switch has no effect if the file is already as large as the specified size or
+        larger. The specified size is implicitly rounded up to multiples of 4096. When used with
+        <option>--empty=create</option> this specifies the initial size of the loopback file to
+        create.</para></listitem>
       </varlistentry>
 
       <varlistentry>
       <varlistentry>
         <term><option>--definitions=</option></term>
 
-        <listitem><para>Takes a file system path. If specified the <filename>*.conf</filename> are directly
-        read from the specified directory instead of searching in
-        <filename>/usr/lib/repart.d/*.conf</filename>, <filename>/etc/repart.d/*.conf</filename>,
+        <listitem><para>Takes a file system path. If specified the <filename>*.conf</filename> files are read
+        from the specified directory instead of searching in <filename>/usr/lib/repart.d/*.conf</filename>,
+        <filename>/etc/repart.d/*.conf</filename>,
         <filename>/run/repart.d/*.conf</filename>.</para></listitem>
       </varlistentry>
 
index 53c46a1018e52594c338ea345739d671411cb3b7..914607e3f8d621827dec247c19eac3144fb3a757 100644 (file)
   <refsect1>
     <title>Description</title>
 
-    <para><command>systemd-resolved</command> is a system service that provides network name resolution to local
-    applications. It implements a caching and validating DNS/DNSSEC stub resolver, as well as an LLMNR and MulticastDNS
-    resolver and responder. Local applications may submit network name resolution requests via three interfaces:</para>
+    <para><command>systemd-resolved</command> is a system service that provides network name resolution to
+    local applications. It implements a caching and validating DNS/DNSSEC stub resolver, as well as an LLMNR
+    and MulticastDNS resolver and responder. Local applications may submit network name resolution requests
+    via three interfaces:</para>
 
     <itemizedlist>
-      <listitem><para>The native, fully-featured API <command>systemd-resolved</command> exposes on the bus. See the
-      <ulink url="https://www.freedesktop.org/wiki/Software/systemd/resolved">API Documentation</ulink> for
-      details. Usage of this API is generally recommended to clients as it is asynchronous and fully featured (for
-      example, properly returns DNSSEC validation status and interface scope for addresses as necessary for supporting
-      link-local networking).</para></listitem>
+      <listitem><para>The native, fully-featured API <command>systemd-resolved</command> exposes on the bus,
+      see
+      <citerefentry><refentrytitle>org.freedesktop.resolve1</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+      and
+      <citerefentry><refentrytitle>org.freedesktop.LogControl1</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+      for details. Usage of this API is generally recommended to clients as it is asynchronous and fully
+      featured (for example, properly returns DNSSEC validation status and interface scope for addresses as
+      necessary for supporting link-local networking).</para></listitem>
 
       <listitem><para>The glibc
-      <citerefentry project='man-pages'><refentrytitle>getaddrinfo</refentrytitle><manvolnum>3</manvolnum></citerefentry> API as defined
-      by <ulink url="https://tools.ietf.org/html/rfc3493">RFC3493</ulink> and its related resolver functions,
-      including <citerefentry project='man-pages'><refentrytitle>gethostbyname</refentrytitle><manvolnum>3</manvolnum></citerefentry>. This
-      API is widely supported, including beyond the Linux platform. In its current form it does not expose DNSSEC
-      validation status information however, and is synchronous only. This API is backed by the glibc Name Service
-      Switch (<citerefentry project='man-pages'><refentrytitle>nss</refentrytitle><manvolnum>5</manvolnum></citerefentry>). Usage of the
-      glibc NSS module <citerefentry><refentrytitle>nss-resolve</refentrytitle><manvolnum>8</manvolnum></citerefentry>
-      is required in order to allow glibc's NSS resolver functions to resolve host names via
+      <citerefentry project='man-pages'><refentrytitle>getaddrinfo</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+      API as defined by <ulink url="https://tools.ietf.org/html/rfc3493">RFC3493</ulink> and its related
+      resolver functions, including
+      <citerefentry project='man-pages'><refentrytitle>gethostbyname</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+      This API is widely supported, including beyond the Linux platform. In its current form it does not
+      expose DNSSEC validation status information however, and is synchronous only. This API is backed by the
+      glibc Name Service Switch
+      (<citerefentry project='man-pages'><refentrytitle>nss</refentrytitle><manvolnum>5</manvolnum></citerefentry>).
+      Usage of the glibc NSS module
+      <citerefentry><refentrytitle>nss-resolve</refentrytitle><manvolnum>8</manvolnum></citerefentry> is
+      required in order to allow glibc's NSS resolver functions to resolve hostnames via
       <command>systemd-resolved</command>.</para></listitem>
 
-      <listitem><para>Additionally, <command>systemd-resolved</command> provides a local DNS stub listener on IP
-      address 127.0.0.53 on the local loopback interface. Programs issuing DNS requests directly, bypassing any local
-      API may be directed to this stub, in order to connect them to <command>systemd-resolved</command>. Note however
-      that it is strongly recommended that local programs use the glibc NSS or bus APIs instead (as described above),
-      as various network resolution concepts (such as link-local addressing, or LLMNR Unicode domains) cannot be mapped
-      to the unicast DNS protocol.</para></listitem>
+      <listitem><para>Additionally, <command>systemd-resolved</command> provides a local DNS stub listener on
+      IP address 127.0.0.53 on the local loopback interface. Programs issuing DNS requests directly,
+      bypassing any local API may be directed to this stub, in order to connect them to
+      <command>systemd-resolved</command>. Note however that it is strongly recommended that local programs
+      use the glibc NSS or bus APIs instead (as described above), as various network resolution concepts
+      (such as link-local addressing, or LLMNR Unicode domains) cannot be mapped to the unicast DNS
+      protocol.</para></listitem>
     </itemizedlist>
 
     <para>The DNS servers contacted are determined from the global settings in
     <filename>/etc/systemd/resolved.conf</filename>, the per-link static settings in
     <filename>/etc/systemd/network/*.network</filename> files (in case
-    <citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry> is
-    used), the per-link dynamic settings received over DHCP, user request made via
-    <citerefentry><refentrytitle>resolvectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>, and any DNS server
-    information made available by other system services. See
+    <citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    is used), the per-link dynamic settings received over DHCP, information provided via
+    <citerefentry><refentrytitle>resolvectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>, and any
+    DNS server information made available by other system services. See
     <citerefentry><refentrytitle>resolved.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry> and
-    <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry> for details
-    about systemd's own configuration files for DNS servers. To improve compatibility,
-    <filename>/etc/resolv.conf</filename> is read in order to discover configured system DNS servers, but only if it is
-    not a symlink to <filename>/run/systemd/resolve/stub-resolv.conf</filename>,
-    <filename>/usr/lib/systemd/resolv.conf</filename> or <filename>/run/systemd/resolve/resolv.conf</filename> (see
-    below).</para>
+    <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
+    details about systemd's own configuration files for DNS servers. To improve compatibility,
+    <filename>/etc/resolv.conf</filename> is read in order to discover configured system DNS servers, but
+    only if it is not a symlink to <filename>/run/systemd/resolve/stub-resolv.conf</filename>,
+    <filename>/usr/lib/systemd/resolv.conf</filename> or
+    <filename>/run/systemd/resolve/resolv.conf</filename> (see below).</para>
 
   </refsect1>
 
   <refsect1>
     <title>Synthetic Records</title>
 
-    <para><command>systemd-resolved</command> synthesizes DNS resource records (RRs) for the following cases:</para>
+    <para><command>systemd-resolved</command> synthesizes DNS resource records (RRs) for the following
+    cases:</para>
 
     <itemizedlist>
-      <listitem><para>The local, configured hostname is resolved to
-      all locally configured IP addresses ordered by their scope, or
-      — if none are configured — the IPv4 address 127.0.0.2 (which
-      is on the local loopback) and the IPv6 address ::1 (which is the
-      local host).</para></listitem>
-
-      <listitem><para>The hostnames <literal>localhost</literal> and
-      <literal>localhost.localdomain</literal> (as well as any hostname
-      ending in <literal>.localhost</literal> or <literal>.localhost.localdomain</literal>)
-      are resolved to the IP addresses 127.0.0.1 and ::1.</para></listitem>
-
-      <listitem><para>The hostname <literal>_gateway</literal> is
-      resolved to all current default routing gateway addresses,
-      ordered by their metric. This assigns a stable hostname to the
-      current gateway, useful for referencing it independently of the
-      current network configuration state.</para></listitem>
-
-      <listitem><para>The mappings defined in <filename>/etc/hosts</filename> are resolved
-      to their configured addresses and back, but they will not affect lookups for
-      non-address types (like MX).</para></listitem>
+      <listitem><para>The local, configured hostname is resolved to all locally configured IP addresses
+      ordered by their scope, or — if none are configured — the IPv4 address 127.0.0.2 (which is on the local
+      loopback) and the IPv6 address ::1 (which is the local host).</para></listitem>
+
+      <listitem><para>The hostnames <literal>localhost</literal> and <literal>localhost.localdomain</literal>
+      (as well as any hostname ending in <literal>.localhost</literal> or
+      <literal>.localhost.localdomain</literal>) are resolved to the IP addresses 127.0.0.1 and ::1.
+      </para></listitem>
+
+      <listitem><para>The hostname <literal>_gateway</literal> is resolved to all current default routing
+      gateway addresses, ordered by their metric. This assigns a stable hostname to the current gateway,
+      useful for referencing it independently of the current network configuration state.</para></listitem>
+
+      <listitem><para>The mappings defined in <filename>/etc/hosts</filename> are resolved to their
+      configured addresses and back, but they will not affect lookups for non-address types (like MX).
+      Support for <filename>/etc/hosts</filename> may be disabled with <varname>ReadEtcHosts=no</varname>,
+      see <citerefentry><refentrytitle>resolved.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+      </para></listitem>
     </itemizedlist>
   </refsect1>
 
   <refsect1>
     <title>Protocols and Routing</title>
 
-    <para>Lookup requests are routed to the available DNS servers, LLMNR and MulticastDNS interfaces according to the
-    following rules:</para>
+    <para>Lookup requests are routed to the available DNS servers, LLMNR, and MulticastDNS interfaces
+    according to the following rules:</para>
 
     <itemizedlist>
-      <listitem><para>Lookups for the special hostname <literal>localhost</literal> are never routed to the network. (A
-      few other, special domains are handled the same way.)</para></listitem>
-
-      <listitem><para>Single-label names are routed to all local interfaces capable of IP multicasting, using the LLMNR
-      protocol. Lookups for IPv4 addresses are only sent via LLMNR on IPv4, and lookups for IPv6 addresses are only
-      sent via LLMNR on IPv6. Lookups for the locally configured host name and the <literal>_gateway</literal> host
-      name are never routed to LLMNR.</para></listitem>
-
-      <listitem><para>Multi-label names with the domain suffix <literal>.local</literal> are routed to all local
-      interfaces capable of IP multicasting, using the MulticastDNS protocol. As with LLMNR IPv4 address lookups are
-      sent via IPv4 and IPv6 address lookups are sent via IPv6.</para></listitem>
-
-      <listitem><para>Other multi-label names are routed to all local interfaces that have a DNS server configured,
-      plus the globally configured DNS server if there is one. Address lookups from the link-local address range are
-      never routed to DNS. Note that by default lookups for domains with the <literal>.local</literal> suffix are not
-      routed to DNS servers, unless the domain is specified explicitly as routing or search domain for the DNS server
-      and interface. This means that on networks where the <literal>.local</literal> domain is defined in a
-      site-specific DNS server, explicit search or routing domains need to be configured to make lookups within this
-      DNS domain work. Note that today it's generally recommended to avoid defining <literal>.local</literal> in a DNS
-      server, as <ulink url="https://tools.ietf.org/html/rfc6762">RFC6762</ulink> reserves this domain for exclusive
+      <listitem><para>Names for which synthetic records are generated (as listed in the previous section) are
+      never routed to the network and a reply is sent immediately. In particular this means that lookups for
+      <literal>localhost</literal> are never routed to the network.</para></listitem>
+
+      <listitem><para>Single-label names are routed to all local interfaces capable of IP multicasting, where
+      LLMNR is not disabled, using the LLMNR protocol. Lookups for IPv4 addresses are only sent via LLMNR on
+      IPv4, and lookups for IPv6 addresses are only sent via LLMNR on IPv6. Lookups for the locally
+      configured hostname and the <literal>_gateway</literal> hostname are never routed to LLMNR.
+      </para></listitem>
+
+      <listitem><para>Multi-label names with the domain suffix <literal>.local</literal> are routed to all
+      local interfaces capable of IP multicasting, where MulticastDNS is not disabled, using the MulticastDNS
+      protocol. As with LLMNR, IPv4 address lookups are sent via IPv4 and IPv6 address lookups are sent via
+      IPv6.</para></listitem>
+
+      <listitem><para>Resolution of address records (A and AAAA) via unicast DNS (i.e. not LLMNR or
+      MulticastDNS) for non-synthesized single-label names is allowed for non-top-level domains. This means
+      that such records can be resolved when search domains are defined. For any interface which defines
+      search domains, such look-ups are routed to that interface, suffixed with each of the search domains
+      defined on that interface in turn. When global search domains are defined, such look-ups are routed to
+      all interfaces, suffixed by each of the global search domains in turn. Additionally, lookup of
+      single-label names via unicast DNS may be enabled with the
+      <varname>ResolveUnicastSingleLabel=yes</varname> setting. The details of which servers are queried and
+      how the final reply is chosen are described below. Note that this means that address queries for
+      single-label names are never sent out to remote DNS servers by default, and if no search domains are
+      defined, resolution will fail.</para></listitem>
+
+      <listitem><para>Other multi-label names are routed to all local interfaces that have a DNS server
+      configured, plus the globally configured DNS servers if there are any. Note that by default, lookups for
+      domains with the <literal>.local</literal> suffix are not routed to DNS servers, unless the domain is
+      specified explicitly as routing or search domain for the DNS server and interface. This means that on
+      networks where the <literal>.local</literal> domain is defined in a site-specific DNS server, explicit
+      search or routing domains need to be configured to make lookups within this DNS domain work. Note that
+      these days, it's generally recommended to avoid defining <literal>.local</literal> in a DNS server, as
+      <ulink url="https://tools.ietf.org/html/rfc6762">RFC6762</ulink> reserves this domain for exclusive
       MulticastDNS use.</para></listitem>
+
+      <listitem><para>Address lookups are routed similarly to multi-label names, with the exception that
+      addresses from the link-local address range are never routed to unicast DNS and are only resolved using
+      LLMNR and MulticastDNS (when enabled).</para></listitem>
     </itemizedlist>
 
-    <para>If lookups are routed to multiple interfaces, the first
-    successful response is returned (thus effectively merging the
-    lookup zones on all matching interfaces). If the lookup failed on
-    all interfaces, the last failing response is returned.</para>
+    <para>If lookups are routed to multiple interfaces, the first successful response is returned (thus
+    effectively merging the lookup zones on all matching interfaces). If the lookup failed on all interfaces,
+    the last failing response is returned.</para>
 
-    <para>Routing of lookups may be influenced by configuring per-interface domain names and other settings. See
+    <para>Routing of lookups may be influenced by configuring per-interface domain names and other
+    settings. See
     <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry> and
-    <citerefentry><refentrytitle>resolvectl</refentrytitle><manvolnum>1</manvolnum></citerefentry> for details. The
-    following query routing logic applies for unicast DNS traffic:</para>
+    <citerefentry><refentrytitle>resolvectl</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+    details. The following query routing logic applies for unicast DNS traffic:</para>
 
     <itemizedlist>
-      <listitem><para>If a name to look up matches (that is: is equal to or has as suffix) any of the configured search
-      or route-only domains of any link (or the globally configured DNS settings), the "best matching"
-      search/route-only domain is determined: the matching one with the most labels. The query is then sent to all DNS
-      servers of any links or the globally configured DNS servers associated with this "best matching"
-      search/route-only domain. (Note that more than one link might have this same "best matching" search/route-only
-      domain configured, in which case the query is sent to all of them in parallel).</para></listitem>
-
-      <listitem><para>If a query does not match any configured search/route-only domain (neither per-link nor global),
-      it is sent to all DNS servers that are configured on links with the "DNS default route" option set, as well as
-      the globally configured DNS server.</para></listitem>
-
-      <listitem><para>If there is no link configured as "DNS default route" and no global DNS server configured, the
-      compiled-in fallback DNS server is used.</para></listitem>
-
-      <listitem><para>Otherwise the query is failed as no suitable DNS servers could be determined.</para></listitem>
+      <listitem><para>If a name to look up matches (that is: is equal to or has as suffix) any of the
+      configured search or route-only domains of any link (see
+      <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>),
+      or the globally configured DNS settings (see the discussion of <varname>Domains=</varname> in
+      <citerefentry><refentrytitle>resolved.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>),
+      "best matching" search/route-only domain is determined: the matching one with the most labels. The
+      query is then sent to all DNS servers of any links or the globally configured DNS servers associated
+      with this "best matching" search/route-only domain. (Note that more than one link might have this same
+      "best matching" search/route-only domain configured, in which case the query is sent to all of them in
+      parallel).</para>
+
+      <para>In case of single-label names, when search domains are defined, the same logic applies, except
+      that the name is first suffixed by the search domain.</para></listitem>
+
+      <listitem><para>If a query does not match any configured search/route-only domain (neither per-link nor
+      global), it is sent to all DNS servers that are configured on links with the "DNS default route" option
+      set, as well as the globally configured DNS server.</para></listitem>
+
+      <listitem><para>If there is no link configured as "DNS default route" and no global DNS server
+      configured, the compiled-in fallback DNS server is used.</para></listitem>
+
+      <listitem><para>Otherwise the query is failed as no suitable DNS servers could be determined.
+      </para></listitem>
     </itemizedlist>
 
-    <para>The "DNS default route" option is a boolean setting configurable with <command>resolvectl</command> or in
-    <filename>.network</filename> files. If not set, it is implicitly determined based on the configured DNS domains
-    for a link: if there's any route-only domain (not matching <literal>~.</literal>) it defaults to false, otherwise
-    to true.</para>
-
-    <para>Effectively this means: in order to preferably route all DNS queries not explicitly matched by
-    search/route-only domain configuration to a specific link, configure a <literal>~.</literal> route-only domain on
-    it. This will ensure that other links will not be considered for the queries (unless they too carry such a
-    route-only domain). In order to route all such DNS queries to a specific link only in case no other link is
-    preferable, then set the "DNS default route" option for the link to true, and do not configure a
-    <literal>~.</literal> route-only domain on it. Finally, in order to ensure that a specific link never receives any
-    DNS traffic not matching any of its configured search/route-only domains, set the "DNS default route" option for it
-    to false.</para>
-
-    <para>See the <ulink url="https://www.freedesktop.org/wiki/Software/systemd/resolved"> resolved D-Bus API
-    Documentation</ulink> for information about the APIs <filename>systemd-resolved</filename> provides.</para>
+    <para>The "DNS default route" option is a boolean setting configurable with <command>resolvectl</command>
+    or in <filename>.network</filename> files. If not set, it is implicitly determined based on the
+    configured DNS domains for a link: if there's any route-only domain (not matching <literal>~.</literal>)
+    it defaults to false, otherwise to true.</para>
+
+    <para>Effectively this means: in order to support single-label non-synthetized names, define appropriate
+    search domains. In order to preferably route all DNS queries not explicitly matched by search/route-only
+    domain configuration to a specific link, configure a <literal>~.</literal> route-only domain on it. This
+    will ensure that other links will not be considered for these queries (unless they too carry such a
+    route-only domain). In order to route all such DNS queries to a specific link only if no other link
+    is preferable, set the "DNS default route" option for the link to true and do not configure a
+    <literal>~.</literal> route-only domain on it. Finally, in order to ensure that a specific link never
+    receives any DNS traffic not matching any of its configured search/route-only domains, set the "DNS
+    default route" option for it to false.</para>
+
+    <para>See the <ulink url="https://www.freedesktop.org/wiki/Software/systemd/resolved">resolved D-Bus API
+    Documentation</ulink> for information about the APIs <filename>systemd-resolved</filename> provides.
+    </para>
   </refsect1>
 
   <refsect1>
 
     <itemizedlist>
       <listitem><para><command>systemd-resolved</command> maintains the
-      <filename>/run/systemd/resolve/stub-resolv.conf</filename> file for compatibility with traditional Linux
-      programs. This file may be symlinked from <filename>/etc/resolv.conf</filename>. This file lists the 127.0.0.53
-      DNS stub (see above) as the only DNS server. It also contains a list of search domains that are in use by
-      systemd-resolved. The list of search domains is always kept up-to-date. Note that
-      <filename>/run/systemd/resolve/stub-resolv.conf</filename> should not be used directly by applications, but only
-      through a symlink from <filename>/etc/resolv.conf</filename>. This file may be symlinked from
-      <filename>/etc/resolv.conf</filename> in order to connect all local clients that bypass local DNS APIs to
-      <command>systemd-resolved</command> with correct search domains settings. This mode of operation is
+      <filename>/run/systemd/resolve/stub-resolv.conf</filename> file for compatibility with traditional
+      Linux programs. This file may be symlinked from <filename>/etc/resolv.conf</filename>. This file lists
+      the 127.0.0.53 DNS stub (see above) as the only DNS server. It also contains a list of search domains
+      that are in use by systemd-resolved. The list of search domains is always kept up-to-date. Note that
+      <filename>/run/systemd/resolve/stub-resolv.conf</filename> should not be used directly by applications,
+      but only through a symlink from <filename>/etc/resolv.conf</filename>. This file may be symlinked from
+      <filename>/etc/resolv.conf</filename> in order to connect all local clients that bypass local DNS APIs
+      to <command>systemd-resolved</command> with correct search domains settings. This mode of operation is
       recommended.</para></listitem>
 
       <listitem><para>A static file <filename>/usr/lib/systemd/resolv.conf</filename> is provided that lists
       the 127.0.0.53 DNS stub (see above) as only DNS server. This file may be symlinked from
-      <filename>/etc/resolv.conf</filename> in order to connect all local clients that bypass local DNS APIs to
-      <command>systemd-resolved</command>. This file does not contain any search domains.</para></listitem>
+      <filename>/etc/resolv.conf</filename> in order to connect all local clients that bypass local DNS APIs
+      to <command>systemd-resolved</command>. This file does not contain any search domains.
+      </para></listitem>
 
       <listitem><para><command>systemd-resolved</command> maintains the
       <filename>/run/systemd/resolve/resolv.conf</filename> file for compatibility with traditional Linux
-      programs. This file may be symlinked from <filename>/etc/resolv.conf</filename> and is always kept up-to-date,
-      containing information about all known DNS servers. Note the file format's limitations: it does not know a
-      concept of per-interface DNS servers and hence only contains system-wide DNS server definitions. Note that
-      <filename>/run/systemd/resolve/resolv.conf</filename> should not be used directly by applications, but only
-      through a symlink from <filename>/etc/resolv.conf</filename>. If this mode of operation is used local clients
-      that bypass any local DNS API will also bypass <command>systemd-resolved</command> and will talk directly to the
-      known DNS servers.</para> </listitem>
-
-      <listitem><para>Alternatively, <filename>/etc/resolv.conf</filename> may be managed by other packages, in which
-      case <command>systemd-resolved</command> will read it for DNS configuration data. In this mode of operation
-      <command>systemd-resolved</command> is consumer rather than provider of this configuration
+      programs. This file may be symlinked from <filename>/etc/resolv.conf</filename> and is always kept
+      up-to-date, containing information about all known DNS servers. Note the file format's limitations: it
+      does not know a concept of per-interface DNS servers and hence only contains system-wide DNS server
+      definitions. Note that <filename>/run/systemd/resolve/resolv.conf</filename> should not be used
+      directly by applications, but only through a symlink from <filename>/etc/resolv.conf</filename>. If
+      this mode of operation is used local clients that bypass any local DNS API will also bypass
+      <command>systemd-resolved</command> and will talk directly to the known DNS servers.</para></listitem>
+
+      <listitem><para>Alternatively, <filename>/etc/resolv.conf</filename> may be managed by other packages,
+      in which case <command>systemd-resolved</command> will read it for DNS configuration data. In this mode
+      of operation <command>systemd-resolved</command> is consumer rather than provider of this configuration
       file. </para></listitem>
     </itemizedlist>
 
-    <para>Note that the selected mode of operation for this file is detected fully automatically, depending on whether
-    <filename>/etc/resolv.conf</filename> is a symlink to <filename>/run/systemd/resolve/resolv.conf</filename> or
-    lists 127.0.0.53 as DNS server.</para>
+    <para>Note that the selected mode of operation for this file is detected fully automatically, depending
+    on whether <filename>/etc/resolv.conf</filename> is a symlink to
+    <filename>/run/systemd/resolve/resolv.conf</filename> or lists 127.0.0.53 as DNS server.</para>
   </refsect1>
 
   <refsect1>
         <term><constant>SIGUSR1</constant></term>
 
         <listitem><para>Upon reception of the <constant>SIGUSR1</constant> process signal
-        <command>systemd-resolved</command> will dump the contents of all DNS resource record caches it maintains, as
-        well as all feature level information it learnt about configured DNS servers into the system
-        logs.</para></listitem>
+        <command>systemd-resolved</command> will dump the contents of all DNS resource record caches it
+        maintains, as well as all feature level information it learnt about configured DNS servers into the
+        system logs.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><constant>SIGUSR2</constant></term>
 
         <listitem><para>Upon reception of the <constant>SIGUSR2</constant> process signal
-        <command>systemd-resolved</command> will flush all caches it maintains. Note that it should normally not be
-        necessary to request this explicitly – except for debugging purposes – as <command>systemd-resolved</command>
-        flushes the caches automatically anyway any time the host's network configuration changes. Sending this signal
-        to <command>systemd-resolved</command> is equivalent to the <command>resolvectl flush-caches</command>
-        command, however the latter is recommended since it operates in a synchronous way.</para></listitem>
+        <command>systemd-resolved</command> will flush all caches it maintains. Note that it should normally
+        not be necessary to request this explicitly – except for debugging purposes – as
+        <command>systemd-resolved</command> flushes the caches automatically anyway any time the host's
+        network configuration changes. Sending this signal to <command>systemd-resolved</command> is
+        equivalent to the <command>resolvectl flush-caches</command> command, however the latter is
+        recommended since it operates in a synchronous way.</para></listitem>
       </varlistentry>
 
       <varlistentry>
 
         <listitem><para>Upon reception of the <constant>SIGRTMIN+1</constant> process signal
         <command>systemd-resolved</command> will forget everything it learnt about the configured DNS
-        servers. Specifically any information about server feature support is flushed out, and the server feature
-        probing logic is restarted on the next request, starting with the most fully featured level. Note that it
-        should normally not be necessary to request this explicitly – except for debugging purposes – as
-        <command>systemd-resolved</command> automatically forgets learnt information any time the DNS server
-        configuration changes. Sending this signal to <command>systemd-resolved</command> is equivalent to the
-        <command>resolvectl reset-server-features</command> command, however the latter is recommended since it
-        operates in a synchronous way.</para></listitem>
+        servers. Specifically any information about server feature support is flushed out, and the server
+        feature probing logic is restarted on the next request, starting with the most fully featured
+        level. Note that it should normally not be necessary to request this explicitly – except for
+        debugging purposes – as <command>systemd-resolved</command> automatically forgets learnt information
+        any time the DNS server configuration changes. Sending this signal to
+        <command>systemd-resolved</command> is equivalent to the <command>resolvectl
+        reset-server-features</command> command, however the latter is recommended since it operates in a
+        synchronous way.</para></listitem>
       </varlistentry>
     </variablelist>
 
index 6fc2c8d0eb4766c57c883c6417f014ad61996a5a..a88f60fbb6017da5055e7f52b55448e2c5a51b96 100644 (file)
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--slice-inherit</option></term>
+
+        <listitem><para>Make the new <filename>.service</filename> or <filename>.scope</filename> unit part
+        of the inherited slice. This option can be combined with <option>--slice=</option>.</para>
+
+        <para>An inherited slice is located within <command>systemd-run</command> slice. Example: if
+        <command>systemd-run</command> slice is <filename>foo.slice</filename>, and the
+        <option>--slice=</option> argument is <filename>bar</filename>, the unit will be placed under the
+        <filename>foo-bar.slice</filename>.</para>
+
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>-r</option></term>
         <term><option>--remain-after-exit</option></term>
index a6949b0c3b1ef5283a17666cee2bb82b8b0b5b86..a3714f52ae62345666d3df315c65ceebd0ae1ca2 100644 (file)
@@ -95,7 +95,7 @@
     <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>
     attempts to suspend or hibernate the machine.
     See
-    <citerefentry><refentrytitle>systemd.syntax</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    <citerefentry><refentrytitle>systemd.syntax</refentrytitle><manvolnum>7</manvolnum></citerefentry>
     for a general description of the syntax.</para>
   </refsect1>
 
     <title>Options</title>
 
     <para>The following options can be configured in the
-    <literal>[Sleep]</literal> section of
+    [Sleep] section of
     <filename>/etc/systemd/sleep.conf</filename> or a
     <filename>sleep.conf.d</filename> file:</para>
 
index f3dbb47a61728dfc82d47de8e1d492a932451dac..20a557219257d5db8ff9165a2a902fe5c3ecfdb0 100644 (file)
       <varlistentry>
         <term><varname>$SYSTEMD_LOG_TARGET</varname></term>
         <term><varname>$SYSTEMD_LOG_LEVEL</varname></term>
+        <term><varname>$SYSTEMD_LOG_TIME</varname></term>
         <term><varname>$SYSTEMD_LOG_COLOR</varname></term>
         <term><varname>$SYSTEMD_LOG_LOCATION</varname></term>
 
index a72ac1bbc66fcf2276d9a215897834c48894aec4..a4e18989fc106900eeada51dd70a153490c1166e 100644 (file)
@@ -16,7 +16,7 @@
   </refmeta>
   <refnamediv>
     <refname>systemd-socket-proxyd</refname>
-    <refpurpose>Bidirectionally proxy local sockets to another (possibly remote) socket.</refpurpose>
+    <refpurpose>Bidirectionally proxy local sockets to another (possibly remote) socket</refpurpose>
   </refnamediv>
   <refsynopsisdiv>
     <cmdsynopsis>
         <listitem><para>Sets the maximum number of simultaneous connections, defaults to 256.
         If the limit of concurrent connections is reached further connections will be refused.</para></listitem>
       </varlistentry>
+      <varlistentry>
+        <term><option>--exit-idle-time=</option></term>
+
+        <listitem><para>Sets the time before exiting when there are no connections, defaults to
+        <constant>infinity</constant>. Takes a unit-less value in seconds, or a time span value such
+        as <literal>5min 20s</literal>.</para></listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
   <refsect1>
@@ -115,6 +122,9 @@ server {
         <programlisting><![CDATA[# systemctl enable --now proxy-to-nginx.socket
 $ curl http://localhost:80/]]></programlisting>
       </example>
+      <para>If <filename>nginx.service</filename> has <varname>StopWhenUnneeded=</varname> set, then
+      passing <option>--exit-idle-time=</option> to <command>systemd-socket-proxyd</command> allows
+      both services to stop during idle periods.</para>
     </refsect2>
     <refsect2>
       <title>Namespace Example</title>
index 2310e6f52634b886fe619b49b04058f974381f80..e57000e09ad91443e04fe66892ed3e9a19f93b4e 100644 (file)
     url="https://www.freedesktop.org/wiki/Software/systemd/inhibit">Inhibitor
     interface</ulink>.</para>
 
-    <para>Note that
-    <filename>systemd-suspend.service</filename>,
-    <filename>systemd-hibernate.service</filename>, and
-    <filename>systemd-hybrid-sleep.service</filename>
-    <filename>systemd-suspend-then-hibernate.service</filename>
-    should never be executed directly. Instead, trigger system sleep
-    states with a command such as <literal>systemctl suspend</literal>
-    or similar.</para>
+    <para>Note that <filename>systemd-suspend.service</filename>,
+    <filename>systemd-hibernate.service</filename>, <filename>systemd-hybrid-sleep.service</filename>, and
+    <filename>systemd-suspend-then-hibernate.service</filename> should never be executed directly. Instead,
+    trigger system sleep with a command such as <command>systemctl suspend</command> or <command>systemctl
+    hibernate</command>.</para>
 
     <para>Internally, this service will echo a string like
     <literal>mem</literal> into <filename>/sys/power/state</filename>,
     to trigger the actual system suspend. What exactly is written
-    where can be configured in the <literal>[Sleep]</literal> section
+    where can be configured in the [Sleep] section
     of <filename>/etc/systemd/sleep.conf</filename> or a
     <filename>sleep.conf.d</filename> file. See
     <citerefentry><refentrytitle>systemd-sleep.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
index e22b335d30d726affd00a25f6c18985c34358fdf..c64e57c2777ef4d36b7192748110ecf19ac34ed3 100644 (file)
@@ -48,7 +48,7 @@
     <filename>user.conf.d</filename> directories. These configuration
     files contain a few settings controlling basic manager
     operations. See
-    <citerefentry><refentrytitle>systemd.syntax</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    <citerefentry><refentrytitle>systemd.syntax</refentrytitle><manvolnum>7</manvolnum></citerefentry>
     for a general description of the syntax.</para>
   </refsect1>
 
     <title>Options</title>
 
     <para>All options are configured in the
-    <literal>[Manager]</literal> section:</para>
+    [Manager] section:</para>
 
     <variablelist class='config-directives'>
 
       <varlistentry>
-        <term><varname>LogLevel=</varname></term>
-        <term><varname>LogTarget=</varname></term>
         <term><varname>LogColor=</varname></term>
+        <term><varname>LogLevel=</varname></term>
         <term><varname>LogLocation=</varname></term>
+        <term><varname>LogTarget=</varname></term>
+        <term><varname>LogTime=</varname></term>
         <term><varname>DumpCore=yes</varname></term>
         <term><varname>CrashChangeVT=no</varname></term>
         <term><varname>CrashShell=no</varname></term>
         for details. During the first phase of the shutdown operation the system and service manager remains running
         and hence <varname>RuntimeWatchdogSec=</varname> is still honoured. In order to define a timeout on this first
         phase of system shutdown, configure <varname>JobTimeoutSec=</varname> and <varname>JobTimeoutAction=</varname>
-        in the <literal>[Unit]</literal> section of the <filename>shutdown.target</filename> unit. By default
+        in the [Unit] section of the <filename>shutdown.target</filename> unit. By default
         <varname>RuntimeWatchdogSec=</varname> defaults to 0 (off), and <varname>RebootWatchdogSec=</varname> to
         10min. <varname>KExecWatchdogSec=</varname> may be used to additionally enable the watchdog when kexec
         is being executed rather than when rebooting. Note that if the kernel does not reset the watchdog on kexec (depending
         units. See
         <citerefentry><refentrytitle>setrlimit</refentrytitle><manvolnum>2</manvolnum></citerefentry> for
         details. These settings may be overridden in individual units using the corresponding
-        <varname>LimitXXX=</varname> directives, see
-        <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>, for
-        details, and they accept the same parameter syntax. Note that these resource limits are only defaults
+        <varname>LimitXXX=</varname> directives and they accept the same parameter syntax,
+        see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+        for details. Note that these resource limits are only defaults
         for units, they are not applied to the service manager process (i.e. PID 1) itself.</para></listitem>
       </varlistentry>
 
index 795d9eebe2c7aad5eb6e4682c6d9f98aa350f69b..7396d4ab606e4950fb203065cb0b783a4dff7738 100644 (file)
@@ -43,7 +43,7 @@
     <literal>$named</literal>, <literal>$portmap</literal>,
     <literal>$time</literal> are supported and will be turned into
     dependencies on specific native systemd targets.  See
-    <citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    <citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>7</manvolnum></citerefentry>
     for more details.</para>
 
     <para>SysV runlevels have corresponding systemd targets
index c8a92f94a83b6a3dc65ec7892cb16ec88b6287f2..685fe74339e999067ac3ed65f34d20ff1efc8bcc 100644 (file)
@@ -18,7 +18,7 @@
   <refnamediv>
     <refname>systemd-time-wait-sync.service</refname>
     <refname>systemd-time-wait-sync</refname>
-    <refpurpose>Wait Until Kernel Time Synchronized</refpurpose>
+    <refpurpose>Wait until kernel time is synchronized</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
index f981848cb204a3bfd09a065f2fc902a72bbc6c68..93cfdf511803a06c109101e6450aa8b0905f1fc3 100644 (file)
@@ -29,7 +29,7 @@
   <refsect1>
     <title>Description</title>
 
-    <para><filename>systemd-timedated</filename> is a system service
+    <para><filename>systemd-timedated.service</filename> is a system service
     that may be used as a mechanism to change the system clock and
     timezone, as well as to enable/disable network time synchronization.
     <filename>systemd-timedated</filename> is automatically activated
     <citerefentry><refentrytitle>timedatectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>
     is a command line client to this service.</para>
 
-    <para>See the <ulink
-    url="https://www.freedesktop.org/wiki/Software/systemd/timedated">
-    developer documentation</ulink> for information about the APIs
-    <filename>systemd-timedated</filename> provides.</para>
+    <para><filename>systemd-timedated</filename> currently offers access to
+    the following four settings:
+    <itemizedlist>
+      <listitem><para>The system time</para></listitem>
+
+      <listitem><para>The system timezone</para></listitem>
+
+      <listitem><para>A boolean controlling whether the system RTC is in local or UTC
+      timezone</para></listitem>
+
+      <listitem><para>Whether the time synchronization service is enabled/started or
+      disabled/stopped, see next section.</para></listitem>
+    </itemizedlist></para>
+
+    <para>See
+    <citerefentry><refentrytitle>org.freedesktop.timedate1</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    and
+    <citerefentry><refentrytitle>org.freedesktop.LogControl1</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    for information about the D-Bus API.</para>
   </refsect1>
 
   <refsect1>
index 7720ef53fa19fb2ba73dbb83299d365faeb6b56b..998fd0911baa5ba35df16e3a64d81ca51d81b53b 100644 (file)
         <listitem><para>Takes a directory path as an argument. All paths will be prefixed with the given alternate
         <replaceable>root</replaceable> path, including config search paths.</para>
 
-        <para>Note that this option does not alter how the users and groups specified in the configuration files are
-        resolved. With or without this option, users and groups are always resolved according to the host's user and
-        group databases, any such databases stored under the specified root directories are not
-        consulted.</para></listitem>
+        <para>When this option is used, the libc Name Service Switch (NSS) is bypassed for resolving users
+        and groups. Instead the files <filename>/etc/passwd</filename> and <filename>/etc/group</filename>
+        inside the alternate root are read directly. This means that users/groups not listed in these files
+        will not be resolved, i.e. LDAP NIS and other complex databases are not considered.</para></listitem>
       </varlistentry>
 
       <varlistentry>
index 08af76005c425e64092bbee9858a1e5686da0ced..da7389c7318e388c16ad7d792a8be6ca7705ecfd 100644 (file)
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>-s</option></term>
+        <term><option>--timeout-signal=</option></term>
+        <listitem>
+          <para>Set the signal which <filename>systemd-udevd</filename> will send to
+          forked off processes after reaching event timeout. The setting can be overridden
+          at boot time with the kernel command line option
+          <varname>udev.timeout_signal=</varname>. Setting to <constant>SIGABRT</constant>
+          may be helpful in order to debug worker timeouts. Defaults to
+          <constant>SIGKILL</constant>. Note that setting the option on the command line
+          overrides the setting from the configuration file.
+          </para>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>-N</option></term>
         <term><option>--resolve-names=</option></term>
 
   <refsect1><title>Kernel command line</title>
     <variablelist class='kernel-commandline-options'>
-      <para>Parameters starting with "rd." will be read when
-      <command>systemd-udevd</command> is used in an initrd.</para>
+      <para>Parameters prefixed with "rd." will be read when <command>systemd-udevd</command> is used in an
+      initrd, those without will be processed both in the initrd and on the host.</para>
       <varlistentry>
         <term><varname>udev.log_priority=</varname></term>
         <term><varname>rd.udev.log_priority=</varname></term>
           terminated due to kernel drivers taking too long to initialize.</para>
         </listitem>
       </varlistentry>
+      <varlistentry>
+        <term><varname>udev.timeout_signal=</varname></term>
+        <term><varname>rd.udev.timeout_signal=</varname></term>
+        <listitem>
+          <para>Specifies a signal that <filename>systemd-udevd</filename> will send to
+          workers on timeout. Note that kernel command line option overrides both the
+          setting in the configuration file and the one on the program command line.</para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>udev.blockdev_read_only</varname></term>
+        <term><varname>rd.udev.blockdev_read_only</varname></term>
+        <listitem>
+          <para>If specified, mark all physical block devices read-only as they appear. Synthetic block
+          devices (such as loopback block devices or device mapper devices) are left as they are. This is
+          useful to guarantee that the contents of physical block devices remains unmodified during runtime,
+          for example to implement fully stateless systems, for testing or for recovery situations where
+          corrupted file systems shall not be corrupted further through accidental modification.</para>
+
+          <para>A block device may be marked writable again by issuing the <command>blockdev
+          --setrw</command> command, see <citerefentry
+          project='man-pages'><refentrytitle>blockdev</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+          for details.</para>
+        </listitem>
+      </varlistentry>
       <varlistentry>
         <term><varname>net.ifnames=</varname></term>
         <listitem>
index ad412691a946847c39d1ae09cc501969aed5d3be..91196dff301fb92ce9cab3b2a82ac8a627b82495 100644 (file)
     <citerefentry project='man-pages'><refentrytitle>touch</refentrytitle><manvolnum>1</manvolnum></citerefentry>
     on it.</para>
 
+    <para>Note that if the <varname>systemd.condition-needs-update=</varname> kernel command line option is
+    used it overrides the <varname>ConditionNeedsUpdate=</varname> unit condition checks. In that case
+    <filename>systemd-update-done.service</filename> will not reset the condition state until a follow-up
+    reboot where the kernel switch is not specified anymore.</para>
   </refsect1>
 
   <refsect1>
diff --git a/man/systemd-xdg-autostart-generator.xml b/man/systemd-xdg-autostart-generator.xml
new file mode 100644 (file)
index 0000000..07b6e45
--- /dev/null
@@ -0,0 +1,57 @@
+<?xml version="1.0"?>
+<!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1+ -->
+<refentry id="systemd-xdg-autostart-generator" conditional="ENABLE_XDG_AUTOSTART">
+
+  <refentryinfo>
+    <title>systemd-xdg-autostart-generator</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>systemd-xdg-autostart-generator</refentrytitle>
+    <manvolnum>8</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>systemd-xdg-autostart-generator</refname>
+    <refpurpose>User unit generator for XDG autostart files</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <para><filename>/usr/lib/systemd/system-generators/systemd-xdg-autostart-generator</filename></para>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><filename>systemd-xdg-autostart-generator</filename> is a generator
+    that creates .service units for
+    <ulink url="https://specifications.freedesktop.org/autostart-spec/autostart-spec-latest.html">XDG autostart</ulink>
+    files.
+    This permits desktop environments to delegate startup of these applications to
+    <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+    .</para>
+
+    <para>Units created by <filename>systemd-xdg-autostart-generator</filename>
+    can be started by the desktop environment using <literal>xdg-desktop-autostart.target</literal>.
+    See
+    <citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+    for more details.</para>
+
+    <para><filename>systemd-xdg-autostart-generator</filename> implements
+    <citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd.target</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
index fd65f5da798b1377d2047a3c923e23f074f1c381..29b9bb14e14fc659a36a30d0b626f90c3fafe1bc 100644 (file)
@@ -35,9 +35,9 @@
     this unit type. See
     <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
     for the common options of all unit configuration files. The common
-    configuration items are configured in the generic <literal>[Unit]</literal> and
-    <literal>[Install]</literal> sections. The automount specific configuration options
-    are configured in the <literal>[Automount]</literal> section.</para>
+    configuration items are configured in the generic [Unit] and
+    [Install] sections. The automount specific configuration options
+    are configured in the [Automount] section.</para>
 
     <para>Automount units must be named after the automount directories they control. Example: the automount point
     <filename index="false">/home/lennart</filename> must be configured in a unit file
index ae786a32980843bfbae9953bac31072890735a88..085fd62bceef8d2428d48d7b818df4ec375e2d6e 100644 (file)
@@ -36,8 +36,8 @@
     <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
     for the common options of all unit configuration files. The common
     configuration items are configured in the generic
-    <literal>[Unit]</literal> and <literal>[Install]</literal>
-    sections. A separate <literal>[Device]</literal> section does not
+    [Unit] and [Install]
+    sections. A separate [Device] section does not
     exist, since no device-specific options may be configured.</para>
 
     <para>systemd will dynamically create device units for all kernel
@@ -60,7 +60,7 @@
     <para>Device units will be reloaded by systemd whenever the
     corresponding device generates a <literal>changed</literal> event.
     Other units can use <varname>ReloadPropagatedFrom=</varname> to react
-    to that event</para>
+    to that event.</para>
   </refsect1>
 
   <refsect1>
index 1ac760eec744af1d29470c56d66ba703f41266e5..d7e6caddf1e9bcea4794653cc1e900d0b693fdb1 100644 (file)
@@ -3,7 +3,9 @@
   "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
 <!-- SPDX-License-Identifier: LGPL-2.1+ -->
 
-<refentry id="systemd.dnssd" conditional='ENABLE_RESOLVE'>
+<refentry id="systemd.dnssd"
+          xmlns:xi="http://www.w3.org/2001/XInclude"
+          conditional='ENABLE_RESOLVE'>
 
   <refentryinfo>
     <title>systemd.dnssd</title>
@@ -62,7 +64,7 @@
   <refsect1>
     <title>[Service] Section Options</title>
 
-      <para>The network service file contains a <literal>[Service]</literal>
+      <para>The network service file contains a [Service]
       section, which specifies a discoverable network service announced in a
       local network with Multicast DNS broadcasts.</para>
 
@@ -73,7 +75,7 @@
             <para>An instance name of the network service as defined in the section 4.1.1 of <ulink
             url="https://tools.ietf.org/html/rfc6763">RFC 6763</ulink>, e.g. <literal>webserver</literal>.</para>
             <para>The option supports simple specifier expansion. The following expansions are understood:</para>
-            <table>
+            <table class='specifiers'>
               <title>Specifiers available</title>
               <tgroup cols='3' align='left' colsep='1' rowsep='1'>
                 <colspec colname="spec" />
                   </row>
                 </thead>
                 <tbody>
-                  <row>
-                    <entry><literal>%m</literal></entry>
-                    <entry>Machine ID</entry>
-                    <entry>The machine ID of the running system, formatted as string. See <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry> for more information.</entry>
-                  </row>
-                  <row>
-                    <entry><literal>%b</literal></entry>
-                    <entry>Boot ID</entry>
-                    <entry>The boot ID of the running system, formatted as string. See <citerefentry><refentrytitle>random</refentrytitle><manvolnum>4</manvolnum></citerefentry> for more information.</entry>
-                  </row>
-                  <row>
-                    <entry><literal>%H</literal></entry>
-                    <entry>Host name</entry>
-                    <entry>The hostname of the running system.</entry>
-                  </row>
-                  <row>
-                    <entry><literal>%v</literal></entry>
-                    <entry>Kernel release</entry>
-                    <entry>Identical to <command>uname -r</command> output.</entry>
-                  </row>
+                  <xi:include href="standard-specifiers.xml" xpointer="a"/>
+                  <xi:include href="standard-specifiers.xml" xpointer="b"/>
+                  <xi:include href="standard-specifiers.xml" xpointer="B"/>
+                  <xi:include href="standard-specifiers.xml" xpointer="H"/>
+                  <xi:include href="standard-specifiers.xml" xpointer="m"/>
+                  <xi:include href="standard-specifiers.xml" xpointer="o"/>
+                  <xi:include href="standard-specifiers.xml" xpointer="v"/>
+                  <xi:include href="standard-specifiers.xml" xpointer="w"/>
+                  <xi:include href="standard-specifiers.xml" xpointer="W"/>
+                  <xi:include href="standard-specifiers.xml" xpointer="percent"/>
                 </tbody>
               </tgroup>
             </table>
index f7995e611deb0cc7275ec9065a7b3f63053ce281..3618b52808e86c4ae2dc974a5db92054aa09647b 100644 (file)
         <varname>PrivateDevices=</varname> below, as it may change the setting of
         <varname>DevicePolicy=</varname>.</para>
 
+        <para>Units making use of <varname>RootImage=</varname> automatically gain an
+        <varname>After=</varname> dependency on <filename>systemd-udevd.service</filename>.</para>
+
+        <xi:include href="system-only.xml" xpointer="singular"/></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>RootHash=</varname></term>
+
+        <listitem><para>Takes a data integrity (dm-verity) root hash specified in hexadecimal, or the path to a file
+        containing a root hash in ASCII hexadecimal format. This option enables data integrity checks using dm-verity,
+        if the used image contains the appropriate integrity data (see above) or if <varname>RootVerity=</varname> is used.
+        The specified hash must match the root hash of integrity data, and is usually at least 256 bits (and hence 64
+        formatted hexadecimal characters) long (in case of SHA256 for example). If this option is not specified, but
+        the image file carries the <literal>user.verity.roothash</literal> extended file attribute (see <citerefentry
+        project='man-pages'><refentrytitle>xattr</refentrytitle><manvolnum>7</manvolnum></citerefentry>), then the root
+        hash is read from it, also as formatted hexadecimal characters. If the extended file attribute is not found (or
+        is not supported by the underlying file system), but a file with the <filename>.roothash</filename> suffix is
+        found next to the image file, bearing otherwise the same name (except if the image has the
+        <filename>.raw</filename> suffix, in which case the root hash file must not have it in its name), the root hash
+        is read from it and automatically used, also as formatted hexadecimal characters.</para>
+
+        <xi:include href="system-only.xml" xpointer="singular"/></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>RootHashSignature=</varname></term>
+
+        <listitem><para>Takes a PKCS7 formatted binary signature of the <varname>RootHash=</varname> option as a path
+        to a DER encoded signature file or as an ASCII base64 string encoding of the DER encoded signature, prefixed
+        by <literal>base64:</literal>. The dm-verity volume will only be opened if the signature of the root hash
+        signature is valid and created by a public key present in the kernel keyring. If this option is not specified,
+        but a file with the <filename>.roothash.p7s</filename> suffix is found next to the image file, bearing otherwise
+        the same name (except if the image has the <filename>.raw</filename> suffix, in which case the signature file
+        must not have it in its name), the signature is read from it and automatically used.</para>
+
+        <xi:include href="system-only.xml" xpointer="singular"/></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>RootVerity=</varname></term>
+
+        <listitem><para>Takes the path to a data integrity (dm-verity) file. This option enables data integrity checks
+        using dm-verity, if <varname>RootImage=</varname> is used and a root-hash is passed and if the used image itself
+        does not contains the integrity data. The integrity data must be matched by the root hash. If this option is not
+        specified, but a file with the <filename>.verity</filename> suffix is found next to the image file, bearing otherwise
+        the same name (except if the image has the <filename>.raw</filename> suffix, in which case the verity data file must
+        not have it in its name), the verity data is read from it and automatically used.</para>
+
+        <para>This option is supported only for disk images that contain a single file system, without an
+        enveloping partition table. Images that contain a GPT partition table should instead include both
+        root file system and matching Verity data in the same image, implementing the <ulink
+        url="https://systemd.io/DISCOVERABLE_PARTITIONS">Discoverable Partition Specification</ulink>.</para>
+
         <xi:include href="system-only.xml" xpointer="singular"/></listitem>
       </varlistentry>
 
         files or directories. Moreover <varname>ProtectSystem=strict</varname> and
         <varname>ProtectHome=read-only</varname> are implied, thus prohibiting the service to write to
         arbitrary file system locations. In order to allow the service to write to certain directories, they
-        have to be whitelisted using <varname>ReadWritePaths=</varname>, but care must be taken so that
+        have to be allow-listed using <varname>ReadWritePaths=</varname>, but care must be taken so that
         UID/GID recycling doesn't create security issues involving files created by the service. Use
         <varname>RuntimeDirectory=</varname> (see below) in order to assign a writable runtime directory to a
         service, owned by the dynamic user/group and removed automatically when the unit is terminated. Use
@@ -457,10 +511,11 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
       <varlistentry>
         <term><varname>AppArmorProfile=</varname></term>
 
-        <listitem><para>Takes a profile name as argument. The process executed by the unit will switch to this profile
-        when started.  Profiles must already be loaded in the kernel, or the unit will fail. This result in a non
-        operation if AppArmor is not enabled. If prefixed by <literal>-</literal>, all errors will be ignored. This
-        does not affect commands prefixed with <literal>+</literal>.</para></listitem>
+        <listitem><para>Takes a profile name as argument. The process executed by the unit will switch to
+        this profile when started. Profiles must already be loaded in the kernel, or the unit will fail. If
+        prefixed by <literal>-</literal>, all errors will be ignored. This setting has no effect if AppArmor
+        is not enabled. This setting not affect commands prefixed with <literal>+</literal>.</para>
+        </listitem>
       </varlistentry>
 
       <varlistentry>
@@ -655,8 +710,39 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
         <term><varname>UMask=</varname></term>
 
         <listitem><para>Controls the file mode creation mask. Takes an access mode in octal notation. See
-        <citerefentry><refentrytitle>umask</refentrytitle><manvolnum>2</manvolnum></citerefentry> for details. Defaults
-        to 0022.</para></listitem>
+        <citerefentry><refentrytitle>umask</refentrytitle><manvolnum>2</manvolnum></citerefentry> for
+        details. Defaults to 0022 for system units. For units of the user service manager the default value
+        is inherited from the user instance (whose default is inherited from the system service manager, and
+        thus also is 0022). Hence changing the default value of a user instance, either via
+        <varname>UMask=</varname> or via a PAM module, will affect the user instance itself and all user
+        units started by the user instance unless a user unit has specified its own
+        <varname>UMask=</varname>.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>CoredumpFilter=</varname></term>
+
+        <listitem><para>Controls which types of memory mappings will be saved if the process dumps core
+        (using the <filename>/proc/<replaceable>pid</replaceable>/coredump_filter</filename> file). Takes a
+        whitespace-separated combination of mapping type names or numbers (with the default base 16). Mapping
+        type names are <constant>private-anonymous</constant>, <constant>shared-anonymous</constant>,
+        <constant>private-file-backed</constant>, <constant>shared-file-backed</constant>,
+        <constant>elf-headers</constant>, <constant>private-huge</constant>,
+        <constant>shared-huge</constant>, <constant>private-dax</constant>, <constant>shared-dax</constant>,
+        and the special values <constant>all</constant> (all types) and <constant>default</constant> (the
+        kernel default of <literal><constant>private-anonymous</constant>
+        <constant>shared-anonymous</constant> <constant>elf-headers</constant>
+        <constant>private-huge</constant></literal>). See
+        <citerefentry project='man-pages'><refentrytitle>core</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+        for the meaning of the mapping types. When specified multiple times, all specified masks are
+        ORed. When not set, or if the empty value is assigned, the inherited value is not changed.</para>
+
+        <example>
+          <title>Add DAX pages to the dump filter</title>
+
+          <programlisting>CoredumpFilter=default private-dax shared-dax</programlisting>
+        </example>
+        </listitem>
       </varlistentry>
 
       <varlistentry>
@@ -795,7 +881,7 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
         in <varname>NUMAMask=</varname>. For more details on each policy please see,
         <citerefentry><refentrytitle>set_mempolicy</refentrytitle><manvolnum>2</manvolnum></citerefentry>. For overall
         overview of NUMA support in Linux see,
-        <citerefentry><refentrytitle>numa</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+        <citerefentry project='man-pages'><refentrytitle>numa</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
         </para></listitem>
       </varlistentry>
 
@@ -982,14 +1068,16 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
         <varname>RootDirectory=</varname> or <varname>RootImage=</varname> these paths always reside on the host and
         are mounted from there into the unit's file system namespace.</para>
 
-        <para>If <varname>DynamicUser=</varname> is used in conjunction with <varname>StateDirectory=</varname>,
-        <varname>CacheDirectory=</varname> and <varname>LogsDirectory=</varname> is slightly altered: the directories
-        are created below <filename>/var/lib/private</filename>, <filename>/var/cache/private</filename> and
+        <para>If <varname>DynamicUser=</varname> is used in conjunction with
+        <varname>StateDirectory=</varname>, the logic for <varname>CacheDirectory=</varname> and
+        <varname>LogsDirectory=</varname> is slightly altered: the directories are created below
+        <filename>/var/lib/private</filename>, <filename>/var/cache/private</filename> and
         <filename>/var/log/private</filename>, respectively, which are host directories made inaccessible to
-        unprivileged users, which ensures that access to these directories cannot be gained through dynamic user ID
-        recycling. Symbolic links are created to hide this difference in behaviour. Both from perspective of the host
-        and from inside the unit, the relevant directories hence always appear directly below
-        <filename>/var/lib</filename>, <filename>/var/cache</filename> and <filename>/var/log</filename>.</para>
+        unprivileged users, which ensures that access to these directories cannot be gained through dynamic
+        user ID recycling. Symbolic links are created to hide this difference in behaviour. Both from
+        perspective of the host and from inside the unit, the relevant directories hence always appear
+        directly below <filename>/var/lib</filename>, <filename>/var/cache</filename> and
+        <filename>/var/log</filename>.</para>
 
         <para>Use <varname>RuntimeDirectory=</varname> to manage one or more runtime directories for the unit and bind
         their lifetime to the daemon runtime. This is particularly useful for unprivileged daemons that cannot create
@@ -1064,8 +1152,8 @@ StateDirectory=aaa/bbb ccc</programlisting>
         clean …</command>, see
         <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
         details. Takes the usual time values and defaults to <constant>infinity</constant>, i.e. by default
-        no time-out is applied. If a time-out is configured the clean operation will be aborted forcibly when
-        the time-out is reached, potentially leaving resources on disk.</para></listitem>
+        no timeout is applied. If a timeout is configured the clean operation will be aborted forcibly when
+        the timeout is reached, potentially leaving resources on disk.</para></listitem>
       </varlistentry>
 
       <varlistentry>
@@ -1079,12 +1167,13 @@ StateDirectory=aaa/bbb ccc</programlisting>
         contain symlinks, they are resolved relative to the root directory set with
         <varname>RootDirectory=</varname>/<varname>RootImage=</varname>.</para>
 
-        <para>Paths listed in <varname>ReadWritePaths=</varname> are accessible from within the namespace with the same
-        access modes as from outside of it. Paths listed in <varname>ReadOnlyPaths=</varname> are accessible for
-        reading only, writing will be refused even if the usual file access controls would permit this. Nest
-        <varname>ReadWritePaths=</varname> inside of <varname>ReadOnlyPaths=</varname> in order to provide writable
-        subdirectories within read-only directories. Use <varname>ReadWritePaths=</varname> in order to whitelist
-        specific paths for write access if <varname>ProtectSystem=strict</varname> is used.</para>
+        <para>Paths listed in <varname>ReadWritePaths=</varname> are accessible from within the namespace
+        with the same access modes as from outside of it. Paths listed in <varname>ReadOnlyPaths=</varname>
+        are accessible for reading only, writing will be refused even if the usual file access controls would
+        permit this. Nest <varname>ReadWritePaths=</varname> inside of <varname>ReadOnlyPaths=</varname> in
+        order to provide writable subdirectories within read-only directories. Use
+        <varname>ReadWritePaths=</varname> in order to allow-list specific paths for write access if
+        <varname>ProtectSystem=strict</varname> is used.</para>
 
         <para>Paths listed in <varname>InaccessiblePaths=</varname> will be made inaccessible for processes inside
         the namespace along with everything below them in the file system hierarchy. This may be more restrictive than
@@ -1152,8 +1241,8 @@ BindReadOnlyPaths=/var/lib/systemd</programlisting>
         <term><varname>PrivateTmp=</varname></term>
 
         <listitem><para>Takes a boolean argument. If true, sets up a new file system namespace for the executed
-        processes and mounts private <filename>/tmp</filename> and <filename>/var/tmp</filename> directories inside it
-        that is not shared by processes outside of the namespace. This is useful to secure access to temporary files of
+        processes and mounts private <filename>/tmp/</filename> and <filename>/var/tmp/</filename> directories inside it
+        that are not shared by processes outside of the namespace. This is useful to secure access to temporary files of
         the process, but makes sharing between processes via <filename>/tmp</filename> or <filename>/var/tmp</filename>
         impossible. If this is enabled, all temporary files created by a service in these directories will be removed
         after the service is stopped.  Defaults to false. It is possible to run two or more units within the same
@@ -1313,7 +1402,7 @@ BindReadOnlyPaths=/var/lib/systemd</programlisting>
         this option removes <constant>CAP_SYS_TIME</constant> and <constant>CAP_WAKE_ALARM</constant> from the
         capability bounding set for this unit, installs a system call filter to block calls that can set the
         clock, and <varname>DeviceAllow=char-rtc r</varname> is implied. This ensures <filename>/dev/rtc0</filename>,
-        <filename>/dev/rtc1</filename>, etc are made read only to the service. See
+        <filename>/dev/rtc1</filename>, etc. are made read-only to the service. See
         <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>
         for the details about <varname>DeviceAllow=</varname>.</para>
 
@@ -1398,29 +1487,31 @@ BindReadOnlyPaths=/var/lib/systemd</programlisting>
       <varlistentry>
         <term><varname>RestrictAddressFamilies=</varname></term>
 
-        <listitem><para>Restricts the set of socket address families accessible to the processes of this unit. Takes a
-        space-separated list of address family names to whitelist, such as <constant>AF_UNIX</constant>,
-        <constant>AF_INET</constant> or <constant>AF_INET6</constant>. When prefixed with <constant>~</constant> the
-        listed address families will be applied as blacklist, otherwise as whitelist.  Note that this restricts access
-        to the <citerefentry
-        project='man-pages'><refentrytitle>socket</refentrytitle><manvolnum>2</manvolnum></citerefentry> system call
-        only. Sockets passed into the process by other means (for example, by using socket activation with socket
-        units, see <citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>5</manvolnum></citerefentry>)
-        are unaffected. Also, sockets created with <function>socketpair()</function> (which creates connected AF_UNIX
-        sockets only) are unaffected. Note that this option has no effect on 32-bit x86, s390, s390x, mips, mips-le,
-        ppc, ppc-le, pcc64, ppc64-le and is ignored (but works correctly on other ABIs, including x86-64). Note that on
-        systems supporting multiple ABIs (such as x86/x86-64) it is recommended to turn off alternative ABIs for
-        services, so that they cannot be used to circumvent the restrictions of this option. Specifically, it is
-        recommended to combine this option with <varname>SystemCallArchitectures=native</varname> or similar. If
-        running in user mode, or in system mode, but without the <constant>CAP_SYS_ADMIN</constant> capability
-        (e.g. setting <varname>User=nobody</varname>), <varname>NoNewPrivileges=yes</varname> is implied. By default,
-        no restrictions apply, all address families are accessible to processes. If assigned the empty string, any
-        previous address family restriction changes are undone. This setting does not affect commands prefixed with
-        <literal>+</literal>.</para>
+        <listitem><para>Restricts the set of socket address families accessible to the processes of this
+        unit. Takes a space-separated list of address family names to allow-list, such as
+        <constant>AF_UNIX</constant>, <constant>AF_INET</constant> or <constant>AF_INET6</constant>. When
+        prefixed with <constant>~</constant> the listed address families will be applied as deny list,
+        otherwise as allow list.  Note that this restricts access to the <citerefentry
+        project='man-pages'><refentrytitle>socket</refentrytitle><manvolnum>2</manvolnum></citerefentry>
+        system call only. Sockets passed into the process by other means (for example, by using socket
+        activation with socket units, see
+        <citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>5</manvolnum></citerefentry>)
+        are unaffected. Also, sockets created with <function>socketpair()</function> (which creates connected
+        AF_UNIX sockets only) are unaffected. Note that this option has no effect on 32-bit x86, s390, s390x,
+        mips, mips-le, ppc, ppc-le, ppc64, ppc64-le and is ignored (but works correctly on other ABIs,
+        including x86-64). Note that on systems supporting multiple ABIs (such as x86/x86-64) it is
+        recommended to turn off alternative ABIs for services, so that they cannot be used to circumvent the
+        restrictions of this option. Specifically, it is recommended to combine this option with
+        <varname>SystemCallArchitectures=native</varname> or similar. If running in user mode, or in system
+        mode, but without the <constant>CAP_SYS_ADMIN</constant> capability (e.g. setting
+        <varname>User=nobody</varname>), <varname>NoNewPrivileges=yes</varname> is implied. By default, no
+        restrictions apply, all address families are accessible to processes. If assigned the empty string,
+        any previous address family restriction changes are undone. This setting does not affect commands
+        prefixed with <literal>+</literal>.</para>
 
         <para>Use this option to limit exposure of processes to remote access, in particular via exotic and sensitive
         network protocols, such as <constant>AF_PACKET</constant>. Note that in most cases, the local
-        <constant>AF_UNIX</constant> address family should be included in the configured whitelist as it is frequently
+        <constant>AF_UNIX</constant> address family should be included in the configured allow list as it is frequently
         used for local communication, including for
         <citerefentry><refentrytitle>syslog</refentrytitle><manvolnum>2</manvolnum></citerefentry>
         logging.</para></listitem>
@@ -1438,9 +1529,9 @@ BindReadOnlyPaths=/var/lib/systemd</programlisting>
         any combination of: <constant>cgroup</constant>, <constant>ipc</constant>, <constant>net</constant>,
         <constant>mnt</constant>, <constant>pid</constant>, <constant>user</constant> and <constant>uts</constant>. Any
         namespace type listed is made accessible to the unit's processes, access to namespace types not listed is
-        prohibited (whitelisting). By prepending the list with a single tilde character (<literal>~</literal>) the
+        prohibited (allow-listing). By prepending the list with a single tilde character (<literal>~</literal>) the
         effect may be inverted: only the listed namespace types will be made inaccessible, all unlisted ones are
-        permitted (blacklisting). If the empty string is assigned, the default namespace restrictions are applied,
+        permitted (deny-listing). If the empty string is assigned, the default namespace restrictions are applied,
         which is equivalent to false. This option may appear more than once, in which case the namespace types are
         merged by <constant>OR</constant>, or by <constant>AND</constant> if the lines are prefixed with
         <literal>~</literal> (see examples below). Internally, this setting limits access to the
@@ -1601,14 +1692,14 @@ RestrictNamespaces=~cgroup net</programlisting>
         points of the file system namespace created for each process of this unit. Other file system namespacing unit
         settings (see the discussion in <varname>PrivateMounts=</varname> above) will implicitly disable mount and
         unmount propagation from the unit's processes towards the host by changing the propagation setting of all mount
-        points in the unit's file system namepace to <option>slave</option> first. Setting this option to
+        points in the unit's file system namespace to <option>slave</option> first. Setting this option to
         <option>shared</option> does not reestablish propagation in that case.</para>
 
         <para>If not set – but file system namespaces are enabled through another file system namespace unit setting –
         <option>shared</option> mount propagation is used, but — as mentioned — as <option>slave</option> is applied
         first, propagation from the unit's processes to the host is still turned off.</para>
 
-        <para>It is not recommended to to use <option>private</option> mount propagation for units, as this means
+        <para>It is not recommended to use <option>private</option> mount propagation for units, as this means
         temporary mounts (such as removable media) of the host will stay mounted and thus indefinitely busy in forked
         off processes, as unmount propagation events won't be received by the file system namespace of the unit.</para>
 
@@ -1630,15 +1721,15 @@ RestrictNamespaces=~cgroup net</programlisting>
 
         <listitem><para>Takes a space-separated list of system call names. If this setting is used, all
         system calls executed by the unit processes except for the listed ones will result in immediate
-        process termination with the <constant>SIGSYS</constant> signal (whitelisting). (See
+        process termination with the <constant>SIGSYS</constant> signal (allow-listing). (See
         <varname>SystemCallErrorNumber=</varname> below for changing the default action). If the first
         character of the list is <literal>~</literal>, the effect is inverted: only the listed system calls
-        will result in immediate process termination (blacklisting). Blacklisted system calls and system call
+        will result in immediate process termination (deny-listing). Deny-listed system calls and system call
         groups may optionally be suffixed with a colon (<literal>:</literal>) and <literal>errno</literal>
         error number (between 0 and 4095) or errno name such as <constant>EPERM</constant>,
         <constant>EACCES</constant> or <constant>EUCLEAN</constant> (see <citerefentry
         project='man-pages'><refentrytitle>errno</refentrytitle><manvolnum>3</manvolnum></citerefentry> for a
-        full list). This value will be returned when a blacklisted system call is triggered, instead of
+        full list). This value will be returned when a deny-listed system call is triggered, instead of
         terminating the processes immediately.  This value takes precedence over the one given in
         <varname>SystemCallErrorNumber=</varname>, see below.  If running in user mode, or in system mode,
         but without the <constant>CAP_SYS_ADMIN</constant> capability (e.g. setting
@@ -1647,7 +1738,7 @@ RestrictNamespaces=~cgroup net</programlisting>
         for enforcing a minimal sandboxing environment. Note that the <function>execve</function>,
         <function>exit</function>, <function>exit_group</function>, <function>getrlimit</function>,
         <function>rt_sigreturn</function>, <function>sigreturn</function> system calls and the system calls
-        for querying time and sleeping are implicitly whitelisted and do not need to be listed
+        for querying time and sleeping are implicitly allow-listed and do not need to be listed
         explicitly. This option may be specified more than once, in which case the filter masks are
         merged. If the empty string is assigned, the filter is reset, all prior assignments will have no
         effect. This does not affect commands prefixed with <literal>+</literal>.</para>
@@ -1665,12 +1756,13 @@ RestrictNamespaces=~cgroup net</programlisting>
         might be necessary to temporarily disable system call filters in order to simplify debugging of such
         failures.</para>
 
-        <para>If you specify both types of this option (i.e.  whitelisting and blacklisting), the first encountered
-        will take precedence and will dictate the default action (termination or approval of a system call). Then the
-        next occurrences of this option will add or delete the listed system calls from the set of the filtered system
-        calls, depending of its type and the default action. (For example, if you have started with a whitelisting of
-        <function>read</function> and <function>write</function>, and right after it add a blacklisting of
-        <function>write</function>, then <function>write</function> will be removed from the set.)</para>
+        <para>If you specify both types of this option (i.e.  allow-listing and deny-listing), the first
+        encountered will take precedence and will dictate the default action (termination or approval of a
+        system call). Then the next occurrences of this option will add or delete the listed system calls
+        from the set of the filtered system calls, depending of its type and the default action. (For
+        example, if you have started with an allow list rule for <function>read</function> and
+        <function>write</function>, and right after it add a deny list rule for <function>write</function>,
+        then <function>write</function> will be removed from the set.)</para>
 
         <para>As the number of possible system calls is large, predefined sets of system calls are provided.  A set
         starts with <literal>@</literal> character, followed by name of the set.
@@ -1714,7 +1806,7 @@ RestrictNamespaces=~cgroup net</programlisting>
               </row>
               <row>
                 <entry>@file-system</entry>
-                <entry>File system operations: opening, creating files and directories for read and write, renaming and removing them, reading file properties, or creating hard and symbolic links.</entry>
+                <entry>File system operations: opening, creating files and directories for read and write, renaming and removing them, reading file properties, or creating hard and symbolic links</entry>
               </row>
               <row>
                 <entry>@io-event</entry>
@@ -1730,7 +1822,7 @@ RestrictNamespaces=~cgroup net</programlisting>
               </row>
               <row>
                 <entry>@memlock</entry>
-                <entry>Locking of memory into RAM (<citerefentry project='man-pages'><refentrytitle>mlock</refentrytitle><manvolnum>2</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>mlockall</refentrytitle><manvolnum>2</manvolnum></citerefentry> and related calls)</entry>
+                <entry>Locking of memory in RAM (<citerefentry project='man-pages'><refentrytitle>mlock</refentrytitle><manvolnum>2</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>mlockall</refentrytitle><manvolnum>2</manvolnum></citerefentry> and related calls)</entry>
               </row>
               <row>
                 <entry>@module</entry>
@@ -1754,7 +1846,7 @@ RestrictNamespaces=~cgroup net</programlisting>
               </row>
               <row>
                 <entry>@process</entry>
-                <entry>Process control, execution, namespaceing operations (<citerefentry project='man-pages'><refentrytitle>clone</refentrytitle><manvolnum>2</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>kill</refentrytitle><manvolnum>2</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>namespaces</refentrytitle><manvolnum>7</manvolnum></citerefentry>, …</entry>
+                <entry>Process control, execution, namespaceing operations (<citerefentry project='man-pages'><refentrytitle>clone</refentrytitle><manvolnum>2</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>kill</refentrytitle><manvolnum>2</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>namespaces</refentrytitle><manvolnum>7</manvolnum></citerefentry>, …)</entry>
               </row>
               <row>
                 <entry>@raw-io</entry>
@@ -1782,11 +1874,11 @@ RestrictNamespaces=~cgroup net</programlisting>
               </row>
               <row>
                 <entry>@sync</entry>
-                <entry>Synchronizing files and memory to disk: (<citerefentry project='man-pages'><refentrytitle>fsync</refentrytitle><manvolnum>2</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>msync</refentrytitle><manvolnum>2</manvolnum></citerefentry>, and related calls)</entry>
+                <entry>Synchronizing files and memory to disk (<citerefentry project='man-pages'><refentrytitle>fsync</refentrytitle><manvolnum>2</manvolnum></citerefentry>, <citerefentry project='man-pages'><refentrytitle>msync</refentrytitle><manvolnum>2</manvolnum></citerefentry>, and related calls)</entry>
               </row>
               <row>
                 <entry>@system-service</entry>
-                <entry>A reasonable set of system calls used by common system services, excluding any special purpose calls. This is the recommended starting point for whitelisting system calls for system services, as it contains what is typically needed by system services, but excludes overly specific interfaces. For example, the following APIs are excluded: <literal>@clock</literal>, <literal>@mount</literal>, <literal>@swap</literal>, <literal>@reboot</literal>.</entry>
+                <entry>A reasonable set of system calls used by common system services, excluding any special purpose calls. This is the recommended starting point for allow-listing system calls for system services, as it contains what is typically needed by system services, but excludes overly specific interfaces. For example, the following APIs are excluded: <literal>@clock</literal>, <literal>@mount</literal>, <literal>@swap</literal>, <literal>@reboot</literal>.</entry>
               </row>
               <row>
                 <entry>@timer</entry>
@@ -1802,9 +1894,10 @@ RestrictNamespaces=~cgroup net</programlisting>
         <command>systemd-analyze syscall-filter</command> to list the actual list of system calls in each
         filter.</para>
 
-        <para>Generally, whitelisting system calls (rather than blacklisting) is the safer mode of operation. It is
-        recommended to enforce system call whitelists for all long-running system services. Specifically, the
-        following lines are a relatively safe basic choice for the majority of system services:</para>
+        <para>Generally, allow-listing system calls (rather than deny-listing) is the safer mode of
+        operation. It is recommended to enforce system call allow lists for all long-running system
+        services. Specifically, the following lines are a relatively safe basic choice for the majority of
+        system services:</para>
 
         <programlisting>[Service]
 SystemCallFilter=@system-service
@@ -1815,9 +1908,9 @@ SystemCallErrorNumber=EPERM</programlisting>
         call may be used to execute operations similar to what can be done with the older
         <function>kill()</function> system call, hence blocking the latter without the former only provides
         weak protection. Since new system calls are added regularly to the kernel as development progresses,
-        keeping system call blacklists comprehensive requires constant work. It is thus recommended to use
-        whitelisting instead, which offers the benefit that new system calls are by default implicitly
-        blocked until the whitelist is updated.</para>
+        keeping system call deny lists comprehensive requires constant work. It is thus recommended to use
+        allow-listing instead, which offers the benefit that new system calls are by default implicitly
+        blocked until the allow list is updated.</para>
 
         <para>Also note that a number of system calls are required to be accessible for the dynamic linker to
         work. The dynamic linker is required for running most regular programs (specifically: all dynamic ELF
@@ -1859,7 +1952,7 @@ SystemCallErrorNumber=EPERM</programlisting>
         manager is compiled for). If running in user mode, or in system mode, but without the
         <constant>CAP_SYS_ADMIN</constant> capability (e.g. setting <varname>User=nobody</varname>),
         <varname>NoNewPrivileges=yes</varname> is implied. By default, this option is set to the empty list, i.e. no
-        system call architecture filtering is applied.</para>
+        filtering is applied.</para>
 
         <para>If this setting is used, processes of this unit will only be permitted to call native system calls, and
         system calls of the specified architectures. For the purposes of this option, the x32 architecture is treated
@@ -2117,8 +2210,9 @@ SystemCallErrorNumber=EPERM</programlisting>
         <constant>AF_UNIX</constant> socket in the file system, as in that case only a
         single stream connection is created for both input and output.</para>
 
-        <para><option>append:<replaceable>path</replaceable></option> is similar to <option>file:<replaceable>path
-        </replaceable></option> above, but it opens the file in append mode.</para>
+        <para><option>append:<replaceable>path</replaceable></option> is similar to
+        <option>file:<replaceable>path</replaceable></option> above, but it opens the file in append mode.
+        </para>
 
         <para><option>socket</option> connects standard output to a socket acquired via socket activation. The
         semantics are similar to the same option of <varname>StandardInput=</varname>, see above.</para>
@@ -2455,7 +2549,7 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy
     <varname>UnsetEnvironment=</varname> are removed again from the compiled environment variable list, immediately
     before it is passed to the executed process.</para>
 
-    <para>The following select environment variables are set or propagated by the service manager for each invoked
+    <para>The following environment variables are set or propagated by the service manager for each invoked
     process:</para>
 
     <variablelist class='environment-variables'>
@@ -2526,7 +2620,7 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy
         <term><varname>$LOGS_DIRECTORY</varname></term>
         <term><varname>$CONFIGURATION_DIRECTORY</varname></term>
 
-        <listitem><para>Contains and absolute paths to the directories defined with
+        <listitem><para>Absolute paths to the directories defined with
         <varname>RuntimeDirectory=</varname>, <varname>StateDirectory=</varname>,
         <varname>CacheDirectory=</varname>, <varname>LogsDirectory=</varname>, and
         <varname>ConfigurationDirectory=</varname> when those settings are used.</para>
@@ -2588,6 +2682,13 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy
         </para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>$LOG_NAMESPACE</varname></term>
+
+        <listitem><para>If the <varname>LogNamespace=</varname> service setting is used, contains name of the
+        selected logging namespace.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>$JOURNAL_STREAM</varname></term>
 
@@ -3125,7 +3226,7 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy
           <row>
             <entry>242</entry>
             <entry><constant>EXIT_NUMA_POLICY</constant></entry>
-            <entry>Failed to set up unit's NUMA memory policy. See <varname>NUMAPolicy=</varname> and <varname>NUMAMask=</varname>above.</entry>
+            <entry>Failed to set up unit's NUMA memory policy. See <varname>NUMAPolicy=</varname> and <varname>NUMAMask=</varname> above.</entry>
           </row>
 
         </tbody>
index dd0b5919960768952bb6f7a273a8a9dd59d8a610..babbe14e0447985a0557f401ff156662be31d185 100644 (file)
@@ -310,6 +310,7 @@ find $dir</programlisting>
       <citerefentry><refentrytitle>systemd-rc-local-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd-system-update-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd-sysv-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-xdg-autostart-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd.environment-generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>
index a0771f3c135a2504fdec1a2e9d5f4311396aaefd..197a468f25de8c23e2a73a359fd06b3538f5b6f3 100644 (file)
           </para>
         </listitem>
       </varlistentry>
+
+      <varlistentry>
+        <term><varname>DOCUMENTATION=</varname></term>
+        <listitem>
+          <para>A documentation URL with further information about the topic of the log message. Tools such
+          as <command>journalctl</command> will include a hyperlink to an URL specified this way in their
+          output. Should be a <literal>http://</literal>, <literal>https://</literal>,
+          <literal>file:/</literal>, <literal>man:</literal> or <literal>info:</literal> URL.</para>
+        </listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 
       <varlistentry>
         <term><varname>_LINE_BREAK=</varname></term>
         <listitem>
-          <para>Only applies to <literal>_TRANSPORT=stdout</literal> records: indicates that the log message in the
-          standard output/error stream was not terminated with a normal newline character (<literal>\n</literal>,
-          i.e. ASCII 10). Specifically, when set this field is one of <option>nul</option> (in case the line was
-          terminated by a NUL byte), <option>line-max</option> (in case the maximum log line length was reached, as
-          configured with <varname>LineMax=</varname> in
-          <citerefentry><refentrytitle>journald.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>) or
-          <option>eof</option> (if this was the last log record of a stream and the stream ended without a final
-          newline character). Note that this record is not generated when a normal newline character was used for
-          marking the log line end.</para>
+          <para>Only applies to <literal>_TRANSPORT=stdout</literal> records: indicates that the log message
+          in the standard output/error stream was not terminated with a normal newline character
+          (<literal>\n</literal>, i.e. ASCII 10). Specifically, when set this field is one of
+          <option>nul</option> (in case the line was terminated by a NUL byte), <option>line-max</option> (in
+          case the maximum log line length was reached, as configured with <varname>LineMax=</varname> in
+          <citerefentry><refentrytitle>journald.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>),
+          <option>eof</option> (if this was the last log record of a stream and the stream ended without a
+          final newline character), or <option>pid-change</option> (if the process which generated the log
+          output changed in the middle of a line). Note that this record is not generated when a normal
+          newline character was used for marking the log line end.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
     structured log entries via calls such as
     <citerefentry><refentrytitle>sd_journal_send</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
     They may also not be used as matches for
-    <citerefentry><refentrytitle>sd_journal_add_match</refentrytitle><manvolnum>3</manvolnum></citerefentry></para>
+    <citerefentry><refentrytitle>sd_journal_add_match</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+    </para>
 
     <variablelist class='journal-directives'>
       <varlistentry>
index 6a1c67d4063f8451400c0877cfafcf77619673b9..73f61c80e4fdf28c5c9a5bf9f2a3556b8f334e9c 100644 (file)
 
       <varlistentry>
         <term><varname>KillMode=</varname></term>
-        <listitem><para>Specifies how processes of this unit shall be
-        killed. One of
-        <option>control-group</option>,
-        <option>process</option>,
-        <option>mixed</option>,
+        <listitem><para>Specifies how processes of this unit shall be killed. One of
+        <option>control-group</option>, <option>mixed</option>, <option>process</option>,
         <option>none</option>.</para>
 
-        <para>If set to <option>control-group</option>, all remaining
-        processes in the control group of this unit will be killed on
-        unit stop (for services: after the stop command is executed,
-        as configured with <varname>ExecStop=</varname>). If set to
-        <option>process</option>, only the main process itself is
-        killed. If set to <option>mixed</option>, the
-        <constant>SIGTERM</constant> signal (see below) is sent to the
-        main process while the subsequent <constant>SIGKILL</constant>
-        signal (see below) is sent to all remaining processes of the
-        unit's control group. If set to <option>none</option>, no
-        process is killed. In this case, only the stop command will be
-        executed on unit stop, but no process will be killed otherwise.
-        Processes remaining alive after stop are left in their control
-        group and the control group continues to exist after stop
-        unless it is empty.</para>
+        <para>If set to <option>control-group</option>, all remaining processes in the control group of this
+        unit will be killed on unit stop (for services: after the stop command is executed, as configured
+        with <varname>ExecStop=</varname>). If set to <option>mixed</option>, the
+        <constant>SIGTERM</constant> signal (see below) is sent to the main process while the subsequent
+        <constant>SIGKILL</constant> signal (see below) is sent to all remaining processes of the unit's
+        control group. If set to <option>process</option>, only the main process itself is killed (not
+        recommended!). If set to <option>none</option>, no process is killed (strongly recommended
+        against!). In this case, only the stop command will be executed on unit stop, but no process will be
+        killed otherwise.  Processes remaining alive after stop are left in their control group and the
+        control group continues to exist after stop unless empty.</para>
+
+        <para>Note that it is not recommended to set <varname>KillMode=</varname> to
+        <constant>process</constant> or even <constant>none</constant>, as this allows processes to escape
+        the service manager's lifecycle and resource management, and to remain running even while their
+        service is considered stopped and is assumed to not consume any resources.</para>
 
         <para>Processes will first be terminated via <constant>SIGTERM</constant> (unless the signal to send
         is changed via <varname>KillSignal=</varname> or <varname>RestartKillSignal=</varname>). Optionally,
         terminate upon receiving the initial <constant>SIGTERM</constant>
         signal. This can be achieved by configuring <varname>LimitCORE=</varname>
         and setting <varname>FinalKillSignal=</varname> to either
-        <constant>SIGQUIT</constant> or <constant>SIGABRT</constant>
+        <constant>SIGQUIT</constant> or <constant>SIGABRT</constant>.
         Defaults to <constant>SIGKILL</constant>.
         </para></listitem>
       </varlistentry>
index acda31066bd281ee86308dfa7e0649f8e515eacb..af69c48383c60751bb2248138c68233cc0077458 100644 (file)
@@ -27,9 +27,9 @@
     <title>Description</title>
 
     <para>A plain ini-style text file that encodes configuration for matching network devices, used by
-    <citerefentry><refentrytitle>systemd-udev</refentrytitle><manvolnum>8</manvolnum></citerefentry> and in
+    <citerefentry><refentrytitle>systemd-udevd</refentrytitle><manvolnum>8</manvolnum></citerefentry> and in
     particular its <command>net_setup_link</command> builtin. See
-    <citerefentry><refentrytitle>systemd.syntax</refentrytitle><manvolnum>5</manvolnum></citerefentry> for a
+    <citerefentry><refentrytitle>systemd.syntax</refentrytitle><manvolnum>7</manvolnum></citerefentry> for a
     general description of the syntax.</para>
 
     <para>The link files are read from the files located in the system
@@ -64,8 +64,8 @@
     <title>[Match] Section Options</title>
 
     <para>A link file is said to match a device if all matches specified by the
-    <literal>[Match]</literal> section are satisfied. When a link file does not contain valid settings
-    in <literal>[Match]</literal> section, then the file will match all devices and
+    [Match] section are satisfied. When a link file does not contain valid settings
+    in [Match] section, then the file will match all devices and
     <command>systemd-udevd</command> warns about that. Hint: to avoid the warning and to make it clear
     that all interfaces shall be matched, add the following:
     <programlisting>OriginalName=*</programlisting>
           When unset, the kernel's default will be used.</para>
         </listitem>
       </varlistentry>
+      <varlistentry>
+        <term><varname>RxMiniBufferSize=</varname></term>
+        <listitem>
+          <para>Takes an integer. Specifies the maximum number of pending packets in the NIC mini receive buffer.
+          When unset, the kernel's default will be used.</para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>RxJumboBufferSize=</varname></term>
+        <listitem>
+          <para>Takes an integer. Specifies the maximum number of pending packets in the NIC jumbo receive buffer.
+          When unset, the kernel's default will be used.</para>
+        </listitem>
+      </varlistentry>
       <varlistentry>
         <term><varname>TxBufferSize=</varname></term>
         <listitem>
           When unset, the kernel's default will be used.</para>
         </listitem>
       </varlistentry>
+      <varlistentry>
+        <term><varname>RxFlowControl=</varname></term>
+        <listitem>
+          <para>Takes a boolean. When set, enables the receive flow control, also known as the ethernet
+          receive PAUSE message (generate and send ethernet PAUSE frames). When unset, the kernel's
+          default will be used.</para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>TxFlowControl=</varname></term>
+        <listitem>
+          <para>Takes a boolean. When set, enables the transmit flow control, also known as the ethernet
+          transmit PAUSE message (respond to received ethernet PAUSE frames). When unset, the kernel's
+          default will be used.</para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term><varname>AutoNegotiationFlowControl=</varname></term>
+        <listitem>
+          <para>Takes a boolean. When set, the auto negotiation enables the interface to exchange state
+          advertisements with the connected peer so that the two devices can agree on the ethernet
+          PAUSE configuration. When unset, the kernel's default will be used.</para>
+        </listitem>
+      </varlistentry>
 
     </variablelist>
   </refsect1>
index 3122eef2a005aa0c542607b0d66949f47b1b3606..9e1f5d40fd9f2837aa22b1071d10983071155247 100644 (file)
@@ -34,9 +34,9 @@
     this unit type. See
     <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
     for the common options of all unit configuration files. The common
-    configuration items are configured in the generic <literal>[Unit]</literal> and
-    <literal>[Install]</literal> sections. The mount specific configuration options are
-    configured in the <literal>[Mount]</literal> section.</para>
+    configuration items are configured in the generic [Unit] and
+    [Install] sections. The mount specific configuration options are
+    configured in the [Mount] section.</para>
 
     <para>Additional options are listed in
     <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
         <varname>Options=</varname> setting in a unit file.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>x-systemd.rw-only</option></term>
+
+        <listitem><para>If a mount operation fails to mount the file system
+        read-write, it normally tries mounting the file system read-only instead.
+        This option disables that behaviour, and causes the mount to fail
+        immediately instead. This option is translated into the
+        <varname>ReadWriteOnly=</varname> setting in a unit file.</para>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>_netdev</option></term>
 
         created. (See
         <citerefentry><refentrytitle>systemd.device</refentrytitle><manvolnum>5</manvolnum></citerefentry>
         for more information.) This option is mandatory. Note that the usual specifier expansion is applied
-        to this setting, literal percent characters should hence be written as <literal>%%</literal>. If this
-        mount is a bind mount and the specified path does not exist yet it is created as
-        directory.</para></listitem>
+        to this setting, literal percent characters should hence be written as <literal
+        class='specifiers'>%%</literal>. If this mount is a bind mount and the specified path does not exist
+        yet it is created as directory.</para></listitem>
       </varlistentry>
 
       <varlistentry>
 
         <listitem><para>Mount options to use when mounting. This takes a comma-separated list of options. This setting
         is optional. Note that the usual specifier expansion is applied to this setting, literal percent characters
-        should hence be written as <literal>%%</literal>.</para></listitem>
+        should hence be written as <literal class='specifiers'>%%</literal>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         off.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>ReadWriteOnly=</varname></term>
+
+        <listitem><para>Takes a boolean argument. If false, a mount
+        point that shall be mounted read-write but cannot be mounted
+        so is retried to be mounted read-only. If true the operation
+        will fail immediately after the read-write mount attempt did
+        not succeed. This corresponds with
+        <citerefentry project='man-pages'><refentrytitle>mount</refentrytitle><manvolnum>8</manvolnum></citerefentry>'s
+        <parameter>-w</parameter> switch. Defaults to
+        off.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>ForceUnmount=</varname></term>
 
index 4fc85d40ec3892468683d998fc33699f5a13b00f..324c94dbd9fe3a95380916f4bdbc2d72dfb6694c 100644 (file)
       </variablelist>
 
     <para>Note that <constant>latest</constant> may be used to denote the latest scheme known (to this
-    particular version of systemd.</para>
+    particular version of systemd).</para>
   </refsect1>
 
   <refsect1>
index 6ad1dc9e73714598fa1bdec079bbec7d5a9969e2..c2957fd18232053c616585b4bd711ea2c31892e5 100644 (file)
@@ -29,7 +29,7 @@
 
     <para>A plain ini-style text file that encodes configuration about a virtual network device, used by
     <citerefentry><refentrytitle>systemd-networkd</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
-    See <citerefentry><refentrytitle>systemd.syntax</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    See <citerefentry><refentrytitle>systemd.syntax</refentrytitle><manvolnum>7</manvolnum></citerefentry>
     for a general description of the syntax.</para>
 
     <para>The main Virtual Network Device file must have the extension <filename>.netdev</filename>;
           <entry>An IPv4 over IPv4 tunnel.</entry></row>
 
           <row><entry><varname>ipvlan</varname></entry>
-          <entry>An ipvlan device is a stacked device which receives packets from its underlying device based on IP address filtering.</entry></row>
+          <entry>An IPVLAN device is a stacked device which receives packets from its underlying device based on IP address filtering.</entry></row>
 
           <row><entry><varname>ipvtap</varname></entry>
-          <entry>An ipvtap device is a stacked device which receives packets from its underlying device based on IP address filtering and can be accessed using the tap user space interface.</entry></row>
+          <entry>An IPVTAP device is a stacked device which receives packets from its underlying device based on IP address filtering and can be accessed using the tap user space interface.</entry></row>
 
           <row><entry><varname>macvlan</varname></entry>
           <entry>A macvlan device is a stacked device which receives packets from its underlying device based on MAC address filtering.</entry></row>
           <row><entry><varname>wireguard</varname></entry>
           <entry>WireGuard Secure Network Tunnel.</entry></row>
 
-          <row><entry><varname>netdevsim</varname></entry>
-          <entry>A simulator. This simulated networking device is used for testing various networking APIs and at this time is particularly focused on testing hardware offloading related interfaces.</entry></row>
-
           <row><entry><varname>nlmon</varname></entry>
           <entry>A Netlink monitor device. Use an nlmon device when you want to monitor system Netlink messages.</entry></row>
 
     <title>[Match] Section Options</title>
 
     <para>A virtual network device is only created if the
-    <literal>[Match]</literal> section matches the current
+    [Match] section matches the current
     environment, or if the section is empty. The following keys are
     accepted:</para>
 
   <refsect1>
     <title>[NetDev] Section Options</title>
 
-    <para>The <literal>[NetDev]</literal> section accepts the
+    <para>The [NetDev] section accepts the
     following keys:</para>
 
     <variablelist class='network-directives'>
         <term><varname>Name=</varname></term>
         <listitem>
           <para>The interface name used when creating the netdev.
-          This option is compulsory.</para>
+          This setting is compulsory.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>Kind=</varname></term>
         <listitem>
-          <para>The netdev kind. This option is compulsory. See the
+          <para>The netdev kind. This setting is compulsory. See the
           <literal>Supported netdev kinds</literal> section for the
           valid keys.</para>
         </listitem>
       <varlistentry>
         <term><varname>MTUBytes=</varname></term>
         <listitem>
-          <para>The maximum transmission unit in bytes to set for the device. The usual suffixes K, M, G,
+          <para>The maximum transmission unit in bytes to set for the device. The usual suffixes K, M, G
           are supported and are understood to the base of 1024. For <literal>tun</literal> or
           <literal>tap</literal> devices, <varname>MTUBytes=</varname> setting is not currently supported in
-          <literal>[NetDev]</literal> section. Please specify it in <literal>[Link]</literal> section of
+          [NetDev] section. Please specify it in [Link] section of
           corresponding
           <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>
           files.</para>
         <term><varname>MACAddress=</varname></term>
         <listitem>
           <para>The MAC address to use for the device. For <literal>tun</literal> or <literal>tap</literal>
-          devices, setting <varname>MACAddress=</varname> in the <literal>[NetDev]</literal> section is not
-          supported. Please specify it in <literal>[Link]</literal> section of the corresponding
+          devices, setting <varname>MACAddress=</varname> in the [NetDev] section is not
+          supported. Please specify it in [Link] section of the corresponding
           <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>
           file. If this option is not set, <literal>vlan</literal> devices inherit the MAC address of the
           physical interface. For other kind of netdevs, if this option is not set, then MAC address is
   <refsect1>
     <title>[Bridge] Section Options</title>
 
-    <para>The <literal>[Bridge]</literal> section only applies for
+    <para>The [Bridge] section only applies for
     netdevs of kind <literal>bridge</literal>, and accepts the
     following keys:</para>
 
           </para>
         </listitem>
       </varlistentry>
+      <varlistentry>
+        <term><varname>VLANProtocol=</varname></term>
+        <listitem>
+          <para>Allows setting the protocol used for VLAN filtering. Takes
+          <option>802.1q</option> or,
+          <option>802.1ad</option>, and defaults to unset and kernel's default is used.
+          </para>
+        </listitem>
+      </varlistentry>
       <varlistentry>
         <term><varname>STP=</varname></term>
         <listitem>
   <refsect1>
     <title>[VLAN] Section Options</title>
 
-    <para>The <literal>[VLAN]</literal> section only applies for
+    <para>The [VLAN] section only applies for
     netdevs of kind <literal>vlan</literal>, and accepts the
     following key:</para>
 
         <term><varname>Id=</varname></term>
         <listitem>
           <para>The VLAN ID to use. An integer in the range 0–4094.
-          This option is compulsory.</para>
+          This setting is compulsory.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
       <varlistentry>
         <term><varname>ReorderHeader=</varname></term>
         <listitem>
-          <para>Takes a boolean. The VLAN reorder header is set VLAN interfaces behave like physical interfaces.
-          When unset, the kernel's default will be used.</para>
+          <para>Takes a boolean. When enabled, the VLAN reorder header is used and VLAN interfaces behave
+          like physical interfaces. When unset, the kernel's default will be used.</para>
         </listitem>
       </varlistentry>
     </variablelist>
   <refsect1>
     <title>[MACVLAN] Section Options</title>
 
-    <para>The <literal>[MACVLAN]</literal> section only applies for
+    <para>The [MACVLAN] section only applies for
     netdevs of kind <literal>macvlan</literal>, and accepts the
     following key:</para>
 
           <para>The MACVLAN mode to use. The supported options are
           <literal>private</literal>,
           <literal>vepa</literal>,
-          <literal>bridge</literal>, and
-          <literal>passthru</literal>.
+          <literal>bridge</literal>,
+          <literal>passthru</literal>, and
+          <literal>source</literal>.
           </para>
         </listitem>
       </varlistentry>
+      <varlistentry>
+        <term><varname>SourceMACAddress=</varname></term>
+        <listitem>
+          <para>A whitespace-separated list of remote hardware addresses allowed on the MACVLAN. This
+          option only has an effect in source mode. Use full colon-, hyphen- or dot-delimited
+          hexadecimal. This option may appear more than once, in which case the lists are merged. If
+          the empty string is assigned to this option, the list of hardware addresses defined prior
+          to this is reset. Defaults to unset.</para>
+        </listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 
   <refsect1>
     <title>[MACVTAP] Section Options</title>
 
-    <para>The <literal>[MACVTAP]</literal> section applies for
+    <para>The [MACVTAP] section applies for
     netdevs of kind <literal>macvtap</literal> and accepts the
-    same key as <literal>[MACVLAN]</literal>.</para>
+    same key as [MACVLAN].</para>
   </refsect1>
 
   <refsect1>
     <title>[IPVLAN] Section Options</title>
 
-    <para>The <literal>[IPVLAN]</literal> section only applies for
+    <para>The [IPVLAN] section only applies for
     netdevs of kind <literal>ipvlan</literal>, and accepts the
     following key:</para>
 
   <refsect1>
     <title>[IPVTAP] Section Options</title>
 
-    <para>The <literal>[IPVTAP]</literal> section only applies for
+    <para>The [IPVTAP] section only applies for
     netdevs of kind <literal>ipvtap</literal> and accepts the
-    same key as <literal>[IPVLAN]</literal>.</para>
+    same key as [IPVLAN].</para>
   </refsect1>
 
   <refsect1>
     <title>[VXLAN] Section Options</title>
 
-    <para>The <literal>[VXLAN]</literal> section only applies for
+    <para>The [VXLAN] section only applies for
     netdevs of kind <literal>vxlan</literal>, and accepts the
     following keys:</para>
 
       <varlistentry>
         <term><varname>Group=</varname></term>
         <listitem>
-          <para>Configures VXLAN multicast group IP address. All members of a VXLAN must use the same multicast group address.</para>
+          <para>Configures VXLAN multicast group IP address. All members of a VXLAN must use the same
+          multicast group address.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
       <varlistentry>
         <term><varname>L3MissNotification=</varname></term>
         <listitem>
-          <para>Takes a boolean. When true, enables netlink IP address miss
-          notifications.</para>
+          <para>Takes a boolean. When true, enables netlink IP address miss notifications.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
   <refsect1>
     <title>[GENEVE] Section Options</title>
 
-    <para>The <literal>[GENEVE]</literal> section only applies for
+    <para>The [GENEVE] section only applies for
     netdevs of kind <literal>geneve</literal>, and accepts the
     following keys:</para>
 
       <varlistentry>
         <term><varname>TTL=</varname></term>
         <listitem>
-          <para>Accepts the same key in <literal>[VXLAN]</literal> section except when unset or
-          set to 0, the kernel's default will be used meaning that packets TTL will be set from
+          <para>Accepts the same values as in the [VXLAN] section, except that when unset
+          or set to 0, the kernel's default will be used, meaning that packet TTL will be set from
           <filename>/proc/sys/net/ipv4/ip_default_ttl</filename>.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>UDPChecksum=</varname></term>
         <listitem>
-          <para>Takes a boolean. When true, specifies if UDP checksum is calculated for transmitted packets over IPv4.</para>
+          <para>Takes a boolean. When true, specifies that UDP checksum is calculated for transmitted packets
+          over IPv4.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
       <varlistentry>
         <term><varname>IPDoNotFragment=</varname></term>
         <listitem>
-          <para>Accepts the same key in <literal>[VXLAN]</literal> section.</para>
+          <para>Accepts the same key in [VXLAN] section.</para>
         </listitem>
       </varlistentry>
     </variablelist>
   <refsect1>
     <title>[L2TP] Section Options</title>
 
-    <para>The <literal>[L2TP]</literal> section only applies for
+    <para>The [L2TP] section only applies for
     netdevs of kind <literal>l2tp</literal>, and accepts the
     following keys:</para>
 
       <varlistentry>
         <term><varname>TunnelId=</varname></term>
         <listitem>
-          <para>Specifies the tunnel id. The value used must match the <literal>PeerTunnelId=</literal> value being used at the peer.
-          Ranges a number between 1 and 4294967295). This option is compulsory.</para>
+          <para>Specifies the tunnel identifier. Takes an number in the range 1–4294967295. The value used
+          must match the <literal>PeerTunnelId=</literal> value being used at the peer. This setting is
+          compulsory.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>PeerTunnelId=</varname></term>
         <listitem>
-          <para>Specifies the peer tunnel id. The value used must match the <literal>PeerTunnelId=</literal> value being used at the peer.
-          Ranges a number between 1 and 4294967295). This option is compulsory.</para>
+          <para>Specifies the peer tunnel id. Takes a number in the range 1—4294967295. The value used must
+          match the <literal>PeerTunnelId=</literal> value being used at the peer. This setting is
+          compulsory.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>Remote=</varname></term>
         <listitem>
-          <para>Specifies the IP address of the remote peer. This option is compulsory.</para>
+          <para>Specifies the IP address of the remote peer. This setting is compulsory.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
       <varlistentry>
         <term><varname>EncapsulationType=</varname></term>
         <listitem>
-          <para>Specifies the encapsulation type of the tunnel. Takes one of <literal>udp</literal> or <literal>ip</literal>.</para>
+          <para>Specifies the encapsulation type of the tunnel. Takes one of <literal>udp</literal> or
+          <literal>ip</literal>.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>UDPSourcePort=</varname></term>
         <listitem>
-          <para>Specifies the UDP source port to be used for the tunnel. When UDP encapsulation is selected it's mandotory. Ignored when ip
-          encapsulation is selected.</para>
+          <para>Specifies the UDP source port to be used for the tunnel. When UDP encapsulation is selected
+          it's mandatory. Ignored when IP encapsulation is selected.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>UDPDestinationPort=</varname></term>
         <listitem>
-          <para>Specifies destination port. When UDP encapsulation is selected it's mandotory. Ignored when ip
+          <para>Specifies destination port. When UDP encapsulation is selected it's mandatory. Ignored when IP
           encapsulation is selected.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>UDPChecksum=</varname></term>
         <listitem>
-          <para>Takes a boolean. When true, specifies if UDP checksum is calculated for transmitted packets over IPv4.</para>
+          <para>Takes a boolean. When true, specifies that UDP checksum is calculated for transmitted packets
+          over IPv4.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
   <refsect1>
     <title>[L2TPSession] Section Options</title>
 
-    <para>The <literal>[L2TPSession]</literal> section only applies for
+    <para>The [L2TPSession] section only applies for
     netdevs of kind <literal>l2tp</literal>, and accepts the
     following keys:</para>
     <variablelist class='network-directives'>
       <varlistentry>
         <term><varname>Name=</varname></term>
         <listitem>
-          <para>Specifies the name of the session. This option is compulsory.</para>
+          <para>Specifies the name of the session. This setting is compulsory.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>SessionId=</varname></term>
         <listitem>
-          <para>Specifies the session id. The value used must match the <literal>SessionId=</literal> value being used at the peer.
-          Ranges a number between 1 and 4294967295). This option is compulsory.</para>
+          <para>Specifies the session identifier. Takes an number in the range 1–4294967295. The value used
+          must match the <literal>SessionId=</literal> value being used at the peer. This setting is
+          compulsory.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>PeerSessionId=</varname></term>
         <listitem>
-          <para>Specifies the peer session id. The value used must match the <literal>PeerSessionId=</literal> value being used at the peer.
-          Ranges a number between 1 and 4294967295). This option is compulsory.</para>
+          <para>Specifies the peer session identifier. Takes an number in the range 1–4294967295.
+          The value used must match the <literal>PeerSessionId=</literal> value being used at the peer.
+          This setting is compulsory.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
   <refsect1>
     <title>[MACsec] Section Options</title>
 
-    <para>The <literal>[MACsec]</literal> section only applies for network devices of kind
+    <para>The [MACsec] section only applies for network devices of kind
     <literal>macsec</literal>, and accepts the following keys:</para>
 
     <variablelist class='network-directives'>
 
   <refsect1>
     <title>[MACsecReceiveChannel] Section Options</title>
-    <para>The <literal>[MACsecReceiveChannel]</literal> section only applies for network devices of
+    <para>The [MACsecReceiveChannel] section only applies for network devices of
     kind <literal>macsec</literal>, and accepts the following keys:</para>
 
     <variablelist class='network-directives'>
         <term><varname>MACAddress=</varname></term>
         <listitem>
           <para>Specifies the MAC address to be used for the MACsec receive channel. The MAC address
-          used to make secure channel identifier (SCI). This option is compulsory, and is not set by
+          used to make secure channel identifier (SCI). This setting is compulsory, and is not set by
           default.</para>
         </listitem>
       </varlistentry>
   <refsect1>
     <title>[MACsecTransmitAssociation] Section Options</title>
 
-    <para>The <literal>[MACsecTransmitAssociation]</literal> section only applies for network devices
+    <para>The [MACsecTransmitAssociation] section only applies for network devices
     of kind <literal>macsec</literal>, and accepts the following keys:</para>
 
     <variablelist class='network-directives'>
         <term><varname>Key=</varname></term>
         <listitem>
           <para>Specifies the encryption key used in the transmission channel. The same key must be
-          configured on the peer’s matching receive channel. This option is compulsory, and is not set
+          configured on the peer’s matching receive channel. This setting is compulsory, and is not set
           by default. Takes a 128-bit key encoded in a hexadecimal string, for example
           <literal>dffafc8d7b9a43d5b9a3dfbbf6a30c16</literal>.</para>
         </listitem>
       <varlistentry>
         <term><varname>KeyFile=</varname></term>
         <listitem>
-          <para>Takes a absolute path to a file which contains a 128-bit key encoded in a hexadecimal
-          string, which will be used in the transmission channel. When this option is specified,
+          <para>Takes a absolute path to a file which contains a 128-bit key encoded in a hexadecimal string,
+          which will be used in the transmission channel. When this option is specified,
           <varname>Key=</varname> is ignored. Note that the file must be readable by the user
           <literal>systemd-network</literal>, so it should be, e.g., owned by
-          <literal>root:systemd-network</literal> with a <literal>0640</literal> file mode.</para>
+          <literal>root:systemd-network</literal> with a <literal>0640</literal> file mode. If the path
+          refers to an <constant>AF_UNIX</constant> stream socket in the file system a connection is made to
+          it and the key read from it.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>UseForEncoding=</varname></term>
         <listitem>
           <para>Takes a boolean. If enabled, then the security association is used for encoding. Only
-          one <literal>[MACsecTransmitAssociation]</literal> section can enable this option. When enabled,
+          one [MACsecTransmitAssociation] section can enable this option. When enabled,
           <varname>Activate=yes</varname> is implied. Defaults to unset.</para>
         </listitem>
       </varlistentry>
   <refsect1>
     <title>[MACsecReceiveAssociation] Section Options</title>
 
-    <para>The <literal>[MACsecReceiveAssociation]</literal> section only applies for
+    <para>The [MACsecReceiveAssociation] section only applies for
     network devices of kind <literal>macsec</literal>, and accepts the
     following keys:</para>
 
       <varlistentry>
         <term><varname>Port=</varname></term>
         <listitem>
-          <para>Accepts the same key in <literal>[MACsecReceiveChannel]</literal> section.</para>
+          <para>Accepts the same key in [MACsecReceiveChannel] section.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>MACAddress=</varname></term>
         <listitem>
-          <para>Accepts the same key in <literal>[MACsecReceiveChannel]</literal> section.</para>
+          <para>Accepts the same key in [MACsecReceiveChannel] section.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>PacketNumber=</varname></term>
         <listitem>
-          <para>Accepts the same key in <literal>[MACsecTransmitAssociation]</literal> section.</para>
+          <para>Accepts the same key in [MACsecTransmitAssociation] section.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>KeyId=</varname></term>
         <listitem>
-          <para>Accepts the same key in <literal>[MACsecTransmitAssociation]</literal> section.</para>
+          <para>Accepts the same key in [MACsecTransmitAssociation] section.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>Key=</varname></term>
         <listitem>
-          <para>Accepts the same key in <literal>[MACsecTransmitAssociation]</literal> section.</para>
+          <para>Accepts the same key in [MACsecTransmitAssociation] section.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>KeyFile=</varname></term>
         <listitem>
-          <para>Accepts the same key in <literal>[MACsecTransmitAssociation]</literal> section.</para>
+          <para>Accepts the same key in [MACsecTransmitAssociation] section.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>Activate=</varname></term>
         <listitem>
-          <para>Accepts the same key in <literal>[MACsecTransmitAssociation]</literal> section.</para>
+          <para>Accepts the same key in [MACsecTransmitAssociation] section.</para>
         </listitem>
       </varlistentry>
     </variablelist>
   <refsect1>
     <title>[Tunnel] Section Options</title>
 
-    <para>The <literal>[Tunnel]</literal> section only applies for
+    <para>The [Tunnel] section only applies for
     netdevs of kind
     <literal>ipip</literal>,
     <literal>sit</literal>,
           <para>A fixed Time To Live N on tunneled packets. N is a
           number in the range 1–255. 0 is a special value meaning that
           packets inherit the TTL value. The default value for IPv4
-          tunnels is: inherit. The default value for IPv6 tunnels is
+          tunnels is 0 (inherit). The default value for IPv6 tunnels is
           64.</para>
         </listitem>
       </varlistentry>
           both directions (<varname>InputKey=</varname> and <varname>OutputKey=</varname>).
           The <varname>Key=</varname> is either a number or an IPv4 address-like dotted quad.
           It is used as mark-configured SAD/SPD entry as part of the lookup key (both in data
-          and control path) in ip xfrm (framework used to implement IPsec protocol).
+          and control path) in IP XFRM (framework used to implement IPsec protocol).
           See <ulink url="http://man7.org/linux/man-pages/man8/ip-xfrm.8.html">
           ip-xfrm — transform configuration</ulink> for details. It is only used for VTI/VTI6,
           GRE, GRETAP, and ERSPAN tunnels.</para>
       <varlistentry>
         <term><varname>Encapsulation=</varname></term>
         <listitem>
-          <para>Accepts the same key as in the <literal>[FooOverUDP]</literal> section.</para>
+          <para>Accepts the same key as in the [FooOverUDP] section.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
   <refsect1>
     <title>[FooOverUDP] Section Options</title>
 
-    <para>The <literal>[FooOverUDP]</literal> section only applies for
+    <para>The [FooOverUDP] section only applies for
     netdevs of kind <literal>fou</literal> and accepts the
     following keys:</para>
 
       <varlistentry>
         <term><varname>Encapsulation=</varname></term>
         <listitem>
-          <para>Specifies the encapsulation mechanism used to store networking packets of various protocols inside the UDP packets. Supports the following values:
+          <para>Specifies the encapsulation mechanism used to store networking packets of various protocols
+          inside the UDP packets. Supports the following values:
 
-          <literal>FooOverUDP</literal> provides the simplest no frills model of UDP encapsulation, it simply encapsulates
-          packets directly in the UDP payload.
-          <literal>GenericUDPEncapsulation</literal> is a generic and extensible encapsulation, it allows encapsulation of packets for any IP
-          protocol and optional data as part of the encapsulation.
-          For more detailed information see <ulink url="https://lwn.net/Articles/615044">Generic UDP Encapsulation</ulink>.
-          Defaults to <literal>FooOverUDP</literal>.
+          <literal>FooOverUDP</literal> provides the simplest no frills model of UDP encapsulation, it simply
+          encapsulates packets directly in the UDP payload. <literal>GenericUDPEncapsulation</literal> is a
+          generic and extensible encapsulation, it allows encapsulation of packets for any IP protocol and
+          optional data as part of the encapsulation. For more detailed information see <ulink
+          url="https://lwn.net/Articles/615044">Generic UDP Encapsulation</ulink>. Defaults to
+          <literal>FooOverUDP</literal>.
           </para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>Port=</varname></term>
         <listitem>
-          <para>Specifies the port number, where the IP encapsulation packets will arrive. Please take note that the packets
-          will arrive with the encapsulation will be removed. Then they will be manually fed back into the network stack, and sent ahead
-          for delivery to the real destination. This option is mandatory.</para>
+          <para>Specifies the port number, where the IP encapsulation packets will arrive. Please take note
+          that the packets will arrive with the encapsulation will be removed. Then they will be manually fed
+          back into the network stack, and sent ahead for delivery to the real destination. This option is
+          mandatory.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>PeerPort=</varname></term>
         <listitem>
-          <para>Specifies the peer port number. Defaults to unset. Note that when peer port is set <literal>Peer=</literal> address is mandotory.</para>
+          <para>Specifies the peer port number. Defaults to unset. Note that when peer port is set
+          <literal>Peer=</literal> address is mandatory.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
       <varlistentry>
         <term><varname>Peer=</varname></term>
         <listitem>
-          <para>Configures peer IP address. Note that when peer address is set <literal>PeerPort=</literal> is mandotory.</para>
+          <para>Configures peer IP address. Note that when peer address is set <literal>PeerPort=</literal>
+          is mandatory.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
   <refsect1>
     <title>[Peer] Section Options</title>
 
-    <para>The <literal>[Peer]</literal> section only applies for
+    <para>The [Peer] section only applies for
     netdevs of kind <literal>veth</literal> and accepts the
     following keys:</para>
 
         <term><varname>Name=</varname></term>
         <listitem>
           <para>The interface name used when creating the netdev.
-          This option is compulsory.</para>
+          This setting is compulsory.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
   <refsect1>
     <title>[VXCAN] Section Options</title>
 
-    <para>The <literal>[VXCAN]</literal> section only applies for
+    <para>The [VXCAN] section only applies for
     netdevs of kind <literal>vxcan</literal> and accepts the
     following key:</para>
 
         <term><varname>Peer=</varname></term>
         <listitem>
           <para>The peer interface name used when creating the netdev.
-          This option is compulsory.</para>
+          This setting is compulsory.</para>
         </listitem>
       </varlistentry>
     </variablelist>
   <refsect1>
     <title>[Tun] Section Options</title>
 
-    <para>The <literal>[Tun]</literal> section only applies for
+    <para>The [Tun] section only applies for
     netdevs of kind <literal>tun</literal>, and accepts the following
     keys:</para>
 
   <refsect1>
     <title>[Tap] Section Options</title>
 
-    <para>The <literal>[Tap]</literal> section only applies for
+    <para>The [Tap] section only applies for
     netdevs of kind <literal>tap</literal>, and accepts the same keys
-    as the <literal>[Tun]</literal> section.</para>
+    as the [Tun] section.</para>
   </refsect1>
 
   <refsect1>
     <title>[WireGuard] Section Options</title>
 
-    <para>The <literal>[WireGuard]</literal> section accepts the following
+    <para>The [WireGuard] section accepts the following
     keys:</para>
 
     <variablelist class='network-directives'>
       <varlistentry>
         <term><varname>PrivateKeyFile=</varname></term>
         <listitem>
-          <para>Takes an absolute path to a file which contains the Base64 encoded private key for the interface.
-          When this option is specified, then <varname>PrivateKey=</varname> is ignored.
-          Note that the file must be readable by the user <literal>systemd-network</literal>, so it
-          should be, e.g., owned by <literal>root:systemd-network</literal> with a
-          <literal>0640</literal> file mode.</para>
+          <para>Takes an absolute path to a file which contains the Base64 encoded private key for the
+          interface.  When this option is specified, then <varname>PrivateKey=</varname> is ignored.  Note
+          that the file must be readable by the user <literal>systemd-network</literal>, so it should be,
+          e.g., owned by <literal>root:systemd-network</literal> with a <literal>0640</literal> file mode. If
+          the path refers to an <constant>AF_UNIX</constant> stream socket in the file system a connection is
+          made to it and the key read from it.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
   <refsect1>
     <title>[WireGuardPeer] Section Options</title>
 
-    <para>The <literal>[WireGuardPeer]</literal> section accepts the following
+    <para>The [WireGuardPeer] section accepts the following
     keys:</para>
 
     <variablelist class='network-directives'>
           already existing public-key cryptography, for post-quantum
           resistance.
           Note that because this information is secret, you may want to set
-          the permissions of the .netdev file to be owned by <literal>root:systemd-networkd</literal>
+          the permissions of the .netdev file to be owned by <literal>root:systemd-network</literal>
           with a <literal>0640</literal> file mode.</para>
         </listitem>
       </varlistentry>
         <term><varname>PresharedKeyFile=</varname></term>
         <listitem>
           <para>Takes an absolute path to a file which contains the Base64 encoded preshared key for the
-          peer. When this option is specified, then <varname>PresharedKey=</varname> is ignored.
-          Note that the file must be readable by the user <literal>systemd-network</literal>, so it
-          should be, e.g., owned by <literal>root:systemd-network</literal> with a
-          <literal>0640</literal> file mode.</para>
+          peer. When this option is specified, then <varname>PresharedKey=</varname> is ignored.  Note that
+          the file must be readable by the user <literal>systemd-network</literal>, so it should be, e.g.,
+          owned by <literal>root:systemd-network</literal> with a <literal>0640</literal> file mode. If the
+          path refers to an <constant>AF_UNIX</constant> stream socket in the file system a connection is
+          made to it and the key read from it.</para>
         </listitem>
       </varlistentry>
       <varlistentry>
   <refsect1>
     <title>[Bond] Section Options</title>
 
-    <para>The <literal>[Bond]</literal> section accepts the following
+    <para>The [Bond] section accepts the following
     key:</para>
 
     <variablelist class='network-directives'>
       <varlistentry>
         <term><varname>AdActorSystemPriority=</varname></term>
         <listitem>
-          <para>Specifies the 802.3ad actor system priority. Ranges [1-65535].</para>
+          <para>Specifies the 802.3ad actor system priority. Takes a number in the range 1—65535.</para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
         <term><varname>AdUserPortKey=</varname></term>
         <listitem>
-          <para>Specifies the 802.3ad user defined portion of the port key. Ranges [0-1023].</para>
+          <para>Specifies the 802.3ad user defined portion of the port key. Takes a number in the range
+          0–1023.</para>
         </listitem>
       </varlistentry>
 
   <refsect1>
     <title>[Xfrm] Section Options</title>
 
-    <para>The <literal>[Xfrm]</literal> section accepts the following
+    <para>The [Xfrm] section accepts the following
     keys:</para>
 
     <variablelist class='network-directives'>
     </variablelist>
 
     <para>For more detail information see
-      <ulink url="https://lwn.net/Articles/757391">
-        Virtual xfrm interfaces</ulink></para>
+      <ulink url="https://lwn.net/Articles/757391">Virtual XFRM Interfaces</ulink>.</para>
   </refsect1>
 
   <refsect1>
     <title>[VRF] Section Options</title>
-    <para>The <literal>[VRF]</literal> section only applies for
+    <para>The [VRF] section only applies for
     netdevs of kind <literal>vrf</literal> and accepts the
     following key:</para>
 
       <varlistentry>
         <term><varname>Table=</varname></term>
         <listitem>
-          <para>The numeric routing table identifier. This option is compulsory.</para>
+          <para>The numeric routing table identifier. This setting is compulsory.</para>
         </listitem>
       </varlistentry>
     </variablelist>
index bd19b766c37361eb87c88c00c9976d0a9492f3bd..77986192d4bce846f5bc222f45683e00eeb3c13f 100644 (file)
@@ -31,7 +31,7 @@
     <para>A plain ini-style text file that encodes network configuration for matching network interfaces,
     used by
     <citerefentry><refentrytitle>systemd-networkd</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
-    See <citerefentry><refentrytitle>systemd.syntax</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    See <citerefentry><refentrytitle>systemd.syntax</refentrytitle><manvolnum>7</manvolnum></citerefentry>
     for a general description of the syntax.</para>
 
     <para>The main network file must have the extension <filename>.network</filename>; other
   <refsect1>
     <title>[Match] Section Options</title>
 
-      <para>The network file contains a <literal>[Match]</literal>
-      section, which determines if a given network file may be applied
-      to a given device; and a <literal>[Network]</literal> section
-      specifying how the device should be configured. The first (in
-      lexical order) of the network files that matches a given device
-      is applied, all later files are ignored, even if they match as
-      well.</para>
-
-      <para>A network file is said to match a network interface if all matches specified by the
-      <literal>[Match]</literal> section are satisfied. When a network file does not contain valid
-      settings in <literal>[Match]</literal> section, then the file will match all interfaces and
-      <command>systemd-networkd</command> warns about that. Hint: to avoid the warning and to make it
-      clear that all interfaces shall be matched, add the following:
-      <programlisting>Name=*</programlisting>
-      The following keys are accepted:</para>
+      <para>The network file contains a [Match] section, which determines if a given network file may be
+      applied to a given device; and a [Network] section specifying how the device should be configured. The
+      first (in lexical order) of the network files that matches a given device is applied, all later files
+      are ignored, even if they match as well.</para>
+
+      <para>A network file is said to match a network interface if all matches specified by the [Match]
+      section are satisfied. When a network file does not contain valid settings in [Match] section, then the
+      file will match all interfaces and <command>systemd-networkd</command> warns about that. Hint: to avoid
+      the warning and to make it clear that all interfaces shall be matched, add the following:
+      <programlisting>Name=*</programlisting> The following keys are accepted:</para>
 
       <variablelist class='network-directives'>
         <xi:include href="systemd.link.xml" xpointer="mac-address" />
           <listitem>
             <para>A whitespace-separated list of hardware address of the currently connected wireless
             LAN. Use full colon-, hyphen- or dot-delimited hexadecimal. See the example in
-            <varname>MACAddress=</varname>. This option may appear more than one, in which case the
-            lists are merged. If the empty string is assigned to this option, the list of BSSID defined
-            prior to this is reset.</para>
+            <varname>MACAddress=</varname>. This option may appear more than once, in which case the
+            lists are merged. If the empty string is assigned to this option, the list is reset.</para>
           </listitem>
         </varlistentry>
 
   <refsect1>
     <title>[Link] Section Options</title>
 
-    <para> The <literal>[Link]</literal> section accepts the following keys:</para>
+    <para> The [Link] section accepts the following keys:</para>
 
     <variablelist class='network-directives'>
       <varlistentry>
           controlled by other applications.</para>
         </listitem>
       </varlistentry>
+      <varlistentry>
+        <term><varname>Group=</varname></term>
+        <listitem>
+          <para>Link groups are similar to port ranges found in managed switches.
+          When network interfaces are added to a numbered group, operations on
+          all the interfaces from that group can be performed at once. An unsigned
+          integer in the range 0—4294967294. Defaults to unset.</para>
+        </listitem>
+      </varlistentry>
       <varlistentry>
         <term><varname>RequiredForOnline=</varname></term>
         <listitem>
     </variablelist>
   </refsect1>
 
+  <refsect1>
+      <title>[SR-IOV] Section Options</title>
+      <para>The [SR-IOV] section accepts the following keys. Specify several [SR-IOV] sections to configure
+      several SR-IOVs. SR-IOV provides the ability to partition a single physical PCI resource into virtual
+      PCI functions which can then be injected into a VM. In the case of network VFs, SR-IOV improves
+      north-south network performance (that is, traffic with endpoints outside the host machine) by allowing
+      traffic to bypass the host machine’s network stack.</para>
+
+      <variablelist class='network-directives'>
+        <varlistentry>
+          <term><varname>VirtualFunction=</varname></term>
+          <listitem>
+            <para>Specifies a Virtual Function (VF), lightweight PCIe function designed solely to move data
+            in and out. Takes an unsigned integer in the range 0..2147483646. This option is compulsory.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><varname>VLANId=</varname></term>
+          <listitem>
+            <para>Specifies VLAN ID of the virtual function. Takes an unsigned integer in the range 1..4095.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><varname>QualityOfService=</varname></term>
+          <listitem>
+            <para>Specifies quality of service of the virtual function. Takes an unsigned integer in the range 1..4294967294.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><varname>VLANProtocol=</varname></term>
+          <listitem>
+            <para>Specifies VLAN protocol of the virtual function. Takes <literal>802.1Q</literal> or
+            <literal>802.1ad</literal>.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><varname>MACSpoofCheck=</varname></term>
+          <listitem>
+            <para>Takes a boolean. Controls the MAC spoof checking. When unset, the kernel's default will be used.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><varname>QueryReceiveSideScaling=</varname></term>
+          <listitem>
+            <para>Takes a boolean. Toggle the ability of querying the receive side scaling (RSS)
+            configuration of the virtual function (VF). The VF RSS information like RSS hash key may be
+            considered sensitive on some devices where this information is shared between VF and the
+            physical function (PF). When unset, the kernel's default will be used.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><varname>Trust=</varname></term>
+          <listitem>
+            <para>Takes a boolean. Allows to set trust mode of the virtual function (VF). When set, VF
+            users can set a specific feature which may impact security and/or performance. When unset,
+            the kernel's default will be used.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><varname>LinkState=</varname></term>
+          <listitem>
+            <para>Allows to set the link state of the virtual function (VF). Takes a boolean or a
+            special value <literal>auto</literal>. Setting to <literal>auto</literal> means a
+            reflection of the physical function (PF) link state, <literal>yes</literal> lets the VF to
+            communicate with other VFs on this host even if the PF link state is down,
+            <literal>no</literal> causes the hardware to drop any packets sent by the VF. When unset,
+            the kernel's default will be used.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><varname>MACAddress=</varname></term>
+          <listitem>
+            <para>Specifies the MAC address for the virtual function.</para>
+          </listitem>
+        </varlistentry>
+      </variablelist>
+  </refsect1>
+
   <refsect1>
     <title>[Network] Section Options</title>
 
-      <para>The <literal>[Network]</literal> section accepts the following keys:</para>
+      <para>The [Network] section accepts the following keys:</para>
 
       <variablelist class='network-directives'>
         <varlistentry>
             specified through DHCP is not used for name resolution.
             See option <option>UseDomains=</option> below.</para>
 
-            <para>See the <literal>[DHCPv4]</literal> or <literal>[DHCPv6]</literal> section below for
-            further configuration options for the DHCP client support.</para>
+            <para>See the [DHCPv4] or [DHCPv6] sections below for further configuration options for the DHCP
+            client support.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
           <term><varname>DHCPServer=</varname></term>
           <listitem>
             <para>Takes a boolean. If set to <literal>yes</literal>, DHCPv4 server will be started. Defaults
-            to <literal>no</literal>. Further settings for the DHCP
-            server may be set in the <literal>[DHCPServer]</literal>
+            to <literal>no</literal>. Further settings for the DHCP server may be set in the [DHCPServer]
             section described below.</para>
           </listitem>
         </varlistentry>
             </para>
           </listitem>
         </varlistentry>
+        <varlistentry>
+          <term><varname>IPv6LinkLocalAddressGenerationMode=</varname></term>
+          <listitem>
+            <para>Specifies how IPv6 link local address is generated. Takes one of <literal>eui64</literal>,
+            <literal>none</literal>, <literal>stable-privacy</literal> and <literal>random</literal>.
+            When unset, the kernel's default will be used. Note that if <varname>LinkLocalAdressing=</varname>
+            not configured as <literal>ipv6</literal> then <varname>IPv6LinkLocalAddressGenerationMode=</varname>
+            is ignored.</para>
+          </listitem>
+        </varlistentry>
         <varlistentry>
           <term><varname>IPv4LLRoute=</varname></term>
           <listitem>
         <varlistentry>
           <term><varname>DNSSEC=</varname></term>
           <listitem>
-            <para>Takes a boolean. or
-            <literal>allow-downgrade</literal>. When true, enables
-            <ulink
-            url="https://tools.ietf.org/html/rfc4033">DNSSEC</ulink>
+            <para>Takes a boolean or <literal>allow-downgrade</literal>. When true, enables
+            <ulink url="https://tools.ietf.org/html/rfc4033">DNSSEC</ulink>
             DNS validation support on the link. When set to
             <literal>allow-downgrade</literal>, compatibility with
             non-DNSSEC capable networks is increased, by automatically
             <literal>nearest-bridge</literal>, <literal>non-tpmr-bridge</literal> and
             <literal>customer-bridge</literal>.  Defaults to false, which turns off LLDP packet emission. If not false,
             a short LLDP packet with information about the local system is sent out in regular intervals on the
-            link. The LLDP packet will contain information about the local host name, the local machine ID (as stored
+            link. The LLDP packet will contain information about the local hostname, the local machine ID (as stored
             in <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>) and the
             local interface name, as well as the pretty hostname of the system (as set in
             <citerefentry><refentrytitle>machine-info</refentrytitle><manvolnum>5</manvolnum></citerefentry>). LLDP
             reception.</para>
           </listitem>
         </varlistentry>
+
         <varlistentry>
           <term><varname>BindCarrier=</varname></term>
           <listitem>
             <para>A DNS server address, which must be in the format
             described in
             <citerefentry project='man-pages'><refentrytitle>inet_pton</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
-            This option may be specified more than once. This setting is read by
+            This option may be specified more than once. Each address can optionally take a port number
+            separated with <literal>:</literal>, a network interface name or index separated with
+            <literal>%</literal>, and a Server Name Indication (SNI) separated with <literal>#</literal>.
+            When IPv6 address is specified with a port number, then the address must be in the square
+            brackets. That is, the acceptable full formats are
+            <literal>111.222.333.444:9953%ifname#example.com</literal> for IPv4 and
+            <literal>[1111:2222::3333]:9953%ifname#example.com</literal> for IPv6. This setting can be
+            specified multiple times. If an empty string is assigned, then the all previous assignments
+            are cleared. This setting is read by
             <citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
           </listitem>
         </varlistentry>
             this link. Each item in the list should be a domain name, optionally prefixed with a tilde
             (<literal>~</literal>). The domains with the prefix are called "routing-only domains". The
             domains without the prefix are called "search domains" and are first used as search suffixes for
-            extending single-label host names (host names containing no dots) to become fully qualified
-            domain names (FQDNs). If a single-label host name is resolved on this interface, each of the
+            extending single-label hostnames (hostnames containing no dots) to become fully qualified
+            domain names (FQDNs). If a single-label hostname is resolved on this interface, each of the
             specified search domains are appended to it in turn, converting it into a fully qualified domain
             name, until one of them may be successfully resolved.</para>
 
-            <para>Both "search" and "routing-only" domains are used for routing of DNS queries: look-ups for host names
+            <para>Both "search" and "routing-only" domains are used for routing of DNS queries: look-ups for hostnames
             ending in those domains (hence also single label names, if any "search domains" are listed), are routed to
             the DNS servers configured for this interface. The domain routing logic is particularly useful on
             multi-homed hosts with DNS servers serving particular private DNS zones on each interface.</para>
         <varlistentry>
           <term><varname>NTP=</varname></term>
           <listitem>
-            <para>An NTP server address. This option may be specified more than once. This setting is read by
+            <para>An NTP server address (either an IP address, or a hostname). This option may be specified more than once. This setting is read by
             <citerefentry><refentrytitle>systemd-timesyncd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
           </listitem>
         </varlistentry>
           trigger the start of the DHCPv6 client if the relevant flags are set in the RA data, or if no
           routers are found on the link. The default is to disable RA reception for bridge devices or when IP
           forwarding is enabled, and to enable it otherwise. Cannot be enabled on bond devices and when link
-          local adressing is disabled.</para>
+          local addressing is disabled.</para>
 
-          <para>Further settings for the IPv6 RA support may be configured in the
-          <literal>[IPv6AcceptRA]</literal> section, see below.</para>
+          <para>Further settings for the IPv6 RA support may be configured in the [IPv6AcceptRA] section, see
+          below.</para>
 
           <para>Also see <ulink
           url="https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt">ip-sysctl.txt</ulink> in the kernel
           When unset, the kernel's default will be used.
         </para></listitem>
         </varlistentry>
+        <varlistentry>
+          <term><varname>IPv4AcceptLocal=</varname></term>
+          <listitem><para>Takes a boolean. Accept packets with local source addresses. In combination
+          with suitable routing, this can be used to direct packets between two local interfaces over
+          the wire and have them accepted properly. When unset, the kernel's default will be used.
+          </para></listitem>
+        </varlistentry>
         <varlistentry>
           <term><varname>IPv4ProxyARP=</varname></term>
           <listitem><para>Takes a boolean. Configures proxy ARP for IPv4. Proxy ARP is the technique in which one host,
           usually a router, answers ARP requests intended for another machine. By "faking" its identity,
-          the router accepts responsibility for routing packets to the "real" destination. (see <ulink
+          the router accepts responsibility for routing packets to the "real" destination. See <ulink
           url="https://tools.ietf.org/html/rfc1027">RFC 1027</ulink>.
           When unset, the kernel's default will be used.
         </para></listitem>
         </varlistentry>
         <varlistentry>
           <term><varname>IPv6PrefixDelegation=</varname></term>
-          <listitem><para>Whether to enable or disable Router Advertisement sending on a link.
-          Allowed values are <literal>static</literal> which distributes prefixes as defined in
-          the <literal>[IPv6PrefixDelegation]</literal> and any <literal>[IPv6Prefix]</literal>
-          sections, <literal>dhcpv6</literal> which requests prefixes using a DHCPv6 client
-          configured for another link and any values configured in the
-          <literal>[IPv6PrefixDelegation]</literal> section while ignoring all static prefix
-          configuration sections, <literal>yes</literal> which uses both static configuration
-          and DHCPv6, and <literal>false</literal> which turns off IPv6 prefix delegation
-          altogether. Defaults to <literal>false</literal>. See the
-          <literal>[IPv6PrefixDelegation]</literal> and the <literal>[IPv6Prefix]</literal>
-          sections for more configuration options.
-          </para></listitem>
+          <listitem><para>Whether to enable or disable Router Advertisement sending on a link.  Allowed
+          values are <literal>static</literal> which distributes prefixes as defined in the
+          [IPv6PrefixDelegation] and any [IPv6Prefix] sections, <literal>dhcpv6</literal> which requests
+          prefixes using a DHCPv6 client configured for another link and any values configured in the
+          [IPv6PrefixDelegation] section while ignoring all static prefix configuration sections,
+          <literal>yes</literal> which uses both static configuration and DHCPv6, and
+          <literal>false</literal> which turns off IPv6 prefix delegation altogether. Defaults to
+          <literal>false</literal>. See the [IPv6PrefixDelegation] and the [IPv6Prefix] sections for more
+          configuration options.</para></listitem>
         </varlistentry>
         <varlistentry>
           <term><varname>IPv6MTUBytes=</varname></term>
         <term><varname>ConfigureWithoutCarrier=</varname></term>
         <listitem>
           <para>Takes a boolean. Allows networkd to configure a specific link even if it has no carrier.
-          Defaults to false.
+          Defaults to false. If <option>IgnoreCarrierLoss=</option> is not explicitly set, it will
+          default to this value.
           </para>
         </listitem>
       </varlistentry>
       <varlistentry>
         <term><varname>IgnoreCarrierLoss=</varname></term>
         <listitem>
-          <para>A boolean. Allows networkd to retain both the static and dynamic configuration of the
-          interface even if its carrier is lost. Defaults to false.
+          <para>Takes a boolean. Allows networkd to retain both the static and dynamic configuration
+          of the interface even if its carrier is lost. When unset, the value specified with
+          <option>ConfigureWithoutCarrier=</option> is used.
           </para>
         </listitem>
       </varlistentry>
   <refsect1>
     <title>[Address] Section Options</title>
 
-      <para>An <literal>[Address]</literal> section accepts the
-      following keys. Specify several <literal>[Address]</literal>
+      <para>An [Address] section accepts the following keys. Specify several [Address]
       sections to configure several addresses.</para>
 
       <variablelist class='network-directives'>
         <varlistentry>
           <term><varname>Address=</varname></term>
           <listitem>
-            <para>As in the <literal>[Network]</literal> section. This key is mandatory. Each
-            <literal>[Address]</literal> section can contain one <varname>Address=</varname> setting.</para>
+            <para>As in the [Network] section. This key is mandatory. Each [Address] section can contain one
+            <varname>Address=</varname> setting.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
           <term><varname>Scope=</varname></term>
           <listitem>
             <para>The scope of the address, which can be <literal>global</literal>,
-            <literal>link</literal> or <literal>host</literal> or an unsigned integer ranges 0 to 255.
+            <literal>link</literal> or <literal>host</literal> or an unsigned integer in the range 0—255.
             Defaults to <literal>global</literal>.</para>
           </listitem>
         </varlistentry>
 
   <refsect1>
     <title>[Neighbor] Section Options</title>
-      <para>A <literal>[Neighbor]</literal> section accepts the
-      following keys. The neighbor section adds a permanent, static
-      entry to the neighbor table (IPv6) or ARP table (IPv4) for
-      the given hardware address on the links matched for the network.
-      Specify several <literal>[Neighbor]</literal> sections to configure
-      several static neighbors.</para>
+      <para>A [Neighbor] section accepts the following keys. The neighbor section adds a permanent, static
+      entry to the neighbor table (IPv6) or ARP table (IPv4) for the given hardware address on the links
+      matched for the network. Specify several [Neighbor] sections to configure several static neighbors.
+      </para>
 
       <variablelist class='network-directives'>
         <varlistentry>
     <refsect1>
     <title>[IPv6AddressLabel] Section Options</title>
 
-      <para>An <literal>[IPv6AddressLabel]</literal> section accepts the
-      following keys. Specify several <literal>[IPv6AddressLabel]</literal>
-      sections to configure several address labels. IPv6 address labels are
-      used for address selection. See <ulink url="https://tools.ietf.org/html/rfc3484">RFC 3484</ulink>.
-      Precedence is managed by userspace, and only the label itself is stored in the kernel</para>
+      <para>An [IPv6AddressLabel] section accepts the following keys. Specify several [IPv6AddressLabel]
+      sections to configure several address labels. IPv6 address labels are used for address selection. See
+      <ulink url="https://tools.ietf.org/html/rfc3484">RFC 3484</ulink>. Precedence is managed by userspace,
+      and only the label itself is stored in the kernel</para>
 
       <variablelist class='network-directives'>
         <varlistentry>
           <term><varname>Label=</varname></term>
           <listitem>
-            <para> The label for the prefix (an unsigned integer) ranges 0 to 4294967294.
-            0xffffffff is reserved. This key is mandatory.</para>
+            <para>The label for the prefix, an unsigned integer in the range 0–4294967294.
+            0xffffffff is reserved. This setting is mandatory.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
  <refsect1>
     <title>[RoutingPolicyRule] Section Options</title>
 
-      <para>An <literal>[RoutingPolicyRule]</literal> section accepts the
-      following keys. Specify several <literal>[RoutingPolicyRule]</literal>
+      <para>An [RoutingPolicyRule] section accepts the following keys. Specify several [RoutingPolicyRule]
       sections to configure several rules.</para>
 
       <variablelist class='network-directives'>
         <varlistentry>
           <term><varname>TypeOfService=</varname></term>
           <listitem>
-            <para>Specifies the type of service to match a number between 0 to 255.</para>
+            <para>Takes a number between 0 and 255 that specifies the type of service to match.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
 
     <refsect1>
       <title>[NextHop] Section Options</title>
-      <para>The <literal>[NextHop]</literal> section accepts the
-      following keys. Specify several <literal>[NextHop]</literal>
-      sections to configure several nexthop. Nexthop is used to manipulate entries in the kernel's nexthop
-      tables.</para>
+      <para>The [NextHop] section is used to manipulate entries in the kernel's "nexthop" tables.  The
+      [NextHop] section accepts the following keys. Specify several [NextHop] sections to configure several
+      hops.</para>
 
       <variablelist class='network-directives'>
         <varlistentry>
           <term><varname>Gateway=</varname></term>
           <listitem>
-            <para>As in the <literal>[Network]</literal> section. This is mandatory.</para>
+            <para>As in the [Network] section. This is mandatory.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
 
   <refsect1>
     <title>[Route] Section Options</title>
-      <para>The <literal>[Route]</literal> section accepts the
-      following keys. Specify several <literal>[Route]</literal>
-      sections to configure several routes.</para>
+      <para>The [Route] section accepts the following keys. Specify several [Route] sections to configure
+      several routes.</para>
 
       <variablelist class='network-directives'>
         <varlistentry>
           <term><varname>IPv6Preference=</varname></term>
           <listitem>
             <para>Specifies the route preference as defined in <ulink
-            url="https://tools.ietf.org/html/rfc4191">RFC4191</ulink> for Router Discovery messages.
-            Which can be one of <literal>low</literal> the route has a lowest priority,
-            <literal>medium</literal> the route has a default priority or
-            <literal>high</literal> the route has a highest priority.</para>
+            url="https://tools.ietf.org/html/rfc4191">RFC 4191</ulink> for Router Discovery messages.  Which
+            can be one of <literal>low</literal> the route has a lowest priority, <literal>medium</literal>
+            the route has a default priority or <literal>high</literal> the route has a highest priority.
+            </para>
           </listitem>
         </varlistentry>
         <varlistentry>
 
   <refsect1>
     <title>[DHCPv4] Section Options</title>
-      <para>The <literal>[DHCPv4]</literal> section configures the
-      DHCPv4 client, if it is enabled with the
+      <para>The [DHCPv4] section configures the DHCPv4 client, if it is enabled with the
       <varname>DHCP=</varname> setting described above:</para>
 
       <variablelist class='network-directives'>
         <varlistentry>
           <term><varname>UseNTP=</varname></term>
           <listitem>
-            <para>When true (the default), the NTP servers received
-            from the DHCP server will be used by systemd-timesyncd
-            and take precedence over any statically configured ones.</para>
+            <para>When true (the default), the NTP servers received from the DHCP server will be used by
+            <filename>systemd-timesyncd.service</filename> and take precedence over any statically configured
+            ones.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
           <term><varname>UseSIP=</varname></term>
           <listitem>
-            <para>When true (the default), the SIP servers received
-            from the DHCP server will be saved at the state files and can be
-            read via <function>sd_network_link_get_sip_servers()</function> function.</para>
+            <para>When true (the default), the SIP servers received from the DHCP server will be collected
+            and made available to client programs.</para>
           </listitem>
         </varlistentry>
+
         <varlistentry>
           <term><varname>UseMTU=</varname></term>
           <listitem>
             sent even if this is set to true.</para>
           </listitem>
         </varlistentry>
+
+        <varlistentry>
+          <term><varname>MUDURL=</varname></term>
+          <listitem>
+            <para>When configured, the Manufacturer Usage Descriptions (MUD) URL will be sent to the
+            DHCPv4 server. Takes an URL of length up to 255 characters. A superficial verification that
+            the string is a valid URL will be performed. DHCPv4 clients are intended to have at most one
+            MUD URL associated with them. See
+            <ulink url="https://tools.ietf.org/html/rfc8520">RFC 8520</ulink>.</para>
+          </listitem>
+        </varlistentry>
+
         <varlistentry>
           <term><varname>UseHostname=</varname></term>
           <listitem>
             false.</para>
 
             <para>It is recommended to enable this option only on trusted networks, as setting this affects resolution
-            of all host names, in particular of single-label names. It is generally safer to use the supplied domain
+            of all hostnames, in particular of single-label names. It is generally safer to use the supplied domain
             only as routing domain, rather than as search domain, in order to not have it affect local resolution of
             single-label names.</para>
 
               "link" scope will be used. For anything else, scope defaults to "global".</para>
           </listitem>
         </varlistentry>
-
+        <varlistentry>
+          <term><varname>UseGateway=</varname></term>
+          <listitem>
+            <para>When true, the gateway will be requested from the DHCP server and added to the routing table with a
+            metric of 1024, and a scope of "link".  When unset, the value specified with <option>UseRoutes=</option>
+            is used.</para>
+          </listitem>
+        </varlistentry>
         <varlistentry>
           <term><varname>UseTimezone=</varname></term>
 
         <varlistentry>
           <term><varname>RouteMetric=</varname></term>
           <listitem>
-            <para>Set the routing metric for routes specified by the
-            DHCP server.</para>
+            <para>Set the routing metric for routes specified by the DHCP server. Defaults to 1024.</para>
           </listitem>
         </varlistentry>
 
             <para>The table identifier for DHCP routes (a number between 1 and 4294967295, or 0 to unset).
             The table can be retrieved using <command>ip route show table <replaceable>num</replaceable></command>.
             </para>
-            <para>When used in combination with <varname>VRF=</varname> the
-            VRF's routing table is used unless this parameter is specified.
+            <para>When used in combination with <varname>VRF=</varname>, the
+            VRF's routing table is used when this parameter is not specified.
             </para>
           </listitem>
         </varlistentry>
           </listitem>
         </varlistentry>
 
+         <varlistentry>
+          <term><varname>FallbackLeaseLifetimeSec=</varname></term>
+          <listitem>
+            <para>Allows to set DHCPv4 lease lifetime when DHCPv4 server does not send the lease lifetime.
+            Takes one of <literal>forever</literal> or <literal>infinity</literal> means that the address
+            never expires. Defaults to unset.</para>
+          </listitem>
+        </varlistentry>
+
         <varlistentry>
           <term><varname>SendRelease=</varname></term>
           <listitem>
         <varlistentry>
           <term><varname>SendDecline=</varname></term>
           <listitem>
-            <para>A boolen. When <literal>true</literal>, DHCPv4 clients receives IP address from DHCP server.
-            After new IP is received, DHCPv4 performs IPv4 Duplicate Address Detection. If duplicate use of IP is detected
-            the DHCPv4 client rejects the IP by sending a DHCPDECLINE packet DHCP clients try to obtain an IP address again.
-            See <ulink url="https://tools.ietf.org/html/rfc5227">RFC 5224</ulink>.
-            Defaults to <literal>unset</literal>.</para>
+            <para>A boolean. When <literal>true</literal>, the DHCPv4 client receives the IP address from the
+            DHCP server.  After a new IP is received, the DHCPv4 client performs IPv4 Duplicate Address
+            Detection. If duplicate use is detected, the DHCPv4 client rejects the IP by sending a
+            DHCPDECLINE packet and tries to obtain an IP address again. See <ulink
+            url="https://tools.ietf.org/html/rfc5227">RFC 5224</ulink>. Defaults to
+            <literal>unset</literal>.</para>
           </listitem>
         </varlistentry>
 
         <varlistentry>
-        <term><varname>BlackList=</varname></term>
+        <term><varname>DenyList=</varname></term>
+        <listitem>
+          <para>A whitespace-separated list of IPv4 addresses. DHCP offers from servers in the list are rejected. Note that
+          if <varname>AllowList=</varname> is configured then <varname>DenyList=</varname> is ignored.</para>
+        </listitem>
+        </varlistentry>
+
+        <varlistentry>
+        <term><varname>AllowList=</varname></term>
         <listitem>
-          <para>A whitespace-separated list of IPv4 addresses. DHCP offers from servers in the list are rejected.</para>
+          <para>A whitespace-separated list of IPv4 addresses. DHCP offers from servers in the list are accepted.</para>
         </listitem>
         </varlistentry>
 
         <varlistentry>
           <term><varname>RequestOptions=</varname></term>
           <listitem>
-            <para>A whitespace-separated list of integers in the range 1–254.</para>
+            <para>When configured, allows to set arbitrary request options in the DHCPv4 request options list and will be
+            sent to the DHCPV4 server. A whitespace-separated list of integers in the range 1..254. Defaults to unset.</para>
           </listitem>
         </varlistentry>
 
         <varlistentry>
           <term><varname>SendOption=</varname></term>
           <listitem>
-            <para>Send an arbitrary option in the DHCPv4 request. Takes a DHCP option number, data type
+            <para>Send an arbitrary raw option in the DHCPv4 request. Takes a DHCP option number, data type
+            and data separated with a colon
+            (<literal><replaceable>option</replaceable>:<replaceable>type</replaceable>:<replaceable>value</replaceable></literal>).
+            The option number must be an integer in the range 1..254. The type takes one of <literal>uint8</literal>,
+            <literal>uint16</literal>, <literal>uint32</literal>, <literal>ipv4address</literal>, or
+            <literal>string</literal>. Special characters in the data string may be escaped using
+            <ulink url="https://en.wikipedia.org/wiki/Escape_sequences_in_C#Table_of_escape_sequences">C-style
+            escapes</ulink>. This setting can be specified multiple times. If an empty string is specified,
+            then all options specified earlier are cleared. Defaults to unset.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><varname>SendVendorOption=</varname></term>
+          <listitem>
+            <para>Send an arbitrary vendor option in the DHCPv4 request. Takes a DHCP option number, data type
             and data separated with a colon
             (<literal><replaceable>option</replaceable>:<replaceable>type</replaceable>:<replaceable>value</replaceable></literal>).
             The option number must be an integer in the range 1..254. The type takes one of <literal>uint8</literal>,
 
   <refsect1>
     <title>[DHCPv6] Section Options</title>
-      <para>The <literal>[DHCPv6]</literal> section configures the DHCPv6 client, if it is enabled with the
+      <para>The [DHCPv6] section configures the DHCPv6 client, if it is enabled with the
       <varname>DHCP=</varname> setting described above, or invoked by the IPv6 Router Advertisement:</para>
 
       <variablelist class='network-directives'>
           <term><varname>UseDNS=</varname></term>
           <term><varname>UseNTP=</varname></term>
           <listitem>
-            <para>As in the <literal>[DHCPv4]</literal> section.</para>
+            <para>As in the [DHCPv4] section.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><varname>RouteMetric=</varname></term>
+          <listitem>
+            <para>Set the routing metric for routes specified by the DHCP server. Defaults to 1024.</para>
           </listitem>
         </varlistentry>
 
             <para>Takes a boolean. The DHCPv6 client can obtain configuration parameters from a DHCPv6 server through
             a rapid two-message exchange (solicit and reply). When the rapid commit option is enabled by both
             the DHCPv6 client and the DHCPv6 server, the two-message exchange is used, rather than the default
-            four-method exchange (solicit, advertise, request, and reply). The two-message exchange provides
+            four-message exchange (solicit, advertise, request, and reply). The two-message exchange provides
             faster client configuration and is beneficial in environments in which networks are under a heavy load.
             See <ulink url="https://tools.ietf.org/html/rfc3315#section-17.2.1">RFC 3315</ulink> for details.
             Defaults to true.</para>
           </listitem>
         </varlistentry>
 
+        <varlistentry>
+          <term><varname>MUDURL=</varname></term>
+          <listitem>
+            <para>When configured, the Manufacturer Usage Descriptions (MUD) URL will be sent to the DHCPV6 server.
+            Takes an URL of length up to 255 characters. A superficial verification that the string is a valid URL
+            will be performed. DHCPv6 clients are intended to have at most one MUD URL associated with them. See
+            <ulink url="https://tools.ietf.org/html/rfc8520">RFC 8520</ulink>.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><varname>RequestOptions=</varname></term>
+          <listitem>
+            <para>When configured, allows to set arbitrary request options in the DHCPv6 request options list and will
+            sent to the DHCPV6 server. A whitespace-separated list of integers in the range 1..254. Defaults to unset.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><varname>SendVendorOption=</varname></term>
+          <listitem>
+            <para>Send an arbitrary vendor option in the DHCPv6 request. Takes an enterprise identifier, DHCP
+            option number, data type, and data separated with a colon (<literal><replaceable>enterprise
+            identifier</replaceable>:<replaceable>option</replaceable>:<replaceable>type</replaceable>:
+            <replaceable>value</replaceable></literal>). Enterprise identifier is an unsigned integer in the
+            range 1–4294967294. The option number must be an integer in the range 1–254. Data type takes one
+            of <literal>uint8</literal>, <literal>uint16</literal>, <literal>uint32</literal>,
+            <literal>ipv4address</literal>, <literal>ipv6address</literal>, or
+            <literal>string</literal>. Special characters in the data string may be escaped using <ulink
+            url="https://en.wikipedia.org/wiki/Escape_sequences_in_C#Table_of_escape_sequences">C-style
+            escapes</ulink>. This setting can be specified multiple times. If an empty string is specified,
+            then all options specified earlier are cleared. Defaults to unset.</para>
+          </listitem>
+        </varlistentry>
+
         <varlistentry>
           <term><varname>ForceDHCPv6PDOtherInformation=</varname></term>
           <listitem>
         <varlistentry>
           <term><varname>PrefixDelegationHint=</varname></term>
           <listitem>
-            <para>Takes an IPv6 address with prefix length as <varname>Address=</varname> in
-            the "[Network]" section. Specifies the DHCPv6 client for the requesting router to include
-            a prefix-hint in the DHCPv6 solicitation. Prefix ranges 1-128. Defaults to unset.</para>
+            <para>Takes an IPv6 address with prefix length in the same format as the
+            <varname>Address=</varname> in the [Network] section. The DHCPv6 client will include a prefix
+            hint in the DHCPv6 solicitation sent to the server. The prefix length must be in the range
+            1–128. Defaults to unset.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><varname>WithoutRA=</varname></term>
+          <listitem>
+            <para>Allows DHCPv6 client to start without router advertisements's managed or other address
+            configuration flag. Takes one of <literal>solicit</literal> or
+            <literal>information-request</literal>. Defaults to unset.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><varname>SendOption=</varname></term>
+          <listitem>
+            <para>As in the [DHCPv4] section, however because DHCPv6 uses 16-bit fields to store
+            option numbers, the option number is an integer in the range 1..65536.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><varname>UserClass=</varname></term>
+          <listitem>
+            <para>A DHCPv6 client can use User Class option to identify the type or category of user or applications
+            it represents. The information contained in this option is a string that represents the user class of which
+            the client is a member. Each class sets an identifying string of information to be used by the DHCP
+            service to classify clients. Special characters in the data string may be escaped using
+            <ulink url="https://en.wikipedia.org/wiki/Escape_sequences_in_C#Table_of_escape_sequences">C-style
+            escapes</ulink>. This setting can be specified multiple times. If an empty string is specified,
+            then all options specified earlier are cleared. Takes a whitespace-separated list of strings. Note that
+            currently NUL bytes are not allowed.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><varname>VendorClass=</varname></term>
+          <listitem>
+            <para>A DHCPv6 client can use VendorClass option to identify the vendor that
+            manufactured the hardware on which the client is running. The information
+            contained in the data area of this option is contained in one or more opaque
+            fields that identify details of the hardware configuration. Takes a
+            whitespace-separated list of strings.</para>
           </listitem>
         </varlistentry>
       </variablelist>
   </refsect1>
 
+  <refsect1>
+    <title>[DHCPv6PrefixDelegation] Section Options</title>
+    <para>The [DHCPv6PrefixDelegation] section configures delegated prefix assigned by DHCPv6 server.
+    The settings in this section are used only when <varname>IPv6PrefixDelegation=</varname> setting is
+    enabled, or set to <literal>dhcp6</literal>.</para>
+
+    <variablelist class='network-directives'>
+      <varlistentry>
+        <term><varname>SubnetId=</varname></term>
+        <listitem>
+          <para>Configure a specific subnet ID on the interface from a (previously) received prefix
+          delegation. You can either set "auto" (the default) or a specific subnet ID (as defined in
+          <ulink url="https://tools.ietf.org/html/rfc4291#section-2.5.4">RFC 4291</ulink>, section
+          2.5.4), in which case the allowed value is hexadecimal, from 0 to 0x7fffffffffffffff
+          inclusive. This option is only effective when used together with
+          <varname>IPv6PrefixDelegation=</varname> and the corresponding configuration on the upstream
+          interface.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>Assign=</varname></term>
+        <listitem>
+          <para>Takes a boolean. Specifies whether to add an address from the delegated prefixes which
+          are received from the WAN interface by the <varname>IPv6PrefixDelegation=</varname>. When
+          true (on LAN interfce), the EUI-64 algorithm will be used to form an interface identifier
+          from the delegated prefixes. Defaults to true.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>Token=</varname></term>
+        <listitem>
+          <para>Specifies an optional address generation mode for <varname>Assign=</varname>. Takes an
+          IPv6 address. When set, the lower bits of the supplied address are combined with the upper
+          bits of a delegatad prefix received from the WAN interface by the
+          <varname>IPv6PrefixDelegation=</varname> prefixes to form a complete address.</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
   <refsect1>
     <title>[IPv6AcceptRA] Section Options</title>
-      <para>The <literal>[IPv6AcceptRA]</literal> section configures the IPv6 Router Advertisement
-      (RA) client, if it is enabled with the <varname>IPv6AcceptRA=</varname> setting described
-      above:</para>
+      <para>The [IPv6AcceptRA] section configures the IPv6 Router Advertisement (RA) client, if it is enabled
+      with the <varname>IPv6AcceptRA=</varname> setting described above:</para>
 
       <variablelist class='network-directives'>
         <varlistentry>
             <literal>~</literal>. Defaults to false.</para>
 
             <para>It is recommended to enable this option only on trusted networks, as setting this affects resolution
-            of all host names, in particular of single-label names. It is generally safer to use the supplied domain
+            of all hostnames, in particular of single-label names. It is generally safer to use the supplied domain
             only as routing domain, rather than as search domain, in order to not have it affect local resolution of
             single-label names.</para>
 
         </varlistentry>
 
         <varlistentry>
-          <term><varname>BlackList=</varname></term>
+          <term><varname>DenyList=</varname></term>
           <listitem>
             <para>A whitespace-separated list of IPv6 prefixes. IPv6 prefixes supplied via router advertisements in the list are ignored.</para>
           </listitem>
         </varlistentry>
 
+        <varlistentry>
+          <term><varname>DHCPv6Client=</varname></term>
+          <listitem>
+            <para>Takes a boolean, or the special value <literal>always</literal>. When true (the default), the DHCPv6 client will be started when the
+            RA has the managed or other information flag. If set to <literal>always</literal>, the DHCPv6 client will be started even if there is no
+            managed or other information flag in the RA.</para>
+          </listitem>
+        </varlistentry>
       </variablelist>
   </refsect1>
 
   <refsect1>
     <title>[DHCPServer] Section Options</title>
-    <para>The <literal>[DHCPServer]</literal> section contains
-    settings for the DHCP server, if enabled via the
+    <para>The [DHCPServer] section contains settings for the DHCP server, if enabled via the
     <varname>DHCPServer=</varname> option described above:</para>
 
     <variablelist class='network-directives'>
         <term><varname>EmitDNS=</varname></term>
         <term><varname>DNS=</varname></term>
 
-        <listitem><para>Takes a boolean. Configures whether the DHCP leases handed out
-        to clients shall contain DNS server information. Defaults to <literal>yes</literal>.
-        The DNS servers to pass to clients may be configured with the
-        <varname>DNS=</varname> option, which takes a list of IPv4
-        addresses. If the <varname>EmitDNS=</varname> option is
-        enabled but no servers configured, the servers are
-        automatically propagated from an "uplink" interface that has
-        appropriate servers set. The "uplink" interface is determined
-        by the default route of the system with the highest
-        priority. Note that this information is acquired at the time
-        the lease is handed out, and does not take uplink interfaces
-        into account that acquire DNS or NTP server information at a
-        later point. DNS server propagation does not take
-        <filename>/etc/resolv.conf</filename> into account. Also, note
-        that the leases are not refreshed if the uplink network
-        configuration changes. To ensure clients regularly acquire the
-        most current uplink DNS server information, it is thus
-        advisable to shorten the DHCP lease time via
-        <varname>MaxLeaseTimeSec=</varname> described
+        <listitem><para><varname>EmitDNS=</varname> takes a boolean. Configures whether the DHCP leases
+        handed out to clients shall contain DNS server information. Defaults to <literal>yes</literal>.  The
+        DNS servers to pass to clients may be configured with the <varname>DNS=</varname> option, which takes
+        a list of IPv4 addresses. If the <varname>EmitDNS=</varname> option is enabled but no servers
+        configured, the servers are automatically propagated from an "uplink" interface that has appropriate
+        servers set. The "uplink" interface is determined by the default route of the system with the highest
+        priority. Note that this information is acquired at the time the lease is handed out, and does not
+        take uplink interfaces into account that acquire DNS server information at a later point. If no
+        suitable uplinkg interface is found the DNS server data from <filename>/etc/resolv.conf</filename> is
+        used. Also, note that the leases are not refreshed if the uplink network configuration changes. To
+        ensure clients regularly acquire the most current uplink DNS server information, it is thus advisable
+        to shorten the DHCP lease time via <varname>MaxLeaseTimeSec=</varname> described
         above.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><varname>EmitNTP=</varname></term>
         <term><varname>NTP=</varname></term>
-
-        <listitem><para>Similar to the <varname>EmitDNS=</varname> and
-        <varname>DNS=</varname> settings described above, these
-        settings configure whether and what NTP server information
-        shall be emitted as part of the DHCP lease. The same syntax,
-        propagation semantics and defaults apply as for
-        <varname>EmitDNS=</varname> and
-        <varname>DNS=</varname>.</para></listitem>
-      </varlistentry>
-
-      <varlistentry>
         <term><varname>EmitSIP=</varname></term>
         <term><varname>SIP=</varname></term>
+        <term><varname>EmitPOP3=</varname></term>
+        <term><varname>POP3=</varname></term>
+        <term><varname>EmitSMTP=</varname></term>
+        <term><varname>SMTP=</varname></term>
+        <term><varname>EmitLPR=</varname></term>
+        <term><varname>LPR=</varname></term>
 
-        <listitem><para>Similar to the <varname>EmitDNS=</varname> and
-        <varname>DNS=</varname> settings described above, these
-        settings configure whether and what SIP server information
-        shall be emitted as part of the DHCP lease. The same syntax,
-        propagation semantics and defaults apply as for
-        <varname>EmitDNS=</varname> and
-        <varname>DNS=</varname>.</para></listitem>
+        <listitem><para>Similar to the <varname>EmitDNS=</varname> and <varname>DNS=</varname> settings
+        described above, these settings configure whether and what server information for the indicate
+        protocol shall be emitted as part of the DHCP lease. The same syntax, propagation semantics and
+        defaults apply as for <varname>EmitDNS=</varname> and <varname>DNS=</varname>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
           <para>Send a raw option with value via DHCPv4 server. Takes a DHCP option number, data type
           and data (<literal><replaceable>option</replaceable>:<replaceable>type</replaceable>:<replaceable>value</replaceable></literal>).
           The option number is an integer in the range 1..254. The type takes one of <literal>uint8</literal>,
+          <literal>uint16</literal>, <literal>uint32</literal>, <literal>ipv4address</literal>, <literal>ipv6address</literal>, or
+          <literal>string</literal>. Special characters in the data string may be escaped using
+          <ulink url="https://en.wikipedia.org/wiki/Escape_sequences_in_C#Table_of_escape_sequences">C-style
+          escapes</ulink>. This setting can be specified multiple times. If an empty string is specified,
+          then all options specified earlier are cleared. Defaults to unset.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>SendVendorOption=</varname></term>
+        <listitem>
+          <para>Send a vendor option with value via DHCPv4 server. Takes a DHCP option number, data type
+          and data (<literal><replaceable>option</replaceable>:<replaceable>type</replaceable>:<replaceable>value</replaceable></literal>).
+          The option number is an integer in the range 1..254. The type takes one of <literal>uint8</literal>,
           <literal>uint16</literal>, <literal>uint32</literal>, <literal>ipv4address</literal>, or
           <literal>string</literal>. Special characters in the data string may be escaped using
           <ulink url="https://en.wikipedia.org/wiki/Escape_sequences_in_C#Table_of_escape_sequences">C-style
 
   <refsect1>
     <title>[IPv6PrefixDelegation] Section Options</title>
-    <para>The <literal>[IPv6PrefixDelegation]</literal> section contains
-    settings for sending IPv6 Router Advertisements and whether to act as
-    a router, if enabled via the <varname>IPv6PrefixDelegation=</varname>
-    option described above. IPv6 network prefixes are defined with one or
-    more <literal>[IPv6Prefix]</literal> sections.</para>
+    <para>The [IPv6PrefixDelegation] section contains settings for sending IPv6 Router Advertisements and
+    whether to act as a router, if enabled via the <varname>IPv6PrefixDelegation=</varname> option described
+    above. IPv6 network prefixes are defined with one or more [IPv6Prefix] sections.</para>
 
     <variablelist class='network-directives'>
 
         <term><varname>EmitDNS=</varname></term>
         <term><varname>DNS=</varname></term>
 
-        <listitem><para><varname>DNS=</varname> specifies a list of recursive DNS server IPv6 addresses
-        that are distributed via Router Advertisement messages when <varname>EmitDNS=</varname> is
-        true. <varname>DNS=</varname> also takes special value <literal>_link_local</literal>; in that
-        case the IPv6 link local address is distributed. If <varname>DNS=</varname> is empty, DNS
-        servers are read from the <literal>[Network]</literal> section. If the
-        <literal>[Network]</literal> section does not contain any DNS servers either, DNS servers from
-        the uplink with the highest priority default route are used. When <varname>EmitDNS=</varname>
-        is false, no DNS server information is sent in Router Advertisement messages.
-        <varname>EmitDNS=</varname> defaults to true.
-        </para></listitem>
+        <listitem><para><varname>DNS=</varname> specifies a list of recursive DNS server IPv6 addresses that
+        are distributed via Router Advertisement messages when <varname>EmitDNS=</varname> is
+        true. <varname>DNS=</varname> also takes special value <literal>_link_local</literal>; in that case
+        the IPv6 link local address is distributed. If <varname>DNS=</varname> is empty, DNS servers are read
+        from the [Network] section. If the [Network] section does not contain any DNS servers either, DNS
+        servers from the uplink with the highest priority default route are used. When
+        <varname>EmitDNS=</varname> is false, no DNS server information is sent in Router Advertisement
+        messages. <varname>EmitDNS=</varname> defaults to true.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><varname>EmitDomains=</varname></term>
         <term><varname>Domains=</varname></term>
 
-        <listitem><para>A list of DNS search domains distributed via Router
-        Advertisement messages when <varname>EmitDomains=</varname> is true. If
-        <varname>Domains=</varname> is empty, DNS search domains are read from the
-        <literal>[Network]</literal> section. If the <literal>[Network]</literal>
-        section does not contain any DNS search domains either, DNS search
-        domains from the uplink with the highest priority default route are
-        used. When <varname>EmitDomains=</varname> is false, no DNS search domain
-        information is sent in Router Advertisement messages.
-        <varname>EmitDomains=</varname> defaults to true.
-        </para></listitem>
+        <listitem><para>A list of DNS search domains distributed via Router Advertisement messages when
+        <varname>EmitDomains=</varname> is true. If <varname>Domains=</varname> is empty, DNS search domains
+        are read from the [Network] section. If the [Network] section does not contain any DNS search domains
+        either, DNS search domains from the uplink with the highest priority default route are used. When
+        <varname>EmitDomains=</varname> is false, no DNS search domain information is sent in Router
+        Advertisement messages. <varname>EmitDomains=</varname> defaults to true.</para></listitem>
       </varlistentry>
 
       <varlistentry>
 
     <refsect1>
     <title>[IPv6Prefix] Section Options</title>
-    <para>One or more <literal>[IPv6Prefix]</literal> sections contain the IPv6
-    prefixes that are announced via Router Advertisements. See
-    <ulink url="https://tools.ietf.org/html/rfc4861">RFC 4861</ulink>
-    for further details.</para>
+    <para>One or more [IPv6Prefix] sections contain the IPv6 prefixes that are announced via Router
+    Advertisements. See <ulink url="https://tools.ietf.org/html/rfc4861">RFC 4861</ulink> for further
+    details.</para>
 
     <variablelist class='network-directives'>
 
       <varlistentry>
         <term><varname>Prefix=</varname></term>
 
-        <listitem><para>The IPv6 prefix that is to be distributed to hosts.
-        Similarly to configuring static IPv6 addresses, the setting is
-        configured as an IPv6 prefix and its prefix length, separated by a
-        <literal>/</literal> character. Use multiple
-        <literal>[IPv6Prefix]</literal> sections to configure multiple IPv6
-        prefixes since prefix lifetimes, address autoconfiguration and onlink
-        status may differ from one prefix to another.</para></listitem>
+        <listitem><para>The IPv6 prefix that is to be distributed to hosts.  Similarly to configuring static
+        IPv6 addresses, the setting is configured as an IPv6 prefix and its prefix length, separated by a
+        <literal>/</literal> character. Use multiple [IPv6Prefix] sections to configure multiple IPv6
+        prefixes since prefix lifetimes, address autoconfiguration and onlink status may differ from one
+        prefix to another.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         to 2592000 seconds (30 days).</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>Assign=</varname></term>
+        <listitem><para>Takes a boolean. When true, adds an address from the prefix. Default to false.
+        </para></listitem>
+      </varlistentry>
     </variablelist>
     </refsect1>
 
     <refsect1>
     <title>[IPv6RoutePrefix] Section Options</title>
-    <para>One or more <literal>[IPv6RoutePrefix]</literal> sections contain the IPv6
+    <para>One or more [IPv6RoutePrefix] sections contain the IPv6
     prefix routes that are announced via Router Advertisements. See
     <ulink url="https://tools.ietf.org/html/rfc4191">RFC 4191</ulink>
     for further details.</para>
       <varlistentry>
         <term><varname>Route=</varname></term>
 
-        <listitem><para>The IPv6 route that is to be distributed to hosts.
-        Similarly to configuring static IPv6 routes, the setting is
-        configured as an IPv6 prefix routes and its prefix route length,
-        separated by a<literal>/</literal> character. Use multiple
-        <literal>[IPv6PrefixRoutes]</literal> sections to configure multiple IPv6
-        prefix routes.</para></listitem>
+        <listitem><para>The IPv6 route that is to be distributed to hosts.  Similarly to configuring static
+        IPv6 routes, the setting is configured as an IPv6 prefix routes and its prefix route length,
+        separated by a <literal>/</literal> character. Use multiple [IPv6PrefixRoutes] sections to configure
+        multiple IPv6 prefix routes.</para></listitem>
       </varlistentry>
 
       <varlistentry>
 
     <refsect1>
     <title>[Bridge] Section Options</title>
-      <para>The <literal>[Bridge]</literal> section accepts the
-      following keys.</para>
+      <para>The [Bridge] section accepts the following keys:</para>
       <variablelist class='network-directives'>
         <varlistentry>
           <term><varname>UnicastFlood=</varname></term>
         <varlistentry>
           <term><varname>HairPin=</varname></term>
           <listitem>
-            <para>Takes a boolean. Configures whether traffic may be sent back
-            out of the port on which it was received. When this flag is false, and the bridge
-            will not forward traffic back out of the receiving port.
-            When unset, the kernel's default will be used.</para>
+            <para>Takes a boolean. Configures whether traffic may be sent back out of the port on which it
+            was received. When this flag is false, then the bridge will not forward traffic back out of the
+            receiving port. When unset, the kernel's default will be used.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
   </refsect1>
   <refsect1>
     <title>[BridgeFDB] Section Options</title>
-      <para>The <literal>[BridgeFDB]</literal> section manages the
-      forwarding database table of a port and accepts the following
-      keys. Specify several <literal>[BridgeFDB]</literal> sections to
-      configure several static MAC table entries.</para>
+      <para>The [BridgeFDB] section manages the forwarding database table of a port and accepts the following
+      keys. Specify several [BridgeFDB] sections to configure several static MAC table entries.</para>
 
       <variablelist class='network-directives'>
         <varlistentry>
           <term><varname>MACAddress=</varname></term>
           <listitem>
-            <para>As in the <literal>[Network]</literal> section. This
-            key is mandatory.</para>
+            <para>As in the [Network] section. This key is mandatory.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
       </variablelist>
   </refsect1>
 
+  <refsect1>
+    <title>[LLDP] Section Options</title>
+      <para>The [LLDP] section manages the Link Layer Discovery Protocol (LLDP) and accepts the following
+      keys.</para>
+      <variablelist class='network-directives'>
+        <varlistentry>
+          <term><varname>MUDURL=</varname></term>
+          <listitem>
+            <para>Controls support for Ethernet LLDP packet's Manufacturer Usage Description (MUD). MUD is an embedded software
+            standard defined by the IETF that allows IoT Device makers to advertise device specifications, including the intended
+            communication patterns for their device when it connects to the network. The network can then use this intent to author
+            a context-specific access policy, so the device functions only within those parameters. Takes an URL of length up to 255
+            characters. A superficial verification that the string is a valid URL
+            will be performed. See
+            <ulink url="https://tools.ietf.org/html/rfc8520">RFC 8520</ulink> for details. The MUD URL received
+            from the LLDP packets will be saved at the state files and can be read via
+            <function>sd_lldp_neighbor_get_mud_url()</function> function.</para>
+          </listitem>
+        </varlistentry>
+      </variablelist>
+  </refsect1>
+
   <refsect1>
     <title>[CAN] Section Options</title>
-      <para>The <literal>[CAN]</literal> section manages the Controller Area Network (CAN bus) and accepts the
-      following keys.</para>
+      <para>The [CAN] section manages the Controller Area Network (CAN bus) and accepts the
+      following keys:</para>
       <variablelist class='network-directives'>
         <varlistentry>
           <term><varname>BitRate=</varname></term>
           <listitem>
             <para>The bitrate of CAN device in bits per second. The usual SI prefixes (K, M) with the base of 1000 can
-            be used here.</para>
+            be used here. Takes a number in the range 1..4294967295.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
             <literal>87.5%</literal>) or permille (e.g. <literal>875‰</literal>).</para>
           </listitem>
         </varlistentry>
+        <varlistentry>
+          <term><varname>DataBitRate=</varname></term>
+          <term><varname>DataSamplePoint=</varname></term>
+          <listitem>
+            <para>The bitrate and sample point for the data phase, if CAN-FD is used. These settings are
+            analogous to the <varname>BitRate=</varname> and <varname>SamplePoint=</varname> keys.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term><varname>FDMode=</varname></term>
+          <listitem>
+            <para>Takes a boolean. When <literal>yes</literal>, CAN-FD mode is enabled for the interface.
+            Note, that a bitrate and optional sample point should also be set for the CAN-FD data phase using
+            the <varname>DataBitRate=</varname> and <varname>DataSamplePoint=</varname> keys.</para>
+          </listitem>
+        </varlistentry>
+        <varlistentry>
+          <term><varname>FDNonISO=</varname></term>
+          <listitem>
+            <para>Takes a boolean. When <literal>yes</literal>, non-ISO CAN-FD mode is enabled for the
+            interface. When unset, the kernel's default will be used.</para>
+          </listitem>
+        </varlistentry>
         <varlistentry>
           <term><varname>RestartSec=</varname></term>
           <listitem>
             automatic restart off. By default automatic restart is disabled.</para>
           </listitem>
         </varlistentry>
+        <varlistentry>
+          <term><varname>Termination=</varname></term>
+          <listitem>
+            <para>Takes a boolean. When <literal>yes</literal>, the termination resistor will be selected for
+            the bias network. When unset, the kernel's default will be used.</para>
+          </listitem>
+        </varlistentry>
         <varlistentry>
           <term><varname>TripleSampling=</varname></term>
           <listitem>
             the value of a received bit by majority rule. When unset, the kernel's default will be used.</para>
           </listitem>
         </varlistentry>
+        <varlistentry>
+          <term><varname>ListenOnly=</varname></term>
+          <listitem>
+            <para>Takes a boolean. When <literal>yes</literal>, listen-only mode is enabled. When the
+            interface is in listen-only mode, the interface neither transmit CAN frames nor send ACK
+            bit. Listen-only mode is important to debug CAN networks without interfering with the
+            communication or acknowledge the CAN frame. When unset, the kernel's default will be used.
+            </para>
+          </listitem>
+        </varlistentry>
       </variablelist>
   </refsect1>
 
   <refsect1>
     <title>[QDisc] Section Options</title>
-    <para>The <literal>[QDisc]</literal> section manages the traffic control queueing discipline (qdisc).</para>
+    <para>The [QDisc] section manages the traffic control queueing discipline (qdisc).</para>
 
     <variablelist class='network-directives'>
       <varlistentry>
         </listitem>
       </varlistentry>
 
-      <varlistentry>
-        <term><varname>Handle=</varname></term>
-        <listitem>
-          <para>Specifies the major number of unique identifier of the qdisc, known as the handle.
-          Takes a number in hexadecimal ranges 1 to ffff. Defaults to unset.</para>
-        </listitem>
-      </varlistentry>
+      <xi:include href="tc.xml" xpointer="qdisc-handle" />
     </variablelist>
   </refsect1>
 
   <refsect1>
     <title>[NetworkEmulator] Section Options</title>
-    <para>The <literal>[NetworkEmulator]</literal> section manages the queueing discipline (qdisc) of
-    the network emulator. It can be used to configure the kernel packet scheduler and simulate packet
-    delay and loss for UDP or TCP applications, or limit the bandwidth usage of a particular service to
-    simulate internet connections.</para>
+    <para>The [NetworkEmulator] section manages the queueing discipline (qdisc) of the network emulator. It
+    can be used to configure the kernel packet scheduler and simulate packet delay and loss for UDP or TCP
+    applications, or limit the bandwidth usage of a particular service to simulate internet connections.
+    </para>
 
     <variablelist class='network-directives'>
-      <varlistentry>
-        <term><varname>Parent=</varname></term>
-        <listitem>
-          <para>Specifies the parent Queueing Discipline (qdisc). Takes one of <literal>root</literal>,
-          <literal>clsact</literal> or <literal>ingress</literal>. Defaults to <literal>root</literal>.</para>
-        </listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term><varname>Handle=</varname></term>
-        <listitem>
-          <para>Specifies the major number of unique identifier of the qdisc, known as the handle.
-          Takes a number in hexadecimal ranges 1 to ffff. Defaults to unset.</para>
-        </listitem>
-      </varlistentry>
+      <xi:include href="tc.xml" xpointer="qdisc-parent" />
+      <xi:include href="tc.xml" xpointer="qdisc-handle" />
 
       <varlistentry>
         <term><varname>DelaySec=</varname></term>
         <term><varname>PacketLimit=</varname></term>
         <listitem>
           <para>Specifies the maximum number of packets the qdisc may hold queued at a time.
-          An unsigned integer ranges 0 to 4294967294. Defaults to 1000.</para>
+          An unsigned integer in the range 0–4294967294. Defaults to 1000.</para>
         </listitem>
       </varlistentry>
 
 
   <refsect1>
     <title>[TokenBucketFilter] Section Options</title>
-    <para>The <literal>[TokenBucketFilter]</literal> section manages the queueing discipline (qdisc) of
-    token bucket filter (tbf).</para>
+    <para>The [TokenBucketFilter] section manages the queueing discipline (qdisc) of token bucket filter
+    (tbf).</para>
 
     <variablelist class='network-directives'>
-      <varlistentry>
-        <term><varname>Parent=</varname></term>
-        <listitem>
-          <para>Specifies the parent Queueing Discipline (qdisc). Takes one of <literal>root</literal>,
-          <literal>clsact</literal> or <literal>ingress</literal>. Defaults to <literal>root</literal>.</para>
-        </listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term><varname>Handle=</varname></term>
-        <listitem>
-          <para>Specifies the major number of unique identifier of the qdisc, known as the handle.
-          Takes a number in hexadecimal ranges 1 to ffff. Defaults to unset.</para>
-        </listitem>
-      </varlistentry>
+      <xi:include href="tc.xml" xpointer="qdisc-parent" />
+      <xi:include href="tc.xml" xpointer="qdisc-handle" />
 
       <varlistentry>
         <term><varname>LatencySec=</varname></term>
       </varlistentry>
 
       <varlistentry>
-        <term><varname>LimitSize=</varname></term>
+        <term><varname>LimitBytes=</varname></term>
         <listitem>
           <para>Takes the number of bytes that can be queued waiting for tokens to become available.
           When the size is suffixed with K, M, or G, it is parsed as Kilobytes, Megabytes, or Gigabytes,
-          respectively, to the base of 1000. Defaults to unset.</para>
+          respectively, to the base of 1024. Defaults to unset.</para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><varname>Burst=</varname></term>
+        <term><varname>BurstBytes=</varname></term>
         <listitem>
           <para>Specifies the size of the bucket. This is the maximum amount of bytes that tokens
           can be available for instantaneous transfer. When the size is suffixed with K, M, or G, it is
-          parsed as Kilobytes, Megabytes, or Gigabytes, respectively, to the base of 1000. Defaults to
+          parsed as Kilobytes, Megabytes, or Gigabytes, respectively, to the base of 1024. Defaults to
           unset.</para>
         </listitem>
       </varlistentry>
         <listitem>
           <para>The Minimum Packet Unit (MPU) determines the minimal token usage (specified in bytes)
           for a packet. When suffixed with K, M, or G, the specified size is parsed as Kilobytes,
-          Megabytes, or Gigabytes, respectively, to the base of 1000. Defaults to zero.</para>
+          Megabytes, or Gigabytes, respectively, to the base of 1024. Defaults to zero.</para>
         </listitem>
       </varlistentry>
 
         <term><varname>MTUBytes=</varname></term>
         <listitem>
           <para>Specifies the size of the peakrate bucket. When suffixed with K, M, or G, the specified
-          size is parsed as Kilobytes, Megabytes, or Gigabytes, respectively, to the base of 1000.
+          size is parsed as Kilobytes, Megabytes, or Gigabytes, respectively, to the base of 1024.
           Defaults to unset.</para>
         </listitem>
       </varlistentry>
   </refsect1>
 
   <refsect1>
-    <title>[StochasticFairnessQueueing] Section Options</title>
-    <para>The <literal>[StochasticFairnessQueueing]</literal> section manages the queueing discipline
-    (qdisc) of stochastic fairness queueing (sfq).</para>
+    <title>[PIE] Section Options</title>
+    <para>The [PIE] section manages the queueing discipline (qdisc) of Proportional Integral
+    controller-Enhanced (PIE).</para>
 
     <variablelist class='network-directives'>
+      <xi:include href="tc.xml" xpointer="qdisc-parent" />
+      <xi:include href="tc.xml" xpointer="qdisc-handle" />
+
       <varlistentry>
-        <term><varname>Parent=</varname></term>
+        <term><varname>PacketLimit=</varname></term>
         <listitem>
-          <para>Specifies the parent Queueing Discipline (qdisc). Takes one of <literal>root</literal>,
-          <literal>clsact</literal> or <literal>ingress</literal>. Defaults to <literal>root</literal>.</para>
+          <para>Specifies the hard limit on the queue size in number of packets. When this limit is reached, incoming packets are
+          dropped. An unsigned integer in the range 1–4294967294. Defaults to unset and kernel's default is used.</para>
         </listitem>
       </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>[StochasticFairBlue] Section Options</title>
+    <para>The [StochasticFairBlue] section manages the queueing discipline (qdisc) of stochastic fair blue
+    (sfb).</para>
+
+    <variablelist class='network-directives'>
+      <xi:include href="tc.xml" xpointer="qdisc-parent" />
+      <xi:include href="tc.xml" xpointer="qdisc-handle" />
 
       <varlistentry>
-        <term><varname>Handle=</varname></term>
+        <term><varname>PacketLimit=</varname></term>
         <listitem>
-          <para>Specifies the major number of unique identifier of the qdisc, known as the handle.
-          Takes a number in hexadecimal ranges 1 to ffff. Defaults to unset.</para>
+          <para>Specifies the hard limit on the queue size in number of packets. When this limit is reached,
+          incoming packets are dropped. An unsigned integer in the range 0–4294967294. Defaults to unset and
+          kernel's default is used.</para>
         </listitem>
       </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>[StochasticFairnessQueueing] Section Options</title>
+    <para>The [StochasticFairnessQueueing] section manages the queueing discipline (qdisc) of stochastic
+    fairness queueing (sfq).</para>
+
+    <variablelist class='network-directives'>
+      <xi:include href="tc.xml" xpointer="qdisc-parent" />
+      <xi:include href="tc.xml" xpointer="qdisc-handle" />
 
       <varlistentry>
         <term><varname>PerturbPeriodSec=</varname></term>
   </refsect1>
 
   <refsect1>
-    <title>[ControlledDelay] Section Options</title>
-    <para>The <literal>[ControlledDelay]</literal> section manages the queueing discipline (qdisc) of
-    controlled delay (CoDel).</para>
+    <title>[BFIFO] Section Options</title>
+    <para>The [BFIFO] section manages the queueing discipline (qdisc) of Byte limited Packet First In First
+    Out (bfifo).</para>
 
     <variablelist class='network-directives'>
+      <xi:include href="tc.xml" xpointer="qdisc-parent" />
+      <xi:include href="tc.xml" xpointer="qdisc-handle" />
+
       <varlistentry>
-        <term><varname>Parent=</varname></term>
+        <term><varname>LimitBytes=</varname></term>
         <listitem>
-          <para>Specifies the parent Queueing Discipline (qdisc). Takes one of <literal>root</literal>,
-          <literal>clsact</literal> or <literal>ingress</literal>. Defaults to <literal>root</literal>.</para>
+          <para>Specifies the hard limit on the FIFO size in bytes. The size limit (a buffer size) to prevent
+          it from overflowing in case it is unable to dequeue packets as quickly as it receives them. When
+          this limit is reached, incoming packets are dropped. When suffixed with K, M, or G, the specified
+          size is parsed as Kilobytes, Megabytes, or Gigabytes, respectively, to the base of 1024. Defaults
+          to unset and kernel's default is used.</para>
         </listitem>
       </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>[PFIFO] Section Options</title>
+    <para>The [PFIFO] section manages the queueing discipline (qdisc) of Packet First In First Out
+    (pfifo).</para>
+
+    <variablelist class='network-directives'>
+      <xi:include href="tc.xml" xpointer="qdisc-parent" />
+      <xi:include href="tc.xml" xpointer="qdisc-handle" />
 
       <varlistentry>
-        <term><varname>Handle=</varname></term>
+        <term><varname>PacketLimit=</varname></term>
         <listitem>
-          <para>Specifies the major number of unique identifier of the qdisc, known as the handle.
-          Takes a number in hexadecimal ranges 1 to ffff. Defaults to unset.</para>
+          <para>Specifies the hard limit on the FIFO size in number of packets. The size limit (a buffer
+          size) to prevent it from overflowing in case it is unable to dequeue packets as quickly as it
+          receives them. When this limit is reached, incoming packets are dropped. An unsigned integer in the
+          range 0–4294967294. Defaults to unset and kernel's default is used.</para>
         </listitem>
       </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>[PFIFOHeadDrop] Section Options</title>
+    <para>The [PFIFOHeadDrop] section manages the queueing discipline (qdisc) of Packet First In First Out
+    Head Drop (pfifo_head_drop).</para>
+
+    <variablelist class='network-directives'>
+      <xi:include href="tc.xml" xpointer="qdisc-parent" />
+      <xi:include href="tc.xml" xpointer="qdisc-handle" />
 
       <varlistentry>
         <term><varname>PacketLimit=</varname></term>
         <listitem>
-          <para>Specifies the hard limit on the queue size in number of packets. When this limit is reached, incoming packets are
-          dropped. An unsigned integer ranges 0 to 4294967294. Defaults to unset and kernel's default is used.</para>
+          <para>As in [PFIFO] section.</para></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+     <title>[PFIFOFast] Section Options</title>
+     <para>The [PFIFOFast] section manages the queueing discipline (qdisc) of Packet First In First Out Fast
+     (pfifo_fast).</para>
+
+     <variablelist class='network-directives'>
+       <xi:include href="tc.xml" xpointer="qdisc-parent" />
+       <xi:include href="tc.xml" xpointer="qdisc-handle" />
+     </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>[CAKE] Section Options</title>
+    <para>The [CAKE] section manages the queueing discipline (qdisc) of Common Applications Kept Enhanced
+    (CAKE).</para>
+
+    <variablelist class='network-directives'>
+      <xi:include href="tc.xml" xpointer="qdisc-parent" />
+      <xi:include href="tc.xml" xpointer="qdisc-handle" />
+
+      <varlistentry>
+        <term><varname>OverheadBytes=</varname></term>
+        <listitem>
+          <para>Specifies that bytes to be addeded to the size of each packet. Bytes may be negative. Takes
+          an integer in the range from -64 to 256. Defaults to unset and kernel's default is used.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>Bandwidth=</varname></term>
+        <listitem>
+          <para>Specifies the shaper bandwidth. When suffixed with K, M, or G, the specified size is
+          parsed as Kilobits, Megabits, or Gigabits, respectively, to the base of 1000. Defaults to
+          unset and kernel's default is used.</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>[ControlledDelay] Section Options</title>
+    <para>The [ControlledDelay] section manages the queueing discipline (qdisc) of
+    controlled delay (CoDel).</para>
+
+    <variablelist class='network-directives'>
+      <xi:include href="tc.xml" xpointer="qdisc-parent" />
+      <xi:include href="tc.xml" xpointer="qdisc-handle" />
+
+      <varlistentry>
+        <term><varname>PacketLimit=</varname></term>
+        <listitem>
+          <para>Specifies the hard limit on the queue size in number of packets. When this limit is reached,
+          incoming packets are dropped. An unsigned integer in the range 0–4294967294. Defaults to unset and
+          kernel's default is used.</para>
         </listitem>
       </varlistentry>
 
   </refsect1>
 
   <refsect1>
-    <title>[FairQueueingControlledDelay] Section Options</title>
-    <para>The <literal>[FairQueueingControlledDelay]</literal> section manages the queueing discipline
-    (qdisc) of fair queuing controlled delay (FQ-CoDel).</para>
+    <title>[DeficitRoundRobinScheduler] Section Options</title>
+    <para>The [DeficitRoundRobinScheduler] section manages the queueing discipline (qdisc) of Deficit Round
+    Robin Scheduler (DRR).</para>
 
     <variablelist class='network-directives'>
+      <xi:include href="tc.xml" xpointer="qdisc-parent" />
+      <xi:include href="tc.xml" xpointer="qdisc-handle" />
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>[DeficitRoundRobinSchedulerClass] Section Options</title>
+    <para>The [DeficitRoundRobinSchedulerClass] section manages the traffic control class of Deficit Round
+    Robin Scheduler (DRR).</para>
+
+    <variablelist class='network-directives'>
+      <xi:include href="tc.xml" xpointer="tclass-parent" />
+      <xi:include href="tc.xml" xpointer="tclass-classid" />
+
       <varlistentry>
-        <term><varname>Parent=</varname></term>
+        <term><varname>QuantumBytes=</varname></term>
+        <listitem>
+          <para>Specifies the amount of bytes a flow is allowed to dequeue before the scheduler moves
+          to the next class. When suffixed with K, M, or G, the specified size is parsed as Kilobytes,
+          Megabytes, or Gigabytes, respectively, to the base of 1024. Defaults to the MTU of the
+          interface.</para>
+        </listitem>
+      </varlistentry>
+
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>[EnhancedTransmissionSelection] Section Options</title>
+    <para>The [EnhancedTransmissionSelection] section manages the queueing discipline (qdisc) of Enhanced
+    Transmission Selection (ETS).</para>
+
+    <variablelist class='network-directives'>
+      <xi:include href="tc.xml" xpointer="qdisc-parent" />
+      <xi:include href="tc.xml" xpointer="qdisc-handle" />
+
+      <varlistentry>
+        <term><varname>Bands=</varname></term>
+        <listitem>
+          <para>Specifies the number of bands. An unsigned integer in the range 1–16. This value has to be at
+          least large enough to cover the strict bands specified through the <varname>StrictBands=</varname>
+          and bandwidth-sharing bands specified in <varname>QuantumBytes=</varname>.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>StrictBands=</varname></term>
+        <listitem>
+          <para>Specifies the number of bands that should be created in strict mode. An unsigned integer in
+          the range 1–16.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>QuantumBytes=</varname></term>
+        <listitem>
+          <para>Specifies the white-space separated list of quantum used in band-sharing bands. When
+          suffixed with K, M, or G, the specified size is parsed as Kilobytes, Megabytes, or Gigabytes,
+          respectively, to the base of 1024. This setting can be specified multiple times. If an empty
+          string is assigned, then the all previous assignments are cleared.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>PriorityMap=</varname></term>
+        <listitem>
+          <para>The priority map maps the priority of a packet to a band. The argument is a white-space
+          separated list of numbers. The first number indicates which band the packets with priority
+          0 should be put to, the second is for priority 1, and so on. There can be up to 16 numbers in
+          the list. If there are fewer, the default band that traffic with one of the unmentioned
+          priorities goes to is the last one. Each band number must be 0..255. This setting can be
+          specified multiple times. If an empty string is assigned, then the all previous assignments
+          are cleared.</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>[GenericRandomEarlyDetection] Section Options</title>
+    <para>The [GenericRandomEarlyDetection] section manages the queueing discipline (qdisc) of Generic Random
+    Early Detection (GRED).</para>
+
+    <variablelist class='network-directives'>
+      <xi:include href="tc.xml" xpointer="qdisc-parent" />
+      <xi:include href="tc.xml" xpointer="qdisc-handle" />
+
+      <varlistentry>
+        <term><varname>VirtualQueues=</varname></term>
+        <listitem>
+          <para>Specifies the number of virtual queues. Takes a integer in the range 1-16. Defaults to unset and kernel's default is used.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>DefaultVirtualQueue=</varname></term>
         <listitem>
-          <para>Specifies the parent Queueing Discipline (qdisc). Takes one of <literal>root</literal>,
-          <literal>clsact</literal> or <literal>ingress</literal>. Defaults to <literal>root</literal>.</para>
+          <para>Specifies the number of default virtual queue. This must be less than <varname>VirtualQueue=</varname>.
+          Defaults to unset and kernel's default is used.</para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><varname>Handle=</varname></term>
+        <term><varname>GenericRIO=</varname></term>
         <listitem>
-          <para>Specifies the major number of unique identifier of the qdisc, known as the handle.
-          Takes a number in hexadecimal ranges 1 to ffff. Defaults to unset.</para>
+          <para>Takes a boolean. It turns on the RIO-like buffering scheme. Defaults to
+          unset and kernel's default is used.</para>
         </listitem>
       </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>[FairQueueingControlledDelay] Section Options</title>
+    <para>The [FairQueueingControlledDelay] section manages the queueing discipline (qdisc) of fair queuing
+    controlled delay (FQ-CoDel).</para>
+
+    <variablelist class='network-directives'>
+      <xi:include href="tc.xml" xpointer="qdisc-parent" />
+      <xi:include href="tc.xml" xpointer="qdisc-handle" />
 
       <varlistentry>
         <term><varname>PacketLimit=</varname></term>
       </varlistentry>
 
       <varlistentry>
-        <term><varname>MemoryLimit=</varname></term>
+        <term><varname>MemoryLimitBytes=</varname></term>
         <listitem>
           <para>Specifies the limit on the total number of bytes that can be queued in this FQ-CoDel instance.
           When suffixed with K, M, or G, the specified size is parsed as Kilobytes, Megabytes, or Gigabytes,
       </varlistentry>
 
       <varlistentry>
-        <term><varname>Quantum=</varname></term>
+        <term><varname>QuantumBytes=</varname></term>
         <listitem>
-          <para>Specifies the number of bytes used as 'deficit' in the fair queuing algorithmtimespan.
+          <para>Specifies the number of bytes used as the "deficit" in the fair queuing algorithm timespan.
           When suffixed with K, M, or G, the specified size is parsed as Kilobytes, Megabytes, or Gigabytes,
           respectively, to the base of 1024. Defaults to unset and kernel's default is used.</para>
         </listitem>
 
   <refsect1>
     <title>[FairQueueing] Section Options</title>
-    <para>The <literal>[FairQueueing]</literal> section manages the queueing discipline
-    (qdisc) of fair queue traffic policing (FQ).</para>
+    <para>The [FairQueueing] section manages the queueing discipline (qdisc) of fair queue traffic policing
+    (FQ).</para>
 
     <variablelist class='network-directives'>
-      <varlistentry>
-        <term><varname>Parent=</varname></term>
-        <listitem>
-          <para>Specifies the parent Queueing Discipline (qdisc). Takes one of <literal>root</literal>,
-          <literal>clsact</literal> or <literal>ingress</literal>. Defaults to <literal>root</literal>.</para>
-        </listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term><varname>Handle=</varname></term>
-        <listitem>
-          <para>Specifies the major number of unique identifier of the qdisc, known as the handle.
-          Takes a number in hexadecimal ranges 1 to ffff. Defaults to unset.</para>
-        </listitem>
-      </varlistentry>
+      <xi:include href="tc.xml" xpointer="qdisc-parent" />
+      <xi:include href="tc.xml" xpointer="qdisc-handle" />
 
       <varlistentry>
         <term><varname>PacketLimit=</varname></term>
       </varlistentry>
 
       <varlistentry>
-        <term><varname>Quantum=</varname></term>
+        <term><varname>QuantumBytes=</varname></term>
         <listitem>
           <para>Specifies the credit per dequeue RR round, i.e. the amount of bytes a flow is allowed
           to dequeue at once. When suffixed with K, M, or G, the specified size is parsed as Kilobytes,
       </varlistentry>
 
       <varlistentry>
-        <term><varname>InitialQuantum=</varname></term>
+        <term><varname>InitialQuantumBytes=</varname></term>
         <listitem>
           <para>Specifies the initial sending rate credit, i.e. the amount of bytes a new flow is
           allowed to dequeue initially. When suffixed with K, M, or G, the specified size is parsed as
 
   <refsect1>
     <title>[TrivialLinkEqualizer] Section Options</title>
-    <para>The <literal>[TrivialLinkEqualizer]</literal> section manages the queueing discipline (qdisc) of
-    trivial link equalizer (teql).</para>
+    <para>The [TrivialLinkEqualizer] section manages the queueing discipline (qdisc) of trivial link
+    equalizer (teql).</para>
 
     <variablelist class='network-directives'>
+      <xi:include href="tc.xml" xpointer="qdisc-parent" />
+      <xi:include href="tc.xml" xpointer="qdisc-handle" />
+
       <varlistentry>
-        <term><varname>Parent=</varname></term>
+        <term><varname>Id=</varname></term>
         <listitem>
-          <para>Specifies the parent Queueing Discipline (qdisc). Takes one of <literal>root</literal>,
-          <literal>clsact</literal> or <literal>ingress</literal>. Defaults to <literal>root</literal>.</para>
+          <para>Specifies the interface ID <literal>N</literal> of teql. Defaults to <literal>0</literal>.
+          Note that when teql is used, currently, the module <constant>sch_teql</constant> with
+          <constant>max_equalizers=N+1</constant> option must be loaded before
+          <command>systemd-networkd</command> is started.</para>
         </listitem>
       </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>[HierarchyTokenBucket] Section Options</title>
+    <para>The [HierarchyTokenBucket] section manages the queueing discipline (qdisc) of hierarchy token
+    bucket (htb).</para>
+
+    <variablelist class='network-directives'>
+      <xi:include href="tc.xml" xpointer="qdisc-parent" />
+      <xi:include href="tc.xml" xpointer="qdisc-handle" />
 
       <varlistentry>
-        <term><varname>Handle=</varname></term>
+        <term><varname>DefaultClass=</varname></term>
         <listitem>
-          <para>Specifies the major number of unique identifier of the qdisc, known as the handle.
-          Takes a number in hexadecimal ranges 1 to ffff. Defaults to unset.</para>
+          <para>Takes the minor id in hexadecimal of the default class. Unclassified traffic gets sent
+          to the class. Defaults to unset.</para>
         </listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><varname>Id=</varname></term>
+        <term><varname>RateToQuantum=</varname></term>
         <listitem>
-          <para>Specifies the interface ID <literal>N</literal> of teql. Defaults to <literal>0</literal>.
-          Note that when teql is used, currently, the module <constant>sch_teql</constant> with
-          <constant>max_equalizers=N+1</constant> option must be loaded before
-          <command>systemd-networkd</command> is started.</para>
+          <para>Takes an unsigned integer. The DRR quantums are calculated by dividing the value
+          configured in <varname>Rate=</varname> by <varname>RateToQuantum=</varname>.</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>[HierarchyTokenBucketClass] Section Options</title>
+    <para>The [HierarchyTokenBucketClass] section manages the traffic control class of hierarchy token bucket
+    (htb).</para>
+
+    <variablelist class='network-directives'>
+      <xi:include href="tc.xml" xpointer="tclass-parent" />
+      <xi:include href="tc.xml" xpointer="tclass-classid" />
+
+      <varlistentry>
+        <term><varname>Priority=</varname></term>
+        <listitem>
+          <para>Specifies the priority of the class. In the round-robin process, classes with the lowest
+          priority field are tried for packets first.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>QuantumBytes=</varname></term>
+        <listitem>
+          <para>Specifies how many bytes to serve from leaf at once. When suffixed with K, M, or G, the
+          specified size is parsed as Kilobytes, Megabytes, or Gigabytes, respectively, to the base of
+          1024.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>MTUBytes=</varname></term>
+        <listitem>
+          <para>Specifies the maximum packet size we create. When suffixed with K, M, or G, the specified
+          size is parsed as Kilobytes, Megabytes, or Gigabytes, respectively, to the base of 1024.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>OverheadBytes=</varname></term>
+        <listitem>
+          <para>Takes an unsigned integer which specifies per-packet size overhead used in rate
+          computations. When suffixed with K, M, or G, the specified size is parsed as Kilobytes,
+          Megabytes, or Gigabytes, respectively, to the base of 1024.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>Rate=</varname></term>
+        <listitem>
+          <para>Specifies the maximum rate this class and all its children are guaranteed. When suffixed
+          with K, M, or G, the specified size is parsed as Kilobits, Megabits, or Gigabits, respectively,
+          to the base of 1000. This setting is mandatory.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>CeilRate=</varname></term>
+        <listitem>
+          <para>Specifies the maximum rate at which a class can send, if its parent has bandwidth to spare.
+          When suffixed with K, M, or G, the specified size is parsed as Kilobits, Megabits, or Gigabits,
+          respectively, to the base of 1000. When unset, the value specified with <varname>Rate=</varname>
+          is used.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>BufferBytes=</varname></term>
+        <listitem>
+          <para>Specifies the maximum bytes burst which can be accumulated during idle period. When suffixed
+          with K, M, or G, the specified size is parsed as Kilobytes, Megabytes, or Gigabytes, respectively,
+          to the base of 1024.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>CeilBufferBytes=</varname></term>
+        <listitem>
+          <para>Specifies the maximum bytes burst for ceil which can be accumulated during idle period.
+          When suffixed with K, M, or G, the specified size is parsed as Kilobytes, Megabytes, or Gigabytes,
+          respectively, to the base of 1024.</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>[HeavyHitterFilter] Section Options</title>
+    <para>The [HeavyHitterFilter] section manages the queueing discipline (qdisc) of Heavy Hitter Filter
+    (hhf).</para>
+
+    <variablelist class='network-directives'>
+      <xi:include href="tc.xml" xpointer="qdisc-parent" />
+      <xi:include href="tc.xml" xpointer="qdisc-handle" />
+
+      <varlistentry>
+        <term><varname>PacketLimit=</varname></term>
+        <listitem>
+          <para>Specifies the hard limit on the queue size in number of packets. When this limit is reached,
+          incoming packets are dropped. An unsigned integer in the range 0–4294967294. Defaults to unset and
+          kernel's default is used.</para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>[QuickFairQueueing] Section Options</title>
+    <para>The [QuickFairQueueing] section manages the queueing discipline (qdisc) of Quick Fair Queueing
+    (QFQ).</para>
+
+    <variablelist class='network-directives'>
+      <xi:include href="tc.xml" xpointer="qdisc-parent" />
+      <xi:include href="tc.xml" xpointer="qdisc-handle" />
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>[QuickFairQueueingClass] Section Options</title>
+    <para>The [QuickFairQueueingClass] section manages the traffic control class of Quick Fair Queueing
+    (qfq).</para>
+
+    <variablelist class='network-directives'>
+      <xi:include href="tc.xml" xpointer="tclass-parent" />
+      <xi:include href="tc.xml" xpointer="tclass-classid" />
+
+      <varlistentry>
+        <term><varname>Weight=</varname></term>
+        <listitem>
+          <para>Specifies the weight of the class. Takes an integer in the range 1..1023. Defaults to
+          unset in which case the kernel default is used.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>MaxPacketBytes=</varname></term>
+        <listitem>
+          <para>Specifies the maximum packet size in bytes for the class. When suffixed with K, M, or G, the specified
+          size is parsed as Kilobytes, Megabytes, or Gigabytes, respectively, to the base of 1024. When unset,
+          the kernel default is used.</para>
         </listitem>
       </varlistentry>
     </variablelist>
 
   <refsect1>
     <title>[BridgeVLAN] Section Options</title>
-      <para>The <literal>[BridgeVLAN]</literal> section manages the VLAN ID configuration of a bridge port and accepts
-      the following keys. Specify several <literal>[BridgeVLAN]</literal> sections to configure several VLAN entries.
-      The <varname>VLANFiltering=</varname> option has to be enabled, see <literal>[Bridge]</literal> section in
+      <para>The [BridgeVLAN] section manages the VLAN ID configuration of a bridge port and accepts the
+      following keys. Specify several [BridgeVLAN] sections to configure several VLAN entries. The
+      <varname>VLANFiltering=</varname> option has to be enabled, see the [Bridge] section in
       <citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
 
       <variablelist class='network-directives'>
@@ -2869,7 +3533,10 @@ DHCP=ipv6</programlisting>
 Name=enp2s0
 
 [Network]
-IPv6PrefixDelegation=dhcpv6</programlisting>
+IPv6PrefixDelegation=dhcpv6
+
+[DHCPv6]
+AssignAcquiredDelegatedPrefixAddress=yes</programlisting>
 
       <para>This will enable IPv6 PD on the interface enp1s0 as an upstream interface where the
       DHCPv6 client is running and enp2s0 as a downstream interface where the prefix is delegated to.</para>
index 27eae50b5170780d7c2c0845144f5ac383d4cf95..4b37c5dc4400b9cb24b18d927e2f63e299d3a8fc 100644 (file)
@@ -80,7 +80,7 @@
   <refsect1>
     <title>[Exec] Section Options</title>
 
-    <para>Settings files may include an <literal>[Exec]</literal>
+    <para>Settings files may include an [Exec]
     section, which carries various execution parameters:</para>
 
     <variablelist class='nspawn-directives'>
   <refsect1>
     <title>[Files] Section Options</title>
 
-    <para>Settings files may include a <literal>[Files]</literal>
+    <para>Settings files may include a [Files]
     section, which carries various parameters configuring the file
     system of the container:</para>
 
       <varlistentry>
         <term><varname>Inaccessible=</varname></term>
 
-        <listitem><para>Masks the specified file or directly in the container, by over-mounting it with an empty file
+        <listitem><para>Masks the specified file or directory in the container, by over-mounting it with an empty file
         node of the same type with the most restrictive access mode. Takes a file system path as argument. This option
         may be used multiple times to mask multiple files or directories. This option is equivalent to the command line
         switch <option>--inaccessible=</option>, see
   <refsect1>
     <title>[Network] Section Options</title>
 
-    <para>Settings files may include a <literal>[Network]</literal>
+    <para>Settings files may include a [Network]
     section, which carries various parameters configuring the network
     connectivity of the container:</para>
 
index 89c12b598bdacc2b6e2565c72f9c2bc5070706a0..81a37f67898c649bc1d42da5fe6b0460167f28e8 100644 (file)
@@ -33,7 +33,7 @@
 
     <orderedlist>
       <listitem>
-        <para>The package manager prepares system updates by downloading all (RPM or DEB or
+        <para>The package manager prepares system updates by downloading all (.rpm or .deb or
         whatever) packages to update off-line in a special directory
         <filename index="false">/var/lib/system-update</filename> (or
         another directory of the package/upgrade manager's choice).</para>
@@ -85,8 +85,8 @@
       </listitem>
 
       <listitem>
-        <para>The upgrade scripts should exit only after the update is finished. It is expected
-        that the service which performs the upgrade will cause the machine to reboot after it
+        <para>The update scripts should exit only after the update is finished. It is expected
+        that the service which performs the update will cause the machine to reboot after it
         is done. If the <filename>system-update.target</filename> is successfully reached, i.e.
         all update services have run, and the <filename>/system-update</filename> symlink still
         exists, it will be removed and the machine rebooted as a safety measure.</para>
index f6fe3d83883eca518563535f522ed27c3d169503..604bf494ba71a6222a4df753a18e3be622a5ebe5 100644 (file)
@@ -34,9 +34,9 @@
     this unit type. See
     <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
     for the common options of all unit configuration files. The common
-    configuration items are configured in the generic <literal>[Unit]</literal> and
-    <literal>[Install]</literal> sections. The path specific configuration options are
-    configured in the <literal>[Path]</literal> section.</para>
+    configuration items are configured in the generic [Unit] and
+    [Install] sections. The path specific configuration options are
+    configured in the [Path] section.</para>
 
     <para>For each path file, a matching unit file must exist,
     describing the unit to activate when the path changes. By default,
index 079cc27f11cd1869e7804ce37bd200afce47d9cd..3ccb5c49271dfcc516f00521fce03697f4cf2292 100644 (file)
         <term><varname>IPAddressDeny=<replaceable>ADDRESS[/PREFIXLENGTH]…</replaceable></varname></term>
 
         <listitem>
-          <para>Turn on address range network traffic filtering for IP packets sent and received over
-          <constant>AF_INET</constant> and <constant>AF_INET6</constant> sockets.  Both directives take a
+          <para>Turn on network traffic filtering for IP packets sent and received over
+          <constant>AF_INET</constant> and <constant>AF_INET6</constant> sockets. Both directives take a
           space separated list of IPv4 or IPv6 addresses, each optionally suffixed with an address prefix
-          length in bits (separated by a <literal>/</literal> character). If the latter is omitted, the
-          address is considered a host address, i.e. the prefix covers the whole address (32 for IPv4, 128
-          for IPv6).</para>
+          length in bits after a <literal>/</literal> character. If the suffix is omitted, the address is
+          considered a host address, i.e. the filter covers the whole address (32 bits for IPv4, 128 bits for
+          IPv6).</para>
 
           <para>The access lists configured with this option are applied to all sockets created by processes
           of this unit (or in the case of socket units, associated with it). The lists are implicitly
           combined with any lists configured for any of the parent slice units this unit might be a member
-          of. By default all access lists are empty. Both ingress and egress traffic is filtered by these
+          of. By default both access lists are empty. Both ingress and egress traffic is filtered by these
           settings. In case of ingress traffic the source IP address is checked against these access lists,
-          in case of egress traffic the destination IP address is checked. When configured the lists are
-          enforced as follows:</para>
+          in case of egress traffic the destination IP address is checked. The following rules are applied in
+          turn:</para>
 
           <itemizedlist>
-            <listitem><para>Access will be granted in case an IP packet's destination/source address matches
-            any entry in the <varname>IPAddressAllow=</varname> setting.</para></listitem>
+            <listitem><para>Access is granted when the checked IP address matches an entry in the
+            <varname>IPAddressAllow=</varname> list.</para></listitem>
 
-            <listitem><para>Otherwise, access will be denied in case its destination/source address matches
-            any entry in the <varname>IPAddressDeny=</varname> setting.</para></listitem>
+            <listitem><para>Otherwise, access is denied when the checked IP address matches an entry in the
+            <varname>IPAddressDeny=</varname> list.</para></listitem>
 
-            <listitem><para>Otherwise, access will be granted.</para></listitem>
+            <listitem><para>Otherwise, access is granted.</para></listitem>
           </itemizedlist>
 
-          <para>In order to implement a whitelisting IP firewall, it is recommended to use a
-          <varname>IPAddressDeny=</varname><constant>any</constant> setting on an upper-level slice unit (such as the
-          root slice <filename>-.slice</filename> or the slice containing all system services
+          <para>In order to implement an allow-listing IP firewall, it is recommended to use a
+          <varname>IPAddressDeny=</varname><constant>any</constant> setting on an upper-level slice unit
+          (such as the root slice <filename>-.slice</filename> or the slice containing all system services
           <filename>system.slice</filename> – see
-          <citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
-          details on these slice units), plus individual per-service <varname>IPAddressAllow=</varname> lines
-          permitting network access to relevant services, and only them.</para>
-
-          <para>Note that for socket-activated services, the IP access list configured on the socket unit applies to
-          all sockets associated with it directly, but not to any sockets created by the ultimately activated services
-          for it. Conversely, the IP access list configured for the service is not applied to any sockets passed into
-          the service via socket activation. Thus, it is usually a good idea, to replicate the IP access lists on both
-          the socket and the service unit, however it often makes sense to maintain one list more open and the other
-          one more restricted, depending on the usecase.</para>
+          <citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+          for details on these slice units), plus individual per-service <varname>IPAddressAllow=</varname>
+          lines permitting network access to relevant services, and only them.</para>
+
+          <para>Note that for socket-activated services, the IP access list configured on the socket unit
+          applies to all sockets associated with it directly, but not to any sockets created by the
+          ultimately activated services for it. Conversely, the IP access list configured for the service is
+          not applied to any sockets passed into the service via socket activation. Thus, it is usually a
+          good idea to replicate the IP access lists on both the socket and the service unit. Nevertheless,
+          it may make sense to maintain one list more open and the other one more restricted, depending on
+          the usecase.</para>
 
           <para>If these settings are used multiple times in the same unit the specified lists are combined. If an
           empty string is assigned to these settings the specific access list is reset and all previous settings undone.</para>
           <para>The device node specifier is either a path to a device node in the file system, starting with
           <filename>/dev/</filename>, or a string starting with either <literal>char-</literal> or
           <literal>block-</literal> followed by a device group name, as listed in
-          <filename>/proc/devices</filename>. The latter is useful to whitelist all current and future
+          <filename>/proc/devices</filename>. The latter is useful to allow-list all current and future
           devices belonging to a specific device group at once. The device group is matched according to
           filename globbing rules, you may hence use the <literal>*</literal> and <literal>?</literal>
           wildcards. (Note that such globbing wildcards are not available for device node path
           all pseudo TTYs and all ALSA sound devices, respectively. <literal>char-cpu/*</literal> is a
           specifier matching all CPU related device groups.</para>
 
-          <para>Note that whitelists defined this way should only reference device groups which are
+          <para>Note that allow lists defined this way should only reference device groups which are
           resolvable at the time the unit is started. Any device groups not resolvable then are not added to
-          the device whitelist. In order to work around this limitation, consider extending service units
+          the device allow list. In order to work around this limitation, consider extending service units
           with a pair of <command>After=modprobe@xyz.service</command> and
           <command>Wants=modprobe@xyz.service</command> lines that load the necessary kernel module
           implementing the device group if missing.
index b624ac5f9303990683bf502ee1672dd2e4dcc80d..449b90b48455733f35df7712862adf1c1ae90dcf 100644 (file)
@@ -89,7 +89,7 @@
   <refsect1>
     <title>Options</title>
 
-    <para>Scope files may include a <literal>[Scope]</literal>
+    <para>Scope files may include a [Scope]
     section, which carries information about the scope and the
     units it contains. A number of options that may be used in
     this section are shared with other unit types. These options are
@@ -97,7 +97,7 @@
     <citerefentry><refentrytitle>systemd.kill</refentrytitle><manvolnum>5</manvolnum></citerefentry>
     and
     <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
-    The options specific to the <literal>[Scope]</literal> section
+    The options specific to the [Scope] section
     of scope units are the following:</para>
 
     <variablelist class='unit-directives'>
index bc05e3dcb02e31a0c3af42aa00a9c5a25107adfa..4e281ec6d4f80d43151bdce6d91c9d04160b8852 100644 (file)
@@ -35,9 +35,9 @@
     <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
     for the common options of all unit configuration files. The common
     configuration items are configured in the generic
-    <literal>[Unit]</literal> and <literal>[Install]</literal>
+    [Unit] and [Install]
     sections. The service specific configuration options are
-    configured in the <literal>[Service]</literal> section.</para>
+    configured in the [Service] section.</para>
 
     <para>Additional options are listed in
     <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
   <refsect1>
     <title>Options</title>
 
-    <para>Service files must include a <literal>[Service]</literal>
+    <para>Service files must include a [Service]
     section, which carries information about the service and the
     process it supervises. A number of options that may be used in
     this section are shared with other unit types. These options are
     <citerefentry><refentrytitle>systemd.kill</refentrytitle><manvolnum>5</manvolnum></citerefentry>
     and
     <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
-    The options specific to the <literal>[Service]</literal> section
+    The options specific to the [Service] section
     of service units are the following:</para>
 
     <variablelist class='unit-directives'>
             option is used without <varname>RemainAfterExit=</varname> the service will never enter
             <literal>active</literal> unit state, but directly transition from <literal>activating</literal>
             to <literal>deactivating</literal> or <literal>dead</literal> since no process is configured that
-            shall run continously. In particular this means that after a service of this type ran (and which
+            shall run continuously. In particular this means that after a service of this type ran (and which
             has <varname>RemainAfterExit=</varname> not set) it will not show up as started afterwards, but
             as dead.</para></listitem>
 
         of the daemon, and may be used for command lines like the
         following:</para>
 
-        <programlisting>/bin/kill -HUP $MAINPID</programlisting>
+        <programlisting>ExecReload=kill -HUP $MAINPID</programlisting>
 
         <para>Note however that reloading a daemon by sending a signal
         (as with the example line above) is usually not a good choice,
         other. It is strongly recommended to set
         <varname>ExecReload=</varname> to a command that not only
         triggers a configuration reload of the daemon, but also
-        synchronously waits for it to complete.</para>
+        synchronously waits for it to complete. For example,
+        <citerefentry project='mankier'><refentrytitle>dbus-broker</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+        uses the following:</para>
+
+        <programlisting>ExecReload=busctl call org.freedesktop.DBus \
+        /org/freedesktop/DBus org.freedesktop.DBus \
+        ReloadConfig
+</programlisting>
         </listitem>
       </varlistentry>
 
 
       <varlistentry>
         <term><varname>TimeoutStartSec=</varname></term>
-        <listitem><para>Configures the time to wait for start-up. If a
-        daemon service does not signal start-up completion within the
-        configured time, the service will be considered failed and
-        will be shut down again. Takes a unit-less value in seconds,
-        or a time span value such as "5min 20s". Pass
-        <literal>infinity</literal> to disable the timeout logic. Defaults to
-        <varname>DefaultTimeoutStartSec=</varname> from the manager
-        configuration file, except when
-        <varname>Type=oneshot</varname> is used, in which case the
-        timeout is disabled by default (see
+        <listitem><para>Configures the time to wait for start-up. If a daemon service does not signal start-up
+        completion within the configured time, the service will be considered failed and will be shut down again. The
+        precise action depends on the <varname>TimeoutStartFailureMode=</varname> option. Takes a unit-less value in
+        seconds, or a time span value such as "5min 20s". Pass <literal>infinity</literal> to disable the timeout logic.
+        Defaults to <varname>DefaultTimeoutStartSec=</varname> from the manager configuration file, except when
+        <varname>Type=oneshot</varname> is used, in which case the timeout is disabled by default (see
         <citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>).
         </para>
 
         <para>If a service of <varname>Type=notify</varname> sends <literal>EXTEND_TIMEOUT_USEC=…</literal>, this may cause
         the start time to be extended beyond <varname>TimeoutStartSec=</varname>. The first receipt of this message
-        must occur before <varname>TimeoutStartSec=</varname> is exceeded, and once the start time has exended beyond
+        must occur before <varname>TimeoutStartSec=</varname> is exceeded, and once the start time has extended beyond
         <varname>TimeoutStartSec=</varname>, the service manager will allow the service to continue to start, provided
         the service repeats <literal>EXTEND_TIMEOUT_USEC=…</literal> within the interval specified until the service
         startup status is finished by <literal>READY=1</literal>. (see
         <listitem><para>This option serves two purposes. First, it configures the time to wait for each
         <varname>ExecStop=</varname> command. If any of them times out, subsequent <varname>ExecStop=</varname> commands
         are skipped and the service will be terminated by <constant>SIGTERM</constant>. If no <varname>ExecStop=</varname>
-        commands are specified, the service gets the <constant>SIGTERM</constant> immediately. Second, it configures the time
+        commands are specified, the service gets the <constant>SIGTERM</constant> immediately. This default behavior
+        can be changed by the <varname>TimeoutStopFailureMode=</varname> option. Second, it configures the time
         to wait for the service itself to stop. If it doesn't terminate in the specified time, it will be forcibly terminated
         by <constant>SIGKILL</constant> (see <varname>KillMode=</varname> in
         <citerefentry><refentrytitle>systemd.kill</refentrytitle><manvolnum>5</manvolnum></citerefentry>).
 
         <para>If a service of <varname>Type=notify</varname> sends <literal>EXTEND_TIMEOUT_USEC=…</literal>, this may cause
         the stop time to be extended beyond <varname>TimeoutStopSec=</varname>. The first receipt of this message
-        must occur before <varname>TimeoutStopSec=</varname> is exceeded, and once the stop time has exended beyond
+        must occur before <varname>TimeoutStopSec=</varname> is exceeded, and once the stop time has extended beyond
         <varname>TimeoutStopSec=</varname>, the service manager will allow the service to continue to stop, provided
         the service repeats <literal>EXTEND_TIMEOUT_USEC=…</literal> within the interval specified, or terminates itself
         (see <citerefentry><refentrytitle>sd_notify</refentrytitle><manvolnum>3</manvolnum></citerefentry>).
         <para>If a service of <varname>Type=notify</varname> handles <constant>SIGABRT</constant> itself (instead of relying
         on the kernel to write a core dump) it can send <literal>EXTEND_TIMEOUT_USEC=…</literal> to
         extended the abort time beyond <varname>TimeoutAbortSec=</varname>. The first receipt of this message
-        must occur before <varname>TimeoutAbortSec=</varname> is exceeded, and once the abort time has exended beyond
+        must occur before <varname>TimeoutAbortSec=</varname> is exceeded, and once the abort time has extended beyond
         <varname>TimeoutAbortSec=</varname>, the service manager will allow the service to continue to abort, provided
         the service repeats <literal>EXTEND_TIMEOUT_USEC=…</literal> within the interval specified, or terminates itself
         (see <citerefentry><refentrytitle>sd_notify</refentrytitle><manvolnum>3</manvolnum></citerefentry>).
         </para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>TimeoutStartFailureMode=</varname></term>
+        <term><varname>TimeoutStopFailureMode=</varname></term>
+
+        <listitem><para>These options configure the action that is taken in case a daemon service does not signal
+        start-up within its configured <varname>TimeoutStartSec=</varname>, respectively if it does not stop within
+        <varname>TimeoutStopSec=</varname>. Takes one of <option>terminate</option>, <option>abort</option> and
+        <option>kill</option>. Both options default to <option>terminate</option>.</para>
+
+        <para>If <option>terminate</option> is set the service will be gracefully terminated by sending the signal
+        specified in <varname>KillSignal=</varname> (defaults to <constant>SIGTERM</constant>, see
+        <citerefentry><refentrytitle>systemd.kill</refentrytitle><manvolnum>5</manvolnum></citerefentry>). If the
+        service does not terminate the <varname>FinalKillSignal=</varname> is sent after
+        <varname>TimeoutStopSec=</varname>. If <option>abort</option> is set, <varname>WatchdogSignal=</varname> is sent
+        instead and <varname>TimeoutAbortSec=</varname> applies before sending <varname>FinalKillSignal=</varname>.
+        This setting may be used to analyze services that fail to start-up or shut-down intermittently.
+        By using <option>kill</option> the service is immediately terminated by sending
+        <varname>FinalKillSignal=</varname> without any further timeout. This setting can be used to expedite the
+        shutdown of failing services.
+        </para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>RuntimeMaxSec=</varname></term>
 
 
         <para>If a service of <varname>Type=notify</varname> sends <literal>EXTEND_TIMEOUT_USEC=…</literal>, this may cause
         the runtime to be extended beyond <varname>RuntimeMaxSec=</varname>. The first receipt of this message
-        must occur before <varname>RuntimeMaxSec=</varname> is exceeded, and once the runtime has exended beyond
+        must occur before <varname>RuntimeMaxSec=</varname> is exceeded, and once the runtime has extended beyond
         <varname>RuntimeMaxSec=</varname>, the service manager will allow the service to continue to run, provided
         the service repeats <literal>EXTEND_TIMEOUT_USEC=…</literal> within the interval specified until the service
         shutdown is achieved by <literal>STOPPING=1</literal> (or termination). (see
         project='man-pages'><refentrytitle>signal</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
         a list of signal names.</para>
 
-        <para>Note that this setting does not change the the mapping between numeric exit statuses and their
+        <para>Note that this setting does not change the mapping between numeric exit statuses and their
         names, i.e. regardless how this setting is used 0 will still be mapped to <literal>SUCCESS</literal>
         (and thus typically shown as <literal>0/SUCCESS</literal> in tool outputs) and 1 to
         <literal>FAILURE</literal> (and thus typically shown as <literal>1/FAILURE</literal>), and so on. It
         this option will have no effect.</para>
 
         <example>
-          <title>A service with with the <varname>SuccessExitStatus=</varname> setting</title>
+          <title>A service with the <varname>SuccessExitStatus=</varname> setting</title>
 
           <programlisting>SuccessExitStatus=TEMPFAIL 250 SIGUSR1</programlisting>
 
         <option>exec</option>. Conversely, if an auxiliary process of the unit sends an
         <function>sd_notify()</function> message and immediately exits, the service manager might not be able to
         properly attribute the message to the unit, and thus will ignore it, even if
-        <varname>NotifyAccess=</varname><option>all</option> is set for it.</para></listitem>
+        <varname>NotifyAccess=</varname><option>all</option> is set for it.</para>
+
+        <para>Hence, to eliminate all race conditions involving lookup of the client's unit and attribution of notifications
+        to units correctly, <function>sd_notify_barrier()</function> may be used. This call acts as a synchronization point
+        and ensures all notifications sent before this call have been picked up by the service manager when it returns
+        successfully. Use of <function>sd_notify_barrier()</function> is needed for clients which are not invoked by the
+        service manager, otherwise this synchronization mechanism is unnecessary for attribution of notifications to the
+        unit.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         manager. If set to <constant>kill</constant> and one of the service's processes is killed by the OOM
         killer the kernel is instructed to kill all remaining processes of the service, too. Defaults to the
         setting <varname>DefaultOOMPolicy=</varname> in
-        <citerefentry><refentrytitle>system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry> is
-        set to, except for services where <varname>Delegate=</varname> is turned on, where it defaults to
+        <citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+        is set to, except for services where <varname>Delegate=</varname> is turned on, where it defaults to
         <constant>continue</constant>.</para>
 
         <para>Use the <varname>OOMScoreAdjust=</varname> setting to configure whether processes of the unit
@@ -1462,7 +1495,7 @@ ExecStart=/usr/sbin/simple-dbus-service
 WantedBy=multi-user.target</programlisting>
 
       <para>For <emphasis>bus-activatable</emphasis> services, do not
-      include a <literal>[Install]</literal> section in the systemd
+      include a [Install] section in the systemd
       service file, but use the <varname>SystemdService=</varname>
       option in the corresponding DBus service file, for example
       (<filename>/usr/share/dbus-1/system-services/org.example.simple-dbus-service.service</filename>):</para>
index 7157dfa32d390ed3f4dda254f64146b0f4d78f70..928c9905e105e2214fe37be8eb868c0e03419969 100644 (file)
     <para>By default, service and scope units are placed in
     <filename>system.slice</filename>, virtual machines and containers
     registered with
-    <citerefentry><refentrytitle>systemd-machined</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+    <citerefentry><refentrytitle>systemd-machined</refentrytitle><manvolnum>8</manvolnum></citerefentry>
     are found in <filename>machine.slice</filename>, and user sessions
     handled by
-    <citerefentry><refentrytitle>systemd-logind</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+    <citerefentry><refentrytitle>systemd-logind</refentrytitle><manvolnum>8</manvolnum></citerefentry>
     in <filename>user.slice</filename>. See
-    <citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    <citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>7</manvolnum></citerefentry>
     for more information.</para>
 
     <para>See
     <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
     for the common options of all unit configuration
     files. The common configuration items are configured
-    in the generic <literal>[Unit]</literal> and <literal>[Install]</literal> sections. The
+    in the generic [Unit] and [Install] sections. The
     slice specific configuration options are configured in
-    the <literal>[Slice]</literal> section. Currently, only generic resource control settings
+    the [Slice] section. Currently, only generic resource control settings
     as described in
     <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry> are allowed.
     </para>
index 60ea63f742a2766938cb6083223c394308873808..f989b99f955cb14d0ffac00ba462229194a2cdf4 100644 (file)
@@ -35,9 +35,9 @@
     this unit type. See
     <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
     for the common options of all unit configuration files. The common
-    configuration items are configured in the generic <literal>[Unit]</literal> and
-    <literal>[Install]</literal> sections. The socket specific configuration options are
-    configured in the <literal>[Socket]</literal> section.</para>
+    configuration items are configured in the generic [Unit] and
+    [Install] sections. The socket specific configuration options are
+    configured in the [Socket] section.</para>
 
     <para>Additional options are listed in
     <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
         <listitem><para>Socket units automatically gain a <varname>Before=</varname>
         dependency on the service units they activate.</para></listitem>
 
-        <listitem><para>Socket units referring to file system paths (such as AF_UNIX
-        sockets or FIFOs) implicitly gain <varname>Requires=</varname> and
-        <varname>After=</varname> dependencies on all mount units
-        necessary to access those paths.</para></listitem>
+        <listitem><para>Socket units referring to file system paths (such as <constant>AF_UNIX</constant>
+        sockets or FIFOs) implicitly gain <varname>Requires=</varname> and <varname>After=</varname>
+        dependencies on all mount units necessary to access those paths.</para></listitem>
 
         <listitem><para>Socket units using the <varname>BindToDevice=</varname>
         setting automatically gain a <varname>BindsTo=</varname> and
         url="https://www.kernel.org/doc/Documentation/usb/functionfs.txt">USB
         FunctionFS</ulink> endpoints location to listen on, for
         implementation of USB gadget functions. This expects an
-        absolute file system path of functionfs mount point as the argument.
+        absolute file system path of FunctionFS mount point as the argument.
         Behavior otherwise is very similar to the <varname>ListenFIFO=</varname>
         directive above. Use this to open the FunctionFS endpoint
         <filename>ep0</filename>. When using this option, the
       <varlistentry>
         <term><varname>SocketProtocol=</varname></term>
         <listitem><para>Takes one of <option>udplite</option>
-        or <option>sctp</option>. Specifies a socket protocol
-        (<constant>IPPROTO_UDPLITE</constant>) UDP-Lite
-        (<constant>IPPROTO_SCTP</constant>) SCTP socket respectively. </para>
+        or <option>sctp</option>. The socket will use the UDP-Lite
+        (<constant>IPPROTO_UDPLITE</constant>) or SCTP
+        (<constant>IPPROTO_SCTP</constant>) protocol, respectively.</para>
         </listitem>
       </varlistentry>
 
 
       <varlistentry>
         <term><varname>BindToDevice=</varname></term>
-        <listitem><para>Specifies a network interface name to bind
-        this socket to. If set, traffic will only be accepted from the
-        specified network interfaces. This controls the
-        SO_BINDTODEVICE socket option (see <citerefentry
-        project='man-pages'><refentrytitle>socket</refentrytitle><manvolnum>7</manvolnum></citerefentry>
-        for details). If this option is used, an implicit dependency
-        from this socket unit on the network interface device unit
-        (<citerefentry><refentrytitle>systemd.device</refentrytitle><manvolnum>5</manvolnum></citerefentry>
-        is created. Note that setting this parameter might result in
-        additional dependencies to be added to the unit (see
+        <listitem><para>Specifies a network interface name to bind this socket to. If set, traffic will only
+        be accepted from the specified network interfaces. This controls the
+        <constant>SO_BINDTODEVICE</constant> socket option (see <citerefentry
+        project='man-pages'><refentrytitle>socket</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
+        details). If this option is used, an implicit dependency from this socket unit on the network
+        interface device unit is created
+        (see <citerefentry><refentrytitle>systemd.device</refentrytitle><manvolnum>5</manvolnum></citerefentry>).
+        Note that setting this parameter might result in additional dependencies to be added to the unit (see
         above).</para></listitem>
       </varlistentry>
 
         <term><varname>SocketUser=</varname></term>
         <term><varname>SocketGroup=</varname></term>
 
-        <listitem><para>Takes a UNIX user/group name. When specified,
-        all AF_UNIX sockets and FIFO nodes in the file system are
-        owned by the specified user and group. If unset (the default),
-        the nodes are owned by the root user/group (if run in system
-        context) or the invoking user/group (if run in user context).
-        If only a user is specified but no group, then the group is
+        <listitem><para>Takes a UNIX user/group name. When specified, all <constant>AF_UNIX</constant>
+        sockets and FIFO nodes in the file system are owned by the specified user and group. If unset (the
+        default), the nodes are owned by the root user/group (if run in system context) or the invoking
+        user/group (if run in user context).  If only a user is specified but no group, then the group is
         derived from the user's default group.</para></listitem>
       </varlistentry>
 
         to work unmodified with systemd socket
         activation.</para>
 
-        <para>For IPv4 and IPv6 connections, the <varname>REMOTE_ADDR</varname>
-        environment variable will contain the remote IP address, and <varname>REMOTE_PORT</varname>
-        will contain the remote port. This is the same as the format used by CGI.
-        For SOCK_RAW, the port is the IP protocol.</para></listitem>
+        <para>For IPv4 and IPv6 connections, the <varname>REMOTE_ADDR</varname> environment variable will
+        contain the remote IP address, and <varname>REMOTE_PORT</varname> will contain the remote port. This
+        is the same as the format used by CGI. For <constant>SOCK_RAW</constant>, the port is the IP
+        protocol.</para></listitem>
       </varlistentry>
 
       <varlistentry>
 
        <varlistentry>
         <term><varname>KeepAlive=</varname></term>
-        <listitem><para>Takes a boolean argument. If true, the TCP/IP
-        stack will send a keep alive message after 2h (depending on
-        the configuration of
-        <filename>/proc/sys/net/ipv4/tcp_keepalive_time</filename>)
-        for all TCP streams accepted on this socket. This controls the
-        SO_KEEPALIVE socket option (see
-        <citerefentry project='man-pages'><refentrytitle>socket</refentrytitle><manvolnum>7</manvolnum></citerefentry>
-        and the <ulink
-        url="http://www.tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO/">TCP
-        Keepalive HOWTO</ulink> for details.) Defaults to
-        <option>false</option>.</para></listitem>
+        <listitem><para>Takes a boolean argument. If true, the TCP/IP stack will send a keep alive message
+        after 2h (depending on the configuration of
+        <filename>/proc/sys/net/ipv4/tcp_keepalive_time</filename>) for all TCP streams accepted on this
+        socket. This controls the <constant>SO_KEEPALIVE</constant> socket option (see <citerefentry
+        project='man-pages'><refentrytitle>socket</refentrytitle><manvolnum>7</manvolnum></citerefentry> and
+        the <ulink url="http://www.tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO/">TCP Keepalive
+        HOWTO</ulink> for details.) Defaults to <option>false</option>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
 
       <varlistentry>
         <term><varname>KeepAliveIntervalSec=</varname></term>
-        <listitem><para>Takes time (in seconds) as argument between
-        individual keepalive probes, if the socket option SO_KEEPALIVE
-        has been set on this socket. This controls
-        the TCP_KEEPINTVL socket option (see
-        <citerefentry project='man-pages'><refentrytitle>socket</refentrytitle><manvolnum>7</manvolnum></citerefentry>
-        and the <ulink
-        url="http://www.tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO/">TCP
-        Keepalive HOWTO</ulink> for details.) Defaults value is 75
-        seconds.</para></listitem>
+        <listitem><para>Takes time (in seconds) as argument between individual keepalive probes, if the
+        socket option <constant>SO_KEEPALIVE</constant> has been set on this socket. This controls the
+        <constant>TCP_KEEPINTVL</constant> socket option (see <citerefentry
+        project='man-pages'><refentrytitle>socket</refentrytitle><manvolnum>7</manvolnum></citerefentry> and
+        the <ulink url="http://www.tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO/">TCP Keepalive
+        HOWTO</ulink> for details.) Defaults value is 75 seconds.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         algorithm works by combining a number of small outgoing
         messages, and sending them all at once. This controls the
         TCP_NODELAY socket option (see
-        <citerefentry project='die-net'><refentrytitle>tcp</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+        <citerefentry project='die-net'><refentrytitle>tcp</refentrytitle><manvolnum>7</manvolnum></citerefentry>).
         Defaults to <option>false</option>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><varname>Priority=</varname></term>
-        <listitem><para>Takes an integer argument controlling the
-        priority for all traffic sent from this socket. This controls
-        the SO_PRIORITY socket option (see
-        <citerefentry project='man-pages'><refentrytitle>socket</refentrytitle><manvolnum>7</manvolnum></citerefentry>
-        for details.).</para></listitem>
+        <listitem><para>Takes an integer argument controlling the priority for all traffic sent from this
+        socket. This controls the <constant>SO_PRIORITY</constant> socket option (see <citerefentry
+        project='man-pages'><refentrytitle>socket</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
+        details.).</para></listitem>
       </varlistentry>
 
       <varlistentry>
       <varlistentry>
         <term><varname>ReceiveBuffer=</varname></term>
         <term><varname>SendBuffer=</varname></term>
-        <listitem><para>Takes an integer argument controlling the
-        receive or send buffer sizes of this socket, respectively.
-        This controls the SO_RCVBUF and SO_SNDBUF socket options (see
-        <citerefentry project='man-pages'><refentrytitle>socket</refentrytitle><manvolnum>7</manvolnum></citerefentry>
-        for details.). The usual suffixes K, M, G are supported and
-        are understood to the base of 1024.</para></listitem>
+        <listitem><para>Takes an integer argument controlling the receive or send buffer sizes of this
+        socket, respectively.  This controls the <constant>SO_RCVBUF</constant> and
+        <constant>SO_SNDBUF</constant> socket options (see <citerefentry
+        project='man-pages'><refentrytitle>socket</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
+        details.). The usual suffixes K, M, G are supported and are understood to the base of
+        1024.</para></listitem>
       </varlistentry>
 
       <varlistentry>
 
       <varlistentry>
         <term><varname>Mark=</varname></term>
-        <listitem><para>Takes an integer value. Controls the firewall
-        mark of packets generated by this socket. This can be used in
-        the firewall logic to filter packets from this socket. This
-        sets the SO_MARK socket option. See
-        <citerefentry project='die-net'><refentrytitle>iptables</refentrytitle><manvolnum>8</manvolnum></citerefentry>
-        for details.</para></listitem>
+        <listitem><para>Takes an integer value. Controls the firewall mark of packets generated by this
+        socket. This can be used in the firewall logic to filter packets from this socket. This sets the
+        <constant>SO_MARK</constant> socket option. See <citerefentry
+        project='die-net'><refentrytitle>iptables</refentrytitle><manvolnum>8</manvolnum></citerefentry> for
+        details.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><varname>ReusePort=</varname></term>
-        <listitem><para>Takes a boolean value. If true, allows
-        multiple
-        <citerefentry><refentrytitle>bind</refentrytitle><manvolnum>2</manvolnum></citerefentry>s
-        to this TCP or UDP port. This controls the SO_REUSEPORT socket
-        option. See
-        <citerefentry project='man-pages'><refentrytitle>socket</refentrytitle><manvolnum>7</manvolnum></citerefentry>
-        for details.</para></listitem>
+        <listitem><para>Takes a boolean value. If true, allows multiple
+        <citerefentry><refentrytitle>bind</refentrytitle><manvolnum>2</manvolnum></citerefentry>s to this TCP
+        or UDP port. This controls the <constant>SO_REUSEPORT</constant> socket option. See <citerefentry
+        project='man-pages'><refentrytitle>socket</refentrytitle><manvolnum>7</manvolnum></citerefentry> for
+        details.</para></listitem>
       </varlistentry>
 
       <varlistentry>
 
       <varlistentry>
         <term><varname>Broadcast=</varname></term>
-        <listitem><para>Takes a boolean value. This controls the
-        SO_BROADCAST socket option, which allows broadcast datagrams
-        to be sent from this socket. Defaults to
+        <listitem><para>Takes a boolean value. This controls the <constant>SO_BROADCAST</constant> socket
+        option, which allows broadcast datagrams to be sent from this socket. Defaults to
         <option>false</option>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><varname>PassCredentials=</varname></term>
-        <listitem><para>Takes a boolean value. This controls the
-        SO_PASSCRED socket option, which allows
-        <constant>AF_UNIX</constant> sockets to receive the
-        credentials of the sending process in an ancillary message.
-        Defaults to <option>false</option>.</para></listitem>
+        <listitem><para>Takes a boolean value. This controls the <constant>SO_PASSCRED</constant> socket
+        option, which allows <constant>AF_UNIX</constant> sockets to receive the credentials of the sending
+        process in an ancillary message. Defaults to <option>false</option>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><varname>PassSecurity=</varname></term>
-        <listitem><para>Takes a boolean value. This controls the
-        SO_PASSSEC socket option, which allows
-        <constant>AF_UNIX</constant> sockets to receive the security
-        context of the sending process in an ancillary message.
+        <listitem><para>Takes a boolean value. This controls the <constant>SO_PASSSEC</constant> socket
+        option, which allows <constant>AF_UNIX</constant> sockets to receive the security context of the
+        sending process in an ancillary message.  Defaults to <option>false</option>.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>PassPacketInfo=</varname></term>
+        <listitem><para>Takes a boolean value. This controls the <constant>IP_PKTINFO</constant>,
+        <constant>IPV6_RECVPKTINFO</constant> and <constant>NETLINK_PKTINFO</constant> socket options, which
+        enable reception of additional per-packet metadata as ancillary message, on
+        <constant>AF_INET</constant>, <constant>AF_INET6</constant> and <constant>AF_UNIX</constant> sockets.
         Defaults to <option>false</option>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><varname>TCPCongestion=</varname></term>
-        <listitem><para>Takes a string value. Controls the TCP
-        congestion algorithm used by this socket. Should be one of
-        "westwood", "veno", "cubic", "lp" or any other available
-        algorithm supported by the IP stack. This setting applies only
-        to stream sockets.</para></listitem>
+        <listitem><para>Takes a string value. Controls the TCP congestion algorithm used by this
+        socket. Should be one of <literal>westwood</literal>, <literal>veno</literal>,
+        <literal>cubic</literal>, <literal>lp</literal> or any other available algorithm supported by the IP
+        stack. This setting applies only to stream sockets.</para></listitem>
       </varlistentry>
 
       <varlistentry>
 
       <varlistentry>
         <term><varname>RemoveOnStop=</varname></term>
-        <listitem><para>Takes a boolean argument. If enabled, any file
-        nodes created by this socket unit are removed when it is
-        stopped. This applies to AF_UNIX sockets in the file system,
-        POSIX message queues, FIFOs, as well as any symlinks to them
-        configured with <varname>Symlinks=</varname>. Normally, it
-        should not be necessary to use this option, and is not
-        recommended as services might continue to run after the socket
-        unit has been terminated and it should still be possible to
-        communicate with them via their file system node. Defaults to
+        <listitem><para>Takes a boolean argument. If enabled, any file nodes created by this socket unit are
+        removed when it is stopped. This applies to <constant>AF_UNIX</constant> sockets in the file system,
+        POSIX message queues, FIFOs, as well as any symlinks to them configured with
+        <varname>Symlinks=</varname>. Normally, it should not be necessary to use this option, and is not
+        recommended as services might continue to run after the socket unit has been terminated and it should
+        still be possible to communicate with them via their file system node. Defaults to
         off.</para></listitem>
       </varlistentry>
 
index 30ec7782dd37494f83f22c0dbc6eb8386b03eb25..a948969a8f8efc07ad2700971c7fc8916933ec76 100644 (file)
   </refsect1>
 
   <refsect1>
-    <title>Units managed by the system's service manager</title>
+    <title>Units managed by the system service manager</title>
 
     <refsect2>
       <title>Special System Units</title>
             this unit (or <filename>multi-user.target</filename>) during
             installation. This is best configured via
             <varname>WantedBy=graphical.target</varname> in the unit's
-            <literal>[Install]</literal> section.</para>
+            [Install] section.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
             add <varname>Wants=</varname> dependencies for their unit to
             this unit during installation. This is best configured via
             <varname>WantedBy=multi-user.target</varname> in the unit's
-            <literal>[Install]</literal> section.</para>
+            [Install] section.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
             applications get pulled in via <varname>Wants=</varname>
             dependencies from this unit. This is best configured via a
             <varname>WantedBy=paths.target</varname> in the path unit's
-            <literal>[Install]</literal> section.</para>
+            [Install] section.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
             <para>Adding slice units to <filename>slices.target</filename> is generally not
             necessary. Instead, when some unit that uses <varname>Slice=</varname> is started, the
             specified slice will be started automatically. Adding
-            <varname>WantedBy=slices.target</varname> lines to the <literal>[Install]</literal>
+            <varname>WantedBy=slices.target</varname> lines to the [Install]
             section should only be done for units that need to be always active. In that case care
             needs to be taken to avoid creating a loop through the automatic dependencies on
             "parent" slices.</para>
             <varname>Wants=</varname> dependencies to this unit for
             their socket unit during installation. This is best
             configured via a <varname>WantedBy=sockets.target</varname>
-            in the socket unit's <literal>[Install]</literal>
+            in the socket unit's [Install]
             section.</para>
           </listitem>
         </varlistentry>
             applications get pulled in via <varname>Wants=</varname>
             dependencies from this unit. This is best configured via
             <varname>WantedBy=timers.target</varname> in the timer
-            unit's <literal>[Install]</literal> section.</para>
+            unit's [Install] section.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
             <para>By default, all user processes and services started on
             behalf of the user, including the per-user systemd instance
             are found in this slice.  This is pulled in by
-            <filename>systemd-logind.service</filename></para>
+            <filename>systemd-logind.service</filename>.</para>
           </listitem>
         </varlistentry>
 
           <listitem>
             <para>By default, all virtual machines and containers
             registered with <command>systemd-machined</command> are
-            found in this slice.  This is pulled in by
-            <filename>systemd-machined.service</filename></para>
+            found in this slice. This is pulled in by
+            <filename>systemd-machined.service</filename>.</para>
           </listitem>
         </varlistentry>
       </variablelist>
   </refsect1>
 
   <refsect1>
-    <title>Units managed by the user's service manager</title>
+    <title>Units managed by the user service manager</title>
 
     <refsect2>
       <title>Special User Units</title>
             <para>This target is active whenever any graphical session is running. It is used to
             stop user services which only apply to a graphical (X, Wayland, etc.) session when the
             session is terminated. Such services should have
-            <literal>PartOf=graphical-session.target</literal> in their <literal>[Unit]</literal>
+            <literal>PartOf=graphical-session.target</literal> in their [Unit]
             section. A target for a particular session (e. g.
             <filename>gnome-session.target</filename>) starts and stops
             <literal>graphical-session.target</literal> with
             <filename>gnome-session.target</filename>.</para>
           </listitem>
         </varlistentry>
+
+        <varlistentry>
+          <term><filename>xdg-desktop-autostart.target</filename></term>
+          <listitem>
+            <para>The XDG specification defines a way to autostart applications using XDG desktop files.
+            systemd ships
+            <citerefentry><refentrytitle>systemd-xdg-autostart-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+            for the XDG desktop files in autostart directories.
+            Desktop Environments can opt-in to use this service by adding a <varname>Wants=</varname>
+            dependency on <literal>xdg-desktop-autostart.target</literal></para>.
+          </listitem>
+        </varlistentry>
       </variablelist>
     </refsect2>
   </refsect1>
index 190fc388c0911a5d64a7520ba9d5e659553ad5ad..4b1f850af4f3c1444e758f8d00f16312065b98b2 100644 (file)
@@ -37,9 +37,9 @@
     this unit type. See
     <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
     for the common options of all unit configuration files. The common
-    configuration items are configured in the generic <literal>[Unit]</literal> and
-    <literal>[Install]</literal> sections. The swap specific configuration options are
-    configured in the <literal>[Swap]</literal> section.</para>
+    configuration items are configured in the generic [Unit] and
+    [Install] sections. The swap specific configuration options are
+    configured in the [Swap] section.</para>
 
     <para>Additional options are listed in
     <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
   <refsect1>
     <title>Options</title>
 
-    <para>Swap files must include a [Swap] section, which carries
+    <para>Swap unit files must include a [Swap] section, which carries
     information about the swap device it supervises. A number of
     options that may be used in this section are shared with other
     unit types. These options are documented in
         project='man-pages'><refentrytitle>swapon</refentrytitle><manvolnum>8</manvolnum></citerefentry> for
         details. If this refers to a device node, a dependency on the respective device unit is automatically
         created. (See
-        <citerefentry><refentrytitle>systemd.device</refentrytitle><manvolnum>5</manvolnum></citerefentry> for more
-        information.) If this refers to a file, a dependency on the respective mount unit is automatically
-        created. (See <citerefentry><refentrytitle>systemd.mount</refentrytitle><manvolnum>5</manvolnum></citerefentry>
-        for more information.) This option is mandatory. Note that the usual specifier expansion is applied to this
-        setting, literal percent characters should hence be written as <literal>%%</literal>.</para></listitem>
+        <citerefentry><refentrytitle>systemd.device</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+        for more information.) If this refers to a file, a dependency on the respective mount unit is
+        automatically created. (See
+        <citerefentry><refentrytitle>systemd.mount</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
+        more information.) This option is mandatory. Note that the usual specifier expansion is applied to
+        this setting, literal percent characters should hence be written as
+        <literal class='specifiers'>%%</literal>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
index 04b1564b1747e8bf62f0c985354cae431b3471ce..df100ec4e7b3634eaa517e22d89ef3ed8ea161f1 100644 (file)
@@ -98,10 +98,10 @@ KeyTwo=value 2 \
        value 2 continued
 
 [Section C]
-KeyThree=value 2\
+KeyThree=value 3\
 # this line is ignored
 ; this line is ignored too
-       value 2 continued
+       value 3 continued
 </programlisting></example>
 
     <para>Boolean arguments used in configuration files can be written in
index 3052b177864c56f5123ba1b4ef642c64388083f6..a706a4588af78c3330901f045e101eb44825dfe7 100644 (file)
@@ -34,8 +34,8 @@
     <para>This unit type has no specific options. See
     <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
     for the common options of all unit configuration files. The common
-    configuration items are configured in the generic <literal>[Unit]</literal> and
-    <literal>[Install]</literal> sections. A separate <literal>[Target]</literal> section does not exist,
+    configuration items are configured in the generic [Unit] and
+    [Install] sections. A separate [Target] section does not exist,
     since no target-specific options may be configured.</para>
 
     <para>Target units do not offer any additional functionality on
index b4656b05d10ba07e108cd3023e75f0410ff82206..5b7800e78b467b329676f6095bbf137165b31dcb 100644 (file)
   <refsect1>
     <title>Parsing Timestamps</title>
 
-    <para>When parsing, systemd will accept a similar syntax, but expects no timezone specification, unless it is given
-    as the literal string <literal>UTC</literal> (for the UTC timezone), or is specified to be the locally configured
-    timezone, or the timezone name in the IANA timezone database format. The complete list of timezones
-    supported on your system can be obtained using the <literal>timedatectl list-timezones</literal>
-    (see <citerefentry><refentrytitle>timedatectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>).
-    Using IANA format is recommended over local timezone names, as less prone to errors (eg: with local timezone it's possible to
-    specify daylight saving time in winter, while it's incorrect). The weekday specification is optional, but when
-    the weekday is specified, it must either be in the abbreviated (<literal>Wed</literal>) or non-abbreviated
-    (<literal>Wednesday</literal>) English language form (case does not matter), and is not subject to the locale
-    choice of the user.  Either the date, or the time part may be omitted, in which case the current date or 00:00:00,
-    respectively, is assumed. The seconds component of the time may also be omitted, in which case ":00" is
-    assumed. Year numbers may be specified in full or may be abbreviated (omitting the century).</para>
+    <para>When parsing, systemd will accept a similar syntax, but expects no timezone specification, unless
+    it is given as the literal string <literal>UTC</literal> (for the UTC timezone), or is specified to be
+    the locally configured timezone, or the timezone name in the IANA timezone database format. The complete
+    list of timezones supported on your system can be obtained using the <literal>timedatectl
+    list-timezones</literal> (see
+    <citerefentry><refentrytitle>timedatectl</refentrytitle><manvolnum>1</manvolnum></citerefentry>).  Using
+    IANA format is recommended over local timezone names, as less prone to errors (e.g. with local timezone
+    it's possible to specify daylight saving time in winter, even though that is not correct). The weekday
+    specification is optional, but when the weekday is specified, it must either be in the abbreviated
+    (<literal>Wed</literal>) or non-abbreviated (<literal>Wednesday</literal>) English language form (case
+    does not matter), and is not subject to the locale choice of the user. Either the date, or the time part
+    may be omitted, in which case the current date or 00:00:00, respectively, is assumed. The seconds
+    component of the time may also be omitted, in which case ":00" is assumed. Year numbers may be specified
+    in full or may be abbreviated (omitting the century).</para>
 
     <para>A timestamp is considered invalid if a weekday is specified and the date does not match the specified day of
     the week.</para>
@@ -282,7 +284,7 @@ Wed..Sat,Tue 12-10-15 1:2:3 → Tue..Sat 2012-10-15 01:02:03
       <para>Use the <command>calendar</command> command of
       <citerefentry><refentrytitle>systemd-analyze</refentrytitle><manvolnum>1</manvolnum></citerefentry> to validate
       and normalize calendar time specifications for testing purposes. The tool also calculates when a specified
-      calendar event would elapse next.</para>
+      calendar event would occur next.</para>
   </refsect1>
 
   <refsect1>
index 040b8e28939eadd20bbc887823f67c21e0c008e7..582240271284781312d0ed98c6674f9da8e04d77 100644 (file)
@@ -35,9 +35,9 @@
     this unit type. See
     <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
     for the common options of all unit configuration files. The common
-    configuration items are configured in the generic <literal>[Unit]</literal> and
-    <literal>[Install]</literal> sections. The timer specific configuration options are
-    configured in the <literal>[Timer]</literal> section.</para>
+    configuration items are configured in the generic [Unit] and
+    [Install] sections. The timer specific configuration options are
+    configured in the [Timer] section.</para>
 
     <para>For each timer file, a matching unit file must exist,
     describing the unit to activate when the timer elapses. By
index 86185e48aa72e7a2fcd287dba61f8f8db6d634b8..b3deb2895647497b184df62307a86afd7546da65 100644 (file)
@@ -6,7 +6,8 @@
 ]>
 <!-- SPDX-License-Identifier: LGPL-2.1+ -->
 
-<refentry id="systemd.unit">
+<refentry id="systemd.unit"
+          xmlns:xi="http://www.w3.org/2001/XInclude">
 
   <refentryinfo>
     <title>systemd.unit</title>
@@ -80,7 +81,7 @@
     target, a watched file system path, a timer controlled and supervised by
     <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>, a
     resource management slice or a group of externally created processes. See
-    <citerefentry><refentrytitle>systemd.syntax</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    <citerefentry><refentrytitle>systemd.syntax</refentrytitle><manvolnum>7</manvolnum></citerefentry>
     for a general description of the syntax.</para>
 
     <para>This man page lists the common configuration options of all
     <filename>foo-.service.d/10-override.conf</filename> would override
     <filename>service.d/10-override.conf</filename>.</para>
 
-    <!-- Note that we do not document .include here, as we consider it mostly obsolete, and want
-         people to use .d/ drop-ins instead. -->
-
     <para>Note that while systemd offers a flexible dependency system
     between units it is recommended to use this functionality only
     sparingly and instead rely on techniques such as bus-based or
         that the listed unit is fully started up before the configured unit is started.</para>
 
         <para>When two units with an ordering dependency between them are shut down, the inverse of the
-        start-up order is applied. i.e. if a unit is configured with <varname>After=</varname> on another
+        start-up order is applied. I.e. if a unit is configured with <varname>After=</varname> on another
         unit, the former is stopped before the latter if both are shut down. Given two units with any
         ordering dependency between them, if one unit is shut down and the other is started up, the shutdown
         is ordered before the start-up. It doesn't matter if the ordering dependency is
         type when precisely a unit has finished starting up. Most importantly, for service units start-up is
         considered completed for the purpose of <varname>Before=</varname>/<varname>After=</varname> when all
         its configured start-up commands have been invoked and they either failed or reported start-up
-        success. Note that this does includes ExecStartPost (or ExecStopPost for the shutdown case).</para>
+        success. Note that this does includes <varname>ExecStartPost=</varname> (or
+        <varname>ExecStopPost=</varname> for the shutdown case).</para>
 
         <para>Note that those settings are independent of and orthogonal to the requirement dependencies as
         configured by <varname>Requires=</varname>, <varname>Wants=</varname>, <varname>Requisite=</varname>,
         <option>--job-mode=</option> option for details on the
         possible values. If this is set to <literal>isolate</literal>,
         only a single unit may be listed in
-        <varname>OnFailure=</varname>..</para></listitem>
+        <varname>OnFailure=</varname>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><varname>StartLimitAction=</varname></term>
 
         <listitem><para>Configure an additional action to take if the rate limit configured with
-        <varname>StartLimitIntervalSec=</varname> and <varname>StartLimitBurst=</varname> is hit.  Takes the same
-        values as the setting <varname>FailureAction=</varname>/<varname>SuccessAction=</varname> settings and executes
-        the same actions. If <option>none</option> is set, hitting the rate limit will trigger no action besides that
+        <varname>StartLimitIntervalSec=</varname> and <varname>StartLimitBurst=</varname> is hit. Takes the same
+        values as the <varname>FailureAction=</varname>/<varname>SuccessAction=</varname> settings. If
+        <option>none</option> is set, hitting the rate limit will trigger no action except that
         the start will not be permitted. Defaults to <option>none</option>.</para></listitem>
       </varlistentry>
 
           <literal>podman</literal>,
           <literal>rkt</literal>,
           <literal>wsl</literal>,
+          <literal>proot</literal>,
           <literal>acrn</literal> to test
           against a specific implementation, or
           <literal>private-users</literal> to check whether we are running in a user namespace. See
           </listitem>
         </varlistentry>
 
+        <varlistentry>
+          <term><varname>ConditionEnvironment=</varname></term>
+
+          <listitem><para><varname>ConditionEnvironment=</varname> may be used to check whether a specific
+          environment variable is set (or if prefixed with the exclamation mark — unset) in the service
+          manager's environment block.
+
+          The argument may be a single word, to check if the variable with this name is defined in the
+          environment block, or an assignment
+          (<literal><replaceable>name</replaceable>=<replaceable>value</replaceable></literal>), to check if
+          the variable with this exact value is defined. Note that the environment block of the service
+          manager itself is checked, i.e. not any variables defined with <varname>Environment=</varname> or
+          <varname>EnvironmentFile=</varname>, as described above. This is particularly useful when the
+          service manager runs inside a containerized environment or as per-user service manager, in order to
+          check for variables passed in by the enclosing container manager or PAM.</para>
+          </listitem>
+        </varlistentry>
+
         <varlistentry>
           <term><varname>ConditionSecurity=</varname></term>
 
           <citerefentry><refentrytitle>systemd-update-done.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
           to make sure they run before the stamp file's modification time gets reset indicating a completed
           update.</para>
+
+          <para>If the <varname>systemd.condition-needs-update=</varname> option is specified on the kernel
+          command line (taking a boolean), it will override the result of this condition check, taking
+          precedence over any file modification time checks. If it is used
+          <filename>systemd-update-done.service</filename> will not have immediate effect on any following
+          <varname>ConditionNeedsUpdate=</varname> checks, until the system is rebooted where the kernel
+          command line option is not specified anymore.</para>
           </listitem>
         </varlistentry>
 
           (specifically: an <filename>/etc</filename> with no <filename>/etc/machine-id</filename>). This may
           be used to populate <filename>/etc</filename> on the first boot after factory reset, or when a new
           system instance boots up for the first time.</para>
+
+          <para>If the <varname>systemd.condition-first-boot=</varname> option is specified on the kernel
+          command line (taking a boolean), it will override the result of this condition check, taking
+          precedence over <filename>/etc/machine-id</filename> existence checks.</para>
           </listitem>
         </varlistentry>
 
           </listitem>
         </varlistentry>
 
+        <varlistentry>
+          <term><varname>ConditionPathIsEncrypted=</varname></term>
+
+          <listitem><para><varname>ConditionPathIsEncrypted=</varname> is similar to
+          <varname>ConditionPathExists=</varname> but verifies that the underlying file system's backing
+          block device is encrypted using dm-crypt/LUKS. Note that this check does not cover ext4
+          per-directory encryption, and only detects block level encryption. Moreover, if the specified path
+          resides on a file system on top of a loopback block device, only encryption above the loopback device is
+          detected. It is not detected whether the file system backing the loopback block device is encrypted.</para>
+          </listitem>
+        </varlistentry>
+
         <varlistentry>
           <term><varname>ConditionDirectoryNotEmpty=</varname></term>
 
   <refsect1>
     <title>[Install] Section Options</title>
 
-    <para>Unit files may include an <literal>[Install]</literal> section, which carries installation information for
+    <para>Unit files may include an [Install] section, which carries installation information for
     the unit. This section is not interpreted by
     <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry> during runtime; it is
     used by the <command>enable</command> and <command>disable</command> commands of the
     and resolvable for the setting to be valid. The following
     specifiers are understood:</para>
 
-    <table>
+    <table class='specifiers'>
       <title>Specifiers available in unit files</title>
       <tgroup cols='3' align='left' colsep='1' rowsep='1'>
         <colspec colname="spec" />
         </thead>
         <tbody>
           <row>
-            <entry><literal>%b</literal></entry>
-            <entry>Boot ID</entry>
-            <entry>The boot ID of the running system, formatted as string. See <citerefentry><refentrytitle>random</refentrytitle><manvolnum>4</manvolnum></citerefentry> for more information.</entry>
+            <!-- We do not use the common definition from standard-specifiers.xml here since it includes a
+                 reference onto our own man page, which would make the rendered version self-referential. -->
+            <entry><literal>%a</literal></entry>
+            <entry>Architecture</entry>
+            <entry>A short string identifying the architecture of the local system. A string such as <constant>x86</constant>, <constant>x86-64</constant> or <constant>arm64</constant>. See the architectures defined for <varname>ConditionArchitecture=</varname> above for a full list.</entry>
           </row>
+          <xi:include href="standard-specifiers.xml" xpointer="b"/>
+          <xi:include href="standard-specifiers.xml" xpointer="B"/>
           <row>
             <entry><literal>%C</literal></entry>
             <entry>Cache directory root</entry>
 Note that this setting is <emphasis>not</emphasis> influenced by the <varname>User=</varname> setting configurable in the [Service] section of the service unit.</entry>
           </row>
           <row>
+            <!-- We do not use the common definition from standard-specifiers.xml here since we want a
+                 slightly more verbose explanation here, referring to the reload cycle. -->
             <entry><literal>%H</literal></entry>
             <entry>Host name</entry>
             <entry>The hostname of the running system at the point in time the unit configuration is loaded.</entry>
           </row>
+          <row>
+            <entry><literal>%l</literal></entry>
+            <entry>Short host name</entry>
+            <entry>The hostname of the running system at the point in time the unit configuration is loaded, truncated at the first dot to remove any domain component.</entry>
+          </row>
           <row>
             <entry><literal>%i</literal></entry>
             <entry>Instance name</entry>
@@ -1763,11 +1815,8 @@ Note that this setting is <emphasis>not</emphasis> influenced by the <varname>Us
             <entry>Log directory root</entry>
             <entry>This is either <filename>/var/log</filename> (for the system manager) or the path <literal>$XDG_CONFIG_HOME</literal> resolves to with <filename index="false">/log</filename> appended (for user managers).</entry>
           </row>
-          <row>
-            <entry><literal>%m</literal></entry>
-            <entry>Machine ID</entry>
-            <entry>The machine ID of the running system, formatted as string. See <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry> for more information.</entry>
-          </row>
+          <xi:include href="standard-specifiers.xml" xpointer="m"/>
+          <xi:include href="standard-specifiers.xml" xpointer="o"/>
           <row>
             <entry><literal>%n</literal></entry>
             <entry>Full unit name</entry>
@@ -1832,21 +1881,15 @@ Note that this setting is <emphasis>not</emphasis> influenced by the <varname>Us
 
 Note that this setting is <emphasis>not</emphasis> influenced by the <varname>User=</varname> setting configurable in the [Service] section of the service unit.</entry>
           </row>
-          <row>
-            <entry><literal>%v</literal></entry>
-            <entry>Kernel release</entry>
-            <entry>Identical to <command>uname -r</command> output</entry>
-          </row>
+          <xi:include href="standard-specifiers.xml" xpointer="v"/>
           <row>
             <entry><literal>%V</literal></entry>
             <entry>Directory for larger and persistent temporary files</entry>
             <entry>This is either <filename>/var/tmp</filename> or the path <literal>$TMPDIR</literal>, <literal>$TEMP</literal> or <literal>$TMP</literal> are set to.</entry>
           </row>
-          <row>
-            <entry><literal>%%</literal></entry>
-            <entry>Single percent sign</entry>
-            <entry>Use <literal>%%</literal> in place of <literal>%</literal> to specify a single percent sign.</entry>
-          </row>
+          <xi:include href="standard-specifiers.xml" xpointer="w"/>
+          <xi:include href="standard-specifiers.xml" xpointer="W"/>
+          <xi:include href="standard-specifiers.xml" xpointer="percent"/>
         </tbody>
       </tgroup>
     </table>
index 28bf49e131b1ab56562e32ee96d72dc788b0f039..a9040545c2ab6181998ba781912a86c550b5ca6a 100644 (file)
     <orderedlist>
       <listitem><para>It is in an active, activating, deactivating or failed state (i.e. in any unit state except for <literal>inactive</literal>)</para></listitem>
       <listitem><para>It has a job queued for it</para></listitem>
-      <listitem><para>It is a dependency of some sort of at least one other unit that is loaded into memory</para></listitem>
+      <listitem><para>It is a dependency of at least one other unit that is loaded into memory</para></listitem>
       <listitem><para>It has some form of resource still allocated (e.g. a service unit that is inactive but for which
       a process is still lingering that ignored the request to be terminated)</para></listitem>
       <listitem><para>It has been pinned into memory programmatically by a D-Bus call</para></listitem>
     execution compared to the target unit's state and is marked successful and
     complete when both satisfy. However, this job also pulls in other
     dependencies due to the defined relationships and thus leads to, in our
-    our example, start jobs for any of those inactive units getting queued as
+    example, start jobs for any of those inactive units getting queued as
     well.</para>
 
     <para>systemd contains native implementations of various tasks
     files or parameters passed on the kernel command line. For details, see
     <citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
 
+    <para>The D-Bus API of <command>systemd</command> is described in
+    <citerefentry><refentrytitle>org.freedesktop.systemd1</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    and
+    <citerefentry><refentrytitle>org.freedesktop.LogControl1</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+    </para>
+
     <para>Systems which invoke systemd in a container or initrd environment should implement the <ulink
     url="https://systemd.io/CONTAINER_INTERFACE">Container Interface</ulink> or <ulink
     url="https://www.freedesktop.org/wiki/Software/systemd/InitrdInterface">initrd Interface</ulink>
     <title>Environment</title>
 
     <variablelist class='environment-variables'>
+      <varlistentry>
+        <term><varname>$SYSTEMD_LOG_COLOR</varname></term>
+        <listitem><para>Controls whether systemd highlights important
+        log messages. This can be overridden with
+        <option>--log-color</option>.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>$SYSTEMD_LOG_LEVEL</varname></term>
         <listitem><para>systemd reads the log level from this
         <option>--log-level=</option>.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>$SYSTEMD_LOG_LOCATION</varname></term>
+        <listitem><para>Controls whether systemd prints the code
+        location along with log messages. This can be overridden with
+        <option>--log-location</option>.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>$SYSTEMD_LOG_TARGET</varname></term>
         <listitem><para>systemd reads the log target from this
       </varlistentry>
 
       <varlistentry>
-        <term><varname>$SYSTEMD_LOG_COLOR</varname></term>
-        <listitem><para>Controls whether systemd highlights important
-        log messages. This can be overridden with
-        <option>--log-color=</option>.</para></listitem>
-      </varlistentry>
-
-      <varlistentry>
-        <term><varname>$SYSTEMD_LOG_LOCATION</varname></term>
-        <listitem><para>Controls whether systemd prints the code
-        location along with log messages. This can be overridden with
-        <option>--log-location=</option>.</para></listitem>
+        <term><varname>$SYSTEMD_LOG_TIME</varname></term>
+        <listitem><para>Controls whether systemd prefixes log
+        messages with the current time. This can be overridden with
+        <option>--log-time=</option>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
 
       <varlistentry>
         <term><varname>$SYSTEMD_UNIT_PATH</varname></term>
-
-        <listitem><para>Controls where systemd looks for unit
-        files.</para></listitem>
+        <term><varname>$SYSTEMD_GENERATOR_PATH</varname></term>
+        <term><varname>$SYSTEMD_ENVIRONMENT_GENERATOR_PATH</varname></term>
+
+        <listitem><para>Controls where systemd looks for unit files and
+        generators.</para>
+        <para>These variables may contain a list of paths, separated by colons
+        (<literal>:</literal>). When set, if the list ends with an empty
+        component (<literal>...:</literal>), this list is prepended to the
+        usual set of of paths. Otherwise, the specified list replaces the usual
+        set of paths.
+        </para></listitem>
       </varlistentry>
 
       <varlistentry>
       <varlistentry>
         <term><varname>systemd.crash_chvt</varname></term>
 
-        <listitem><para>Takes a positive integer, or a boolean argument. Can be also
-        specified without an argument, with the same effect as a positive boolean. If
-        a positive integer (in the range 1–63) is specified, the system manager (PID
-        1) will activate the specified virtual terminal (VT) when it
-        crashes. Defaults to disabled, meaning that no such switch is attempted. If
-        set to enabled, the VT the kernel messages are written to is selected.
-        </para></listitem>
+        <listitem><para>Takes a positive integer, or a boolean argument. Can be also specified without an
+        argument, with the same effect as a positive boolean. If a positive integer (in the range 1–63) is
+        specified, the system manager (PID 1) will activate the specified virtual terminal when it crashes.
+        Defaults to disabled, meaning that no such switch is attempted. If set to enabled, the virtual
+        terminal the kernel messages are written to is used instead.</para></listitem>
       </varlistentry>
 
       <varlistentry>
       </varlistentry>
 
       <varlistentry>
-        <term><varname>systemd.log_target=</varname></term>
-        <term><varname>systemd.log_level=</varname></term>
-        <term><varname>systemd.log_location=</varname></term>
         <term><varname>systemd.log_color</varname></term>
+        <term><varname>systemd.log_level=</varname></term>
+        <term><varname>systemd.log_location</varname></term>
+        <term><varname>systemd.log_target=</varname></term>
+        <term><varname>systemd.log_time</varname></term>
 
         <listitem><para>Controls log output, with the same effect as the
-        <varname>$SYSTEMD_LOG_TARGET</varname>,
+        <varname>$SYSTEMD_LOG_COLOR</varname>,
         <varname>$SYSTEMD_LOG_LEVEL</varname>,
         <varname>$SYSTEMD_LOG_LOCATION</varname>,
-        <varname>$SYSTEMD_LOG_COLOR</varname> environment variables described above.
-        <varname>systemd.log_color</varname> can be specified without an argument,
-        with the same effect as a positive boolean.</para></listitem>
+        <varname>$SYSTEMD_LOG_TARGET</varname>,
+        <varname>$SYSTEMD_LOG_TIME</varname>, environment variables described above.
+        <varname>systemd.log_color</varname>, <varname>systemd.log_location</varname>, and
+        <varname>systemd.log_time</varname> can be specified without an argument, with the
+        same effect as a positive boolean.</para></listitem>
       </varlistentry>
 
       <varlistentry>
       this context, because they are properly namespaced. When an option is specified both on the kernel
       command line, and as a normal command line argument, the latter has higher precedence.</para>
 
-      <para>When <command>systemd</command> is used a user manager, the kernel command line is ignored and
+      <para>When <command>systemd</command> is used as a user manager, the kernel command line is ignored and
       the options described are understood. Nevertheless, <command>systemd</command> is usually started in
       this mode through the
       <citerefentry><refentrytitle>user@.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>
       service, which is shared between all users, and it may be more convenient to use configuration files to
       modify settings, see
       <citerefentry><refentrytitle>systemd-user.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
-      or a drop-in that specifies one of the environment variables listed above in "Environment, see
+      or a drop-in that specifies one of the environment variables listed above in the Environment section,
+      see
       <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
 
       <variablelist>
         <varlistentry>
           <term><option>--show-status</option></term>
 
-          <listitem><para>Show terse unit status information is shown on the console during boot-up and
-          shutdown. See <varname>systemd.show_status</varname> above.</para></listitem>
+          <listitem><para>Show terse unit status information on the console during boot-up and shutdown. See
+          <varname>systemd.show_status</varname> above.</para></listitem>
         </varlistentry>
 
         <varlistentry>
-          <term><option>--log-target=</option></term>
+          <term><option>--log-color</option></term>
 
-          <listitem><para>Set log target. See <varname>systemd.log_target</varname> above.</para></listitem>
+          <listitem><para>Highlight important log messages. See <varname>systemd.log_color</varname> above.
+          </para></listitem>
         </varlistentry>
 
         <varlistentry>
         </varlistentry>
 
         <varlistentry>
-          <term><option>--log-color</option></term>
+          <term><option>--log-location</option></term>
 
-          <listitem><para>Highlight important log messages. See <varname>systemd.log_color</varname> above.
-          </para></listitem>
+          <listitem><para>Include code location in log messages. See <varname>systemd.log_location</varname>
+          above.</para></listitem>
         </varlistentry>
 
         <varlistentry>
-          <term><option>--log-location</option></term>
+          <term><option>--log-target=</option></term>
 
-          <listitem><para>Include code location in log messages. See <varname>systemd.log_location</varname>
-          above.</para></listitem>
+          <listitem><para>Set log target. See <varname>systemd.log_target</varname> above.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><option>--log-time=</option></term>
+
+          <listitem><para>Prefix messages with timestamp. See <varname>systemd.log_time</varname> above.
+          </para></listitem>
         </varlistentry>
 
         <varlistentry>
       <citerefentry><refentrytitle>systemd-notify</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>daemon</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>sd-daemon</refentrytitle><manvolnum>3</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>org.freedesktop.systemd1</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
-      <citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
       <citerefentry project='die-net'><refentrytitle>pkg-config</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>kernel-command-line</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
       <citerefentry project='man-pages'><refentrytitle>bootup</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
index f0e0f2bedc1606f38de928da2fa5ed54b0bc64ef..38a95d6e1fcdc280f241dcfdd876a0c572a622ef 100644 (file)
@@ -234,61 +234,49 @@ r     -        500-900
   <refsect1>
     <title>Specifiers</title>
 
-    <para>Specifiers can be used in the "Name", "ID", "GECOS", "Home directory", and "Shell" fields.
-    An unknown or unresolvable specifier is treated as invalid configuration.
-    The following expansions are understood:</para>
-      <table>
-        <title>Specifiers available</title>
-        <tgroup cols='3' align='left' colsep='1' rowsep='1'>
-          <colspec colname="spec" />
-          <colspec colname="mean" />
-          <colspec colname="detail" />
-          <thead>
-            <row>
-              <entry>Specifier</entry>
-              <entry>Meaning</entry>
-              <entry>Details</entry>
-            </row>
-          </thead>
-          <tbody>
-            <row>
-              <entry><literal>%b</literal></entry>
-              <entry>Boot ID</entry>
-              <entry>The boot ID of the running system, formatted as string. See <citerefentry><refentrytitle>random</refentrytitle><manvolnum>4</manvolnum></citerefentry> for more information.</entry>
-            </row>
-            <row>
-              <entry><literal>%H</literal></entry>
-              <entry>Host name</entry>
-              <entry>The hostname of the running system.</entry>
-            </row>
-            <row>
-              <entry><literal>%m</literal></entry>
-              <entry>Machine ID</entry>
-              <entry>The machine ID of the running system, formatted as string. See <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry> for more information.</entry>
-            </row>
-            <row>
-              <entry><literal>%T</literal></entry>
-              <entry>Directory for temporary files</entry>
-              <entry>This is either <filename>/tmp</filename> or the path <literal>$TMPDIR</literal>, <literal>$TEMP</literal> or <literal>$TMP</literal> are set to.</entry>
-            </row>
-            <row>
-              <entry><literal>%v</literal></entry>
-              <entry>Kernel release</entry>
-              <entry>Identical to <command>uname -r</command> output.</entry>
-            </row>
-            <row>
-              <entry><literal>%V</literal></entry>
-              <entry>Directory for larger and persistent temporary files</entry>
-              <entry>This is either <filename>/var/tmp</filename> or the path <literal>$TMPDIR</literal>, <literal>$TEMP</literal> or <literal>$TMP</literal> are set to.</entry>
-            </row>
-            <row>
-              <entry><literal>%%</literal></entry>
-              <entry>Escaped <literal>%</literal></entry>
-              <entry>Single percent sign.</entry>
-            </row>
-          </tbody>
-        </tgroup>
-      </table>
+    <para>Specifiers can be used in the <literal>Name</literal>, <literal>ID</literal>,
+    <literal>GECOS</literal>, <literal>Home directory</literal>, and <literal>Shell</literal> fields. An
+    unknown or unresolvable specifier is treated as invalid configuration. The following expansions are
+    understood:</para>
+
+    <table class='specifiers'>
+      <title>Specifiers available</title>
+      <tgroup cols='3' align='left' colsep='1' rowsep='1'>
+        <colspec colname="spec" />
+        <colspec colname="mean" />
+        <colspec colname="detail" />
+        <thead>
+          <row>
+            <entry>Specifier</entry>
+            <entry>Meaning</entry>
+            <entry>Details</entry>
+          </row>
+        </thead>
+        <tbody>
+          <xi:include href="standard-specifiers.xml" xpointer="a"/>
+          <xi:include href="standard-specifiers.xml" xpointer="b"/>
+          <xi:include href="standard-specifiers.xml" xpointer="B"/>
+          <xi:include href="standard-specifiers.xml" xpointer="H"/>
+          <xi:include href="standard-specifiers.xml" xpointer="l"/>
+          <xi:include href="standard-specifiers.xml" xpointer="m"/>
+          <xi:include href="standard-specifiers.xml" xpointer="o"/>
+          <row>
+            <entry><literal>%T</literal></entry>
+            <entry>Directory for temporary files</entry>
+            <entry>This is either <filename>/tmp</filename> or the path <literal>$TMPDIR</literal>, <literal>$TEMP</literal> or <literal>$TMP</literal> are set to.</entry>
+          </row>
+          <xi:include href="standard-specifiers.xml" xpointer="v"/>
+          <row>
+            <entry><literal>%V</literal></entry>
+            <entry>Directory for larger and persistent temporary files</entry>
+            <entry>This is either <filename>/var/tmp</filename> or the path <literal>$TMPDIR</literal>, <literal>$TEMP</literal> or <literal>$TMP</literal> are set to.</entry>
+          </row>
+          <xi:include href="standard-specifiers.xml" xpointer="w"/>
+          <xi:include href="standard-specifiers.xml" xpointer="W"/>
+          <xi:include href="standard-specifiers.xml" xpointer="percent"/>
+        </tbody>
+      </tgroup>
+    </table>
   </refsect1>
 
   <refsect1>
diff --git a/man/tc.xml b/man/tc.xml
new file mode 100644 (file)
index 0000000..f312ac2
--- /dev/null
@@ -0,0 +1,48 @@
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
+
+<!--
+  SPDX-License-Identifier: LGPL-2.1+
+-->
+
+<refsect1>
+  <variablelist class='network-directives'>
+    <varlistentry id='qdisc-parent'>
+      <term><varname>Parent=</varname></term>
+      <listitem>
+        <para>Configures the parent Queueing Discipline (qdisc). Takes one of <literal>root</literal>,
+        <literal>clsact</literal>, <literal>ingress</literal> or a class identifier. The class identifier is
+        specified as the major and minor numbers in hexadecimal in the range 0x1–Oxffff separated with a
+        colon (<literal>major:minor</literal>). Defaults to <literal>root</literal>.</para>
+      </listitem>
+    </varlistentry>
+
+    <varlistentry id='qdisc-handle'>
+      <term><varname>Handle=</varname></term>
+      <listitem>
+        <para>Configures the major number of unique identifier of the qdisc, known as the handle.
+        Takes a hexadecimal number in the range 0x1–0xffff. Defaults to unset.</para>
+      </listitem>
+    </varlistentry>
+
+    <varlistentry id='tclass-parent'>
+      <term><varname>Parent=</varname></term>
+      <listitem>
+        <para>Configures the parent Queueing Discipline (qdisc). Takes one of <literal>root</literal>, or a
+        qdisc identifier. The qdisc identifier is specified as the major and minor numbers in hexadecimal in
+        the range 0x1–Oxffff separated with a colon (<literal>major:minor</literal>). Defaults to
+        <literal>root</literal>.
+        </para>
+      </listitem>
+    </varlistentry>
+
+    <varlistentry id='tclass-classid'>
+      <term><varname>ClassId=</varname></term>
+      <listitem>
+        <para>Configues the unique identifier of the class. It is specified as the major and minor numbers in
+        hexadecimal in the range 0x1–Oxffff separated with a colon (<literal>major:minor</literal>).
+        Defaults to unset.</para>
+      </listitem>
+    </varlistentry>
+  </variablelist>
+</refsect1>
index c64d015400cdb67ed017fee25b194fd5f65e9589..c1a37ec837c771bea4439779cfdaa0335e1053d8 100644 (file)
@@ -32,7 +32,7 @@
     <title>Description</title>
 
     <para>These configuration files control NTP network time synchronization. See
-    <citerefentry><refentrytitle>systemd.syntax</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    <citerefentry><refentrytitle>systemd.syntax</refentrytitle><manvolnum>7</manvolnum></citerefentry>
     for a general description of the syntax.</para>
   </refsect1>
 
@@ -41,7 +41,7 @@
   <refsect1>
     <title>Options</title>
 
-    <para>The following settings are configured in the <literal>[Time]</literal> section:</para>
+    <para>The following settings are configured in the [Time] section:</para>
 
     <variablelist class='network-directives'>
 
index 426df6cb4083d73d1fd66934e7138497adcfc2be..b9e9eee96c8a7330f1855238941cb149113257d0 100644 (file)
@@ -6,7 +6,8 @@
 
   Copyright © 2010 Brandon Philips
 -->
-<refentry id="tmpfiles.d">
+<refentry id="tmpfiles.d"
+          xmlns:xi="http://www.w3.org/2001/XInclude">
 
   <refentryinfo>
     <title>tmpfiles.d</title>
@@ -616,7 +617,7 @@ w- /proc/sys/vm/swappiness - - - - 10</programlisting></para>
     <para>Specifiers can be used in the "path" and "argument" fields.
     An unknown or unresolvable specifier is treated as invalid configuration.
     The following expansions are understood:</para>
-      <table>
+      <table class='specifiers'>
         <title>Specifiers available</title>
         <tgroup cols='3' align='left' colsep='1' rowsep='1'>
           <colspec colname="spec" />
@@ -630,11 +631,9 @@ w- /proc/sys/vm/swappiness - - - - 10</programlisting></para>
             </row>
           </thead>
           <tbody>
-            <row>
-              <entry><literal>%b</literal></entry>
-              <entry>Boot ID</entry>
-              <entry>The boot ID of the running system, formatted as string. See <citerefentry><refentrytitle>random</refentrytitle><manvolnum>4</manvolnum></citerefentry> for more information.</entry>
-            </row>
+            <xi:include href="standard-specifiers.xml" xpointer="a"/>
+            <xi:include href="standard-specifiers.xml" xpointer="b"/>
+            <xi:include href="standard-specifiers.xml" xpointer="B"/>
             <row>
               <entry><literal>%C</literal></entry>
               <entry>System or user cache directory</entry>
@@ -645,21 +644,15 @@ w- /proc/sys/vm/swappiness - - - - 10</programlisting></para>
               <entry>User home directory</entry>
               <entry>This is the home directory of the user running the command. In case of the system instance this resolves to <literal>/root</literal>.</entry>
             </row>
-            <row>
-              <entry><literal>%H</literal></entry>
-              <entry>Host name</entry>
-              <entry>The hostname of the running system.</entry>
-            </row>
+            <xi:include href="standard-specifiers.xml" xpointer="H"/>
+            <xi:include href="standard-specifiers.xml" xpointer="l"/>
             <row>
               <entry><literal>%L</literal></entry>
               <entry>System or user log directory</entry>
               <entry>In <option>--user</option> mode, this is the same as <varname>$XDG_CONFIG_HOME</varname> with <filename index="false">/log</filename> appended, and <filename>/var/log</filename> otherwise.</entry>
             </row>
-            <row>
-              <entry><literal>%m</literal></entry>
-              <entry>Machine ID</entry>
-              <entry>The machine ID of the running system, formatted as string. See <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry> for more information.</entry>
-            </row>
+            <xi:include href="standard-specifiers.xml" xpointer="m"/>
+            <xi:include href="standard-specifiers.xml" xpointer="o"/>
             <row>
               <entry><literal>%S</literal></entry>
               <entry>System or user state directory</entry>
@@ -695,21 +688,15 @@ w- /proc/sys/vm/swappiness - - - - 10</programlisting></para>
               <entry>User UID</entry>
               <entry>This is the numeric UID of the user running the command. In case of the system instance this resolves to <constant>0</constant>.</entry>
             </row>
-            <row>
-              <entry><literal>%v</literal></entry>
-              <entry>Kernel release</entry>
-              <entry>Identical to <command>uname -r</command> output.</entry>
-            </row>
+            <xi:include href="standard-specifiers.xml" xpointer="v"/>
             <row>
               <entry><literal>%V</literal></entry>
               <entry>Directory for larger and persistent temporary files</entry>
               <entry>This is either <filename>/var/tmp</filename> or the path <literal>$TMPDIR</literal>, <literal>$TEMP</literal> or <literal>$TMP</literal> are set to.</entry>
             </row>
-            <row>
-              <entry><literal>%%</literal></entry>
-              <entry>Escaped <literal>%</literal></entry>
-              <entry>Single percent sign.</entry>
-            </row>
+            <xi:include href="standard-specifiers.xml" xpointer="w"/>
+            <xi:include href="standard-specifiers.xml" xpointer="W"/>
+            <xi:include href="standard-specifiers.xml" xpointer="percent"/>
           </tbody>
         </tgroup>
       </table>
index 04301c81ab080595a1605c5a222b4759bc3c80f2..0b5669b4425b8dfcbc0e14c0a320c351151ef058 100644 (file)
           <para>This is the same as the <option>--resolve-names=</option> option.</para>
         </listitem>
       </varlistentry>
+
+      <varlistentry>
+        <term><varname>timeout_signal=</varname></term>
+
+        <listitem>
+          <para>Specifies a signal that <filename>systemd-udevd</filename> will send on worker
+          timeouts. Note that both workers and spawned processes will be killed using this
+          signal. Defaults to <option>SIGKILL</option>.</para>
+        </listitem>
+      </varlistentry>
     </variablelist>
 
     <para>
index f4603df073965abc39862972938da4a04ef7c99d..b9d1461404319e818a44ded815211efa2ea19888 100644 (file)
   <refnamediv>
     <refname>user@.service</refname>
     <refname>user-runtime-dir@.service</refname>
-    <refpurpose>System units to manage user processes</refpurpose>
+    <refname>systemd-user-runtime-dir</refname>
+    <refpurpose>System units to start the user manager</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
     <para><filename>user@<replaceable>UID</replaceable>.service</filename></para>
     <para><filename>user-runtime-dir@<replaceable>UID</replaceable>.service</filename></para>
+    <para><filename>/usr/lib/systemd/systemd-user-runtime-dir</filename></para>
     <para><filename>user-<replaceable>UID</replaceable>.slice</filename></para>
   </refsynopsisdiv>
 
   <refsect1>
     <title>Description</title>
 
-    <para>The
-    <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+    <para>The <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>
     system manager (PID 1) starts user manager instances as
-    <filename>user@<replaceable>UID</replaceable>.service</filename>, where the user's numerical UID
-    is used as the instance identifier. Each <command>systemd --user</command> instance manages a
-    hierarchy of its own units. See
-    <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
-    a discussion of systemd units and
-    <citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>1</manvolnum></citerefentry>
-    for a list of units that form the basis of the unit hierarchies of system and user units.</para>
+    <filename>user@<replaceable>UID</replaceable>.service</filename>, with the user's numerical UID used as
+    the instance identifier. These instances use the same executable as the system manager, but running in a
+    mode where it starts a different set of units. Each <command>systemd --user</command> instance manages a
+    hierarchy of units specific to that user. See
+    <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry> for a
+    discussion of units and
+    <citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>7</manvolnum></citerefentry> for a
+    list of units that form the basis of the unit hierarchies of system and user units.</para>
 
     <para><filename>user@<replaceable>UID</replaceable>.service</filename> is accompanied by the
     system unit <filename>user-runtime-dir@<replaceable>UID</replaceable>.service</filename>, which
     creates the user's runtime directory
     <filename>/run/user/<replaceable>UID</replaceable></filename>, and then removes it when this
-    unit is stopped.</para>
+    unit is stopped. <filename>user-runtime-dir@<replaceable>UID</replaceable>.service</filename>
+    executes the <filename>systemd-user-runtime-dir</filename> binary to do the actual work.</para>
 
     <para>User processes may be started by the <filename>user@.service</filename> instance, in which
     case they will be part of that unit in the system hierarchy. They may also be started elsewhere,
     display manager like <command>gdm</command>, in which case they form a .scope unit (see
     <citerefentry><refentrytitle>systemd.scope</refentrytitle><manvolnum>5</manvolnum></citerefentry>).
     Both <filename>user@<replaceable>UID</replaceable>.service</filename> and the scope units are
-    collected under a <filename>user-<replaceable>UID</replaceable>.slice</filename>.</para>
+    collected under the <filename>user-<replaceable>UID</replaceable>.slice</filename>.</para>
 
     <para>Individual <filename>user-<replaceable>UID</replaceable>.slice</filename> slices are
     collected under <filename>user.slice</filename>, see
-    <citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
+    <citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
     </para>
   </refsect1>
 
index 606ce673b5645d8ba33a12e8444b8d4702111d93..9a69f33edbd93b0a1ed3d17fe9b585853c2e5839 100644 (file)
     <title>Well-Known Services</title>
 
     <para>The <command>userdbctl services</command> command will list all currently running services that
-    provide user or group definitions to the system. The following are well-known services are shown among
-    this list.</para>
+    provide user or group definitions to the system. The following well-known services are shown among
+    this list:</para>
 
     <variablelist>
-
       <varlistentry>
         <term><constant>io.systemd.DynamicUser</constant></term>
 
         available to the system.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><constant>io.systemd.Machine</constant></term>
+
+        <listitem><para>This service is provided by
+        <citerefentry><refentrytitle>systemd-machined.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+        and synthesizes records for all users/groups used by a container that employs user
+        namespacing.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><constant>io.systemd.Multiplexer</constant></term>
 
 
     <para>Note that <command>userdbctl</command> has internal support for NSS-based lookups too. This means
     that if neither <constant>io.systemd.Multiplexer</constant> nor
-    <constant>io.systemd.NameSeviceSwitch</constant> are running look-ups into the the basic user/group
+    <constant>io.systemd.NameSeviceSwitch</constant> are running look-ups into the basic user/group
     databases will still work.</para>
   </refsect1>
 
index 98c20eec5227d191fe974a1728dc250725900493..dede12befdfbc83a24b01dd45085fe28430967a3 100644 (file)
@@ -27,6 +27,30 @@ static const sd_bus_vtable vtable[] = {
             "s", SD_BUS_PARAM(returnstring),
             method, offsetof(object, number),
             SD_BUS_VTABLE_DEPRECATED),
+        SD_BUS_METHOD_WITH_ARGS_OFFSET(
+            "Method3",
+            SD_BUS_ARGS("s", string, "o", path),
+            SD_BUS_RESULT("s", returnstring),
+            method, offsetof(object, number),
+            SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_ARGS(
+            "Method4",
+            SD_BUS_NO_ARGS,
+            SD_BUS_NO_RESULT,
+            method,
+            SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_SIGNAL(
+            "Signal1",
+            "so",
+            0),
+        SD_BUS_SIGNAL_WITH_NAMES(
+            "Signal2",
+            "so", SD_BUS_PARAM(string) SD_BUS_PARAM(path),
+            0),
+        SD_BUS_SIGNAL_WITH_ARGS(
+            "Signal3",
+            SD_BUS_ARGS("s", string, "o", path),
+            0),
         SD_BUS_WRITABLE_PROPERTY(
             "AutomaticStringProperty", "s", NULL, NULL,
             offsetof(object, name),
index 9e65bae6211df599afd25fa43e59fa39e8685f07..651246d6a1e464ffb5e38d593bcdd6be19a2d247 100644 (file)
@@ -1,4 +1,4 @@
-# Make sure noone can read the files we generate but us
+# Make sure no one can read the files we generate but us
 umask 077
 
 # Destroy any old key on the Yubikey (careful!)
@@ -23,8 +23,9 @@ dd if=/dev/urandom of=plaintext.bin bs=128 count=1
 base64 < plaintext.bin | tr -d '\n\r\t ' > plaintext.base64
 
 # Encrypt this newly generated (binary) LUKS decryption key using the public key whose private key is on the
-# Yubikey, store the result in /etc/encrypted-luks-key.bin, where we'll look for it during boot.
-sudo openssl rsautl -encrypt -pubin -inkey pubkey.pem -in plaintext.bin -out /etc/encrypted-luks-key.bin
+# Yubikey, store the result in /etc/cryptsetup-keys.d/mytest.key, where we'll look for it during boot.
+mkdir -p /etc/cryptsetup-keys.d
+sudo openssl rsautl -encrypt -pubin -inkey pubkey.pem -in plaintext.bin -out /etc/cryptsetup-keys.d/mytest.key
 
 # Configure the LUKS decryption key on the LUKS device. We use very low pbkdf settings since the key already
 # has quite a high quality (it comes directly from /dev/urandom after all), and thus we don't need to do much
@@ -40,8 +41,10 @@ shred -u plaintext.bin plaintext.base64
 rm pubkey.pem
 
 # Test: Let's run systemd-cryptsetup to test if this all worked. The option string should contain the full
-# PKCS#11 URI we have in the clipboard, it tells the tool how to decypher the encrypted LUKS key.
-sudo systemd-cryptsetup attach mytest /dev/sdXn /etc/encrypted-luks-key.bin 'pkcs11-uri=pkcs11:…'
+# PKCS#11 URI we have in the clipboard; it tells the tool how to decipher the encrypted LUKS key. Note that
+# systemd-cryptsetup automatically searches for the encrypted key in /etc/cryptsetup-keys.d/, hence we do
+# not need to specify the key file path explicitly here.
+sudo systemd-cryptsetup attach mytest /dev/sdXn - 'pkcs11-uri=pkcs11:…'
 
 # If that worked, let's now add the same line persistently to /etc/crypttab, for the future.
-sudo bash -c 'echo "mytest /dev/sdXn /etc/encrypted-luks-key \'pkcs11-uri=pkcs11:…\'" >> /etc/crypttab'
+sudo bash -c 'echo "mytest /dev/sdXn - \'pkcs11-uri=pkcs11:…\'" >> /etc/crypttab'
index c09115e06ab9e9590402d6a0073909084d1eba08..dbbddb68e23283596da04b94f0d3b596d539d96c 100644 (file)
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: LGPL-2.1+
 
 project('systemd', 'c',
-        version : '245',
+        version : '246',
         license : 'LGPLv2+',
         default_options: [
                 'c_std=gnu99',
@@ -13,8 +13,8 @@ project('systemd', 'c',
         meson_version : '>= 0.46',
        )
 
-libsystemd_version = '0.28.0'
-libudev_version = '1.6.17'
+libsystemd_version = '0.29.0'
+libudev_version = '1.6.18'
 
 # We need the same data in two different formats, ugh!
 # Also, for hysterical reasons, we use different variable
@@ -32,27 +32,30 @@ substs.set('PROJECT_VERSION',      meson.project_version(),
 # This is to be used instead of meson.source_root(), as the latter will return
 # the wrong result when systemd is being built as a meson subproject
 project_source_root = meson.current_source_dir()
+project_build_root = meson.current_build_dir()
 relative_source_path = run_command('realpath',
-                                   '--relative-to=@0@'.format(meson.current_build_dir()),
+                                   '--relative-to=@0@'.format(project_build_root),
                                    project_source_root).stdout().strip()
 conf.set_quoted('RELATIVE_SOURCE_PATH', relative_source_path)
 
 want_ossfuzz = get_option('oss-fuzz')
 want_libfuzzer = get_option('llvm-fuzz')
-want_fuzzbuzz = get_option('fuzzbuzz')
-if want_ossfuzz + want_libfuzzer + want_fuzzbuzz > 1
-        error('only one of oss-fuzz, llvm-fuzz or fuzzbuzz can be specified')
+if want_ossfuzz + want_libfuzzer > 1
+        error('only one of oss-fuzz or llvm-fuzz can be specified')
 endif
 
 skip_deps = want_ossfuzz or want_libfuzzer
-fuzzer_build = want_ossfuzz or want_libfuzzer or want_fuzzbuzz
+fuzzer_build = want_ossfuzz or want_libfuzzer
 
 #####################################################################
 
 # Try to install the git pre-commit hook
-git_hook = run_command(join_paths(project_source_root, 'tools/add-git-hook.sh'))
-if git_hook.returncode() == 0
-        message(git_hook.stdout().strip())
+add_git_hook_sh = find_program('tools/add-git-hook.sh', required : false)
+if add_git_hook_sh.found()
+        git_hook = run_command(add_git_hook_sh)
+        if git_hook.returncode() == 0
+                message(git_hook.stdout().strip())
+        endif
 endif
 
 #####################################################################
@@ -82,11 +85,17 @@ if rootprefixdir == ''
 endif
 rootprefixdir_noslash = rootprefixdir == '/' ? '' : rootprefixdir
 
+have_standalone_binaries = get_option('standalone-binaries')
+
 sysvinit_path = get_option('sysvinit-path')
 sysvrcnd_path = get_option('sysvrcnd-path')
 conf.set10('HAVE_SYSV_COMPAT', sysvinit_path != '' and sysvrcnd_path != '',
            description : 'SysV init scripts and rcN.d links are supported')
 
+if get_option('hibernate') and not get_option('initrd')
+        error('hibernate depends on initrd')
+endif
+
 conf.set10('BUMP_PROC_SYS_FS_FILE_MAX', get_option('bump-proc-sys-fs-file-max'))
 conf.set10('BUMP_PROC_SYS_FS_NR_OPEN',  get_option('bump-proc-sys-fs-nr-open'))
 conf.set('HIGH_RLIMIT_NOFILE',          512*1024)
@@ -196,7 +205,7 @@ memory_accounting_default = get_option('memory-accounting-default')
 status_unit_format_default = get_option('status-unit-format-default')
 
 conf.set_quoted('PKGSYSCONFDIR',                              pkgsysconfdir)
-conf.set_quoted('SYSTEM_CONFIG_UNIT_PATH',                    join_paths(pkgsysconfdir, 'system'))
+conf.set_quoted('SYSTEM_CONFIG_UNIT_DIR',                     join_paths(pkgsysconfdir, 'system'))
 conf.set_quoted('SYSTEM_DATA_UNIT_PATH',                      systemunitdir)
 conf.set_quoted('SYSTEM_SYSVINIT_PATH',                       sysvinit_path)
 conf.set_quoted('SYSTEM_SYSVRCND_PATH',                       sysvrcnd_path)
@@ -204,8 +213,8 @@ conf.set_quoted('RC_LOCAL_SCRIPT_PATH_START',                 get_option('rc-loc
 
 conf.set('ANSI_OK_COLOR',                                     'ANSI_' + get_option('ok-color').underscorify().to_upper())
 
-conf.set_quoted('USER_CONFIG_UNIT_PATH',                      join_paths(pkgsysconfdir, 'user'))
-conf.set_quoted('USER_DATA_UNIT_PATH',                        userunitdir)
+conf.set_quoted('USER_CONFIG_UNIT_DIR',                       join_paths(pkgsysconfdir, 'user'))
+conf.set_quoted('USER_DATA_UNIT_DIR',                         userunitdir)
 conf.set_quoted('CERTIFICATE_ROOT',                           get_option('certificate-root'))
 conf.set_quoted('CATALOG_DATABASE',                           join_paths(catalogstatedir, 'database'))
 conf.set_quoted('SYSTEMD_CGROUP_AGENT_PATH',                  join_paths(rootlibexecdir, 'systemd-cgroups-agent'))
@@ -214,7 +223,6 @@ conf.set_quoted('SYSTEMD_FSCK_PATH',                          join_paths(rootlib
 conf.set_quoted('SYSTEMD_MAKEFS_PATH',                        join_paths(rootlibexecdir, 'systemd-makefs'))
 conf.set_quoted('SYSTEMD_GROWFS_PATH',                        join_paths(rootlibexecdir, 'systemd-growfs'))
 conf.set_quoted('SYSTEMD_SHUTDOWN_BINARY_PATH',               join_paths(rootlibexecdir, 'systemd-shutdown'))
-conf.set_quoted('SYSTEMD_SLEEP_BINARY_PATH',                  join_paths(rootlibexecdir, 'systemd-sleep'))
 conf.set_quoted('SYSTEMCTL_BINARY_PATH',                      join_paths(rootbindir, 'systemctl'))
 conf.set_quoted('SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH', join_paths(rootbindir, 'systemd-tty-ask-password-agent'))
 conf.set_quoted('SYSTEMD_STDIO_BRIDGE_BINARY_PATH',           join_paths(bindir, 'systemd-stdio-bridge'))
@@ -222,10 +230,10 @@ conf.set_quoted('ROOTPREFIX',                                 rootprefixdir)
 conf.set_quoted('RANDOM_SEED_DIR',                            randomseeddir)
 conf.set_quoted('RANDOM_SEED',                                join_paths(randomseeddir, 'random-seed'))
 conf.set_quoted('SYSTEMD_CRYPTSETUP_PATH',                    join_paths(rootlibexecdir, 'systemd-cryptsetup'))
-conf.set_quoted('SYSTEM_GENERATOR_PATH',                      systemgeneratordir)
-conf.set_quoted('USER_GENERATOR_PATH',                        usergeneratordir)
-conf.set_quoted('SYSTEM_ENV_GENERATOR_PATH',                  systemenvgeneratordir)
-conf.set_quoted('USER_ENV_GENERATOR_PATH',                    userenvgeneratordir)
+conf.set_quoted('SYSTEM_GENERATOR_DIR',                       systemgeneratordir)
+conf.set_quoted('USER_GENERATOR_DIR',                         usergeneratordir)
+conf.set_quoted('SYSTEM_ENV_GENERATOR_DIR',                   systemenvgeneratordir)
+conf.set_quoted('USER_ENV_GENERATOR_DIR',                     userenvgeneratordir)
 conf.set_quoted('SYSTEM_SHUTDOWN_PATH',                       systemshutdowndir)
 conf.set_quoted('SYSTEM_SLEEP_PATH',                          systemsleepdir)
 conf.set_quoted('SYSTEMD_KBD_MODEL_MAP',                      join_paths(pkgdatadir, 'kbd-model-map'))
@@ -291,17 +299,18 @@ substs.set('RC_LOCAL_SCRIPT_PATH_START',                      get_option('rc-loc
 substs.set('MEMORY_ACCOUNTING_DEFAULT',                       memory_accounting_default ? 'yes' : 'no')
 substs.set('STATUS_UNIT_FORMAT_DEFAULT',                      status_unit_format_default)
 substs.set('HIGH_RLIMIT_NOFILE',                              conf.get('HIGH_RLIMIT_NOFILE'))
-substs.set('BUILD_ROOT',                                      meson.current_build_dir())
+substs.set('BUILD_ROOT',                                      project_build_root)
 
 #####################################################################
 
 cc = meson.get_compiler('c')
 pkgconfig = import('pkgconfig')
-check_compilation_sh = find_program('tools/meson-check-compilation.sh')
+check_compilation_sh = find_program('tools/check-compilation.sh')
 meson_build_sh = find_program('tools/meson-build.sh')
 
 want_tests = get_option('tests')
 slow_tests = want_tests != 'false' and get_option('slow-tests')
+fuzz_tests = want_tests != 'false' and get_option('fuzz-tests')
 install_tests = get_option('install-tests')
 
 if add_languages('cpp', required : fuzzer_build)
@@ -323,8 +332,6 @@ if want_libfuzzer
         endif
 elif want_ossfuzz
         fuzzing_engine = meson.get_compiler('cpp').find_library('FuzzingEngine')
-elif want_fuzzbuzz
-        fuzzing_engine = meson.get_compiler('cpp').find_library(get_option('fuzzbuzz-engine'), dirs: get_option('fuzzbuzz-engine-dir'))
 endif
 
 # Those generate many false positives, and we do not want to change the code to
@@ -335,15 +342,6 @@ basic_disabled_warnings = [
         '-Wno-unused-result',
         '-Wno-format-signedness',
 ]
-if get_option('b_ndebug') == 'true'
-        # With asserts disabled with get a bunch of warnings about variables which
-        # are used only in the asserts. This is not useful at all, so let's just silence
-        # those warnings.
-        basic_disabled_warnings += [
-                '-Wno-unused-variable',
-                '-Wno-unused-but-set-variable',
-        ]
-endif
 
 possible_cc_flags = [
         '-Werror=undef',
@@ -378,9 +376,6 @@ possible_cc_flags = [
         '-Wno-error=#warnings',  # clang
         '-Wno-string-plus-int',  # clang
 
-        # work-around for gcc 7.1 turning this on on its own.
-        '-Wno-error=nonnull',
-
         # Disable -Wmaybe-uninitialized, since it's noisy on gcc 8 with
         # optimizations enabled, producing essentially false positives.
         '-Wno-maybe-uninitialized',
@@ -654,7 +649,14 @@ endforeach
 
 ############################################################
 
-conf.set_quoted('FALLBACK_HOSTNAME', get_option('fallback-hostname'))
+fallback_hostname = get_option('fallback-hostname')
+if fallback_hostname == '' or fallback_hostname[0] == '.' or fallback_hostname[0] == '-'
+        error('Invalid fallback-hostname configuration')
+        # A more extensive test is done in test-hostname-util. Let's catch
+        # the most obvious errors here so we don't fail with an assert later.
+endif
+conf.set_quoted('FALLBACK_HOSTNAME', fallback_hostname)
+
 conf.set10('ENABLE_COMPAT_GATEWAY_HOSTNAME', get_option('compat-gateway-hostname'))
 gateway_hostnames = ['_gateway'] + (conf.get('ENABLE_COMPAT_GATEWAY_HOSTNAME') == 1 ? ['gateway'] : [])
 
@@ -674,13 +676,17 @@ conf.set_quoted('DEFAULT_NET_NAMING_SCHEME', default_net_naming_scheme)
 
 time_epoch = get_option('time-epoch')
 if time_epoch == -1
-        source_date_epoch = run_command('sh', ['-c', 'echo "$SOURCE_DATE_EPOCH"']).stdout().strip()
-        if source_date_epoch != ''
-                time_epoch = source_date_epoch.to_int()
-        else
+        time_epoch = run_command('sh', ['-c', 'echo "$SOURCE_DATE_EPOCH"']).stdout().strip()
+        if time_epoch == '' and git.found() and run_command('test', '-e', '.git').returncode() == 0
+                # If we're in a git repository, use the creation time of the latest git tag.
+                latest_tag = run_command('git', 'describe', '--abbrev=0', '--tags').stdout().strip()
+                time_epoch = run_command('git', 'log', '-1', '--format=%at', latest_tag).stdout()
+        endif
+        if time_epoch == ''
                 NEWS = files('NEWS')
-                time_epoch = run_command(stat, '-c', '%Y', NEWS).stdout().to_int()
+                time_epoch = run_command(stat, '-c', '%Y', NEWS).stdout()
         endif
+        time_epoch = time_epoch.to_int()
 endif
 conf.set('TIME_EPOCH', time_epoch)
 
@@ -929,6 +935,7 @@ conf.set10('HAVE_SELINUX', have)
 want_apparmor = get_option('apparmor')
 if want_apparmor != 'false' and not skip_deps
         libapparmor = dependency('libapparmor',
+                                 version : '>= 2.13',
                                  required : want_apparmor == 'true')
         have = libapparmor.found()
 else
@@ -1032,6 +1039,8 @@ if want_libcryptsetup != 'false' and not skip_deps
 
         conf.set10('HAVE_CRYPT_SET_METADATA_SIZE',
                    have and cc.has_function('crypt_set_metadata_size', dependencies : libcryptsetup))
+        conf.set10('HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY',
+                   have and cc.has_function('crypt_activate_by_signed_key', dependencies : libcryptsetup))
 else
         have = false
         libcryptsetup = []
@@ -1139,8 +1148,8 @@ conf.set10('HAVE_OPENSSL', have)
 want_p11kit = get_option('p11kit')
 if want_p11kit != 'false' and not skip_deps
         libp11kit = dependency('p11-kit-1',
-                                version : '>= 0.23.3',
-                                required : want_p11kit == 'true')
+                               version : '>= 0.23.3',
+                               required : want_p11kit == 'true')
         have = libp11kit.found()
 else
         have = false
@@ -1148,6 +1157,17 @@ else
 endif
 conf.set10('HAVE_P11KIT', have)
 
+want_libfido2 = get_option('libfido2')
+if want_libfido2 != 'false' and not skip_deps
+        libfido2 = dependency('libfido2',
+                              required : want_libfido2 == 'true')
+        have = libfido2.found()
+else
+        have = false
+        libfido2 = []
+endif
+conf.set10('HAVE_LIBFIDO2', have)
+
 want_elfutils = get_option('elfutils')
 if want_elfutils != 'false' and not skip_deps
         libdw = dependency('libdw',
@@ -1185,24 +1205,38 @@ want_xz = get_option('xz')
 if want_xz != 'false' and not skip_deps
         libxz = dependency('liblzma',
                            required : want_xz == 'true')
-        have = libxz.found()
+        have_xz = libxz.found()
 else
-        have = false
+        have_xz = false
         libxz = []
 endif
-conf.set10('HAVE_XZ', have)
+conf.set10('HAVE_XZ', have_xz)
 
 want_lz4 = get_option('lz4')
 if want_lz4 != 'false' and not skip_deps
         liblz4 = dependency('liblz4',
                             version : '>= 1.3.0',
                             required : want_lz4 == 'true')
-        have = liblz4.found()
+        have_lz4 = liblz4.found()
 else
-        have = false
+        have_lz4 = false
         liblz4 = []
 endif
-conf.set10('HAVE_LZ4', have)
+conf.set10('HAVE_LZ4', have_lz4)
+
+want_zstd = get_option('zstd')
+if want_zstd != 'false' and not skip_deps
+        libzstd = dependency('libzstd',
+                             required : want_zstd == 'true',
+                             version : '>= 1.4.0')
+        have_zstd = libzstd.found()
+else
+        have_zstd = false
+        libzstd = []
+endif
+conf.set10('HAVE_ZSTD', have_zstd)
+
+conf.set10('HAVE_COMPRESSION', have_xz or have_lz4 or have_zstd)
 
 want_xkbcommon = get_option('xkbcommon')
 if want_xkbcommon != 'false' and not skip_deps
@@ -1311,6 +1345,16 @@ conf.set('DEFAULT_DNS_OVER_TLS_MODE',
          'DNS_OVER_TLS_' + default_dns_over_tls.underscorify().to_upper())
 substs.set('DEFAULT_DNS_OVER_TLS_MODE', default_dns_over_tls)
 
+default_mdns = get_option('default-mdns')
+conf.set('DEFAULT_MDNS_MODE',
+         'RESOLVE_SUPPORT_' + default_mdns.to_upper())
+substs.set('DEFAULT_MDNS_MODE', default_mdns)
+
+default_llmnr = get_option('default-llmnr')
+conf.set('DEFAULT_LLMNR_MODE',
+         'RESOLVE_SUPPORT_' + default_llmnr.to_upper())
+substs.set('DEFAULT_LLMNR_MODE', default_llmnr)
+
 want_repart = get_option('repart')
 if want_repart != 'false'
         have = (conf.get('HAVE_OPENSSL') == 1 and
@@ -1395,6 +1439,7 @@ foreach term : ['utmp',
                 'tmpfiles',
                 'hwdb',
                 'rfkill',
+                'xdg-autostart',
                 'ldconfig',
                 'efi',
                 'tpm',
@@ -1402,6 +1447,7 @@ foreach term : ['utmp',
                 'smack',
                 'gshadow',
                 'idn',
+                'initrd',
                 'nss-myhostname',
                 'nss-systemd']
         have = get_option(term)
@@ -1483,6 +1529,7 @@ meson_apply_m4 = find_program('tools/meson-apply-m4.sh')
 
 includes = include_directories('src/basic',
                                'src/boot',
+                               'src/home',
                                'src/shared',
                                'src/systemd',
                                'src/journal',
@@ -1496,6 +1543,7 @@ includes = include_directories('src/basic',
                                'src/libudev',
                                'src/core',
                                'src/shutdown',
+                               'src/xdg-autostart-generator',
                                'src/libsystemd/sd-bus',
                                'src/libsystemd/sd-device',
                                'src/libsystemd/sd-event',
@@ -1542,6 +1590,7 @@ libsystemd = shared_library(
         dependencies : [threads,
                         librt,
                         libxz,
+                        libzstd,
                         liblz4],
         link_depends : libsystemd_sym,
         install : true,
@@ -1565,6 +1614,7 @@ install_libsystemd_static = static_library(
         dependencies : [threads,
                         librt,
                         libxz,
+                        libzstd,
                         liblz4,
                         libcap,
                         libblkid,
@@ -1573,7 +1623,7 @@ install_libsystemd_static = static_library(
                         libgcrypt],
         c_args : libsystemd_c_args + (static_libsystemd_pic ? [] : ['-fno-PIC']))
 
-#Generate autosuspend rules
+# Generate autosuspend rules
 make_autosuspend_rules_py = find_program('tools/make-autosuspend-rules.py')
 
 ############################################################
@@ -1606,6 +1656,7 @@ subdir('src/nspawn')
 subdir('src/resolve')
 subdir('src/timedate')
 subdir('src/timesync')
+subdir('src/tmpfiles')
 subdir('src/vconsole')
 subdir('src/boot/efi')
 
@@ -1628,7 +1679,7 @@ test_dlopen = executable(
         build_by_default : want_tests != 'false')
 
 foreach tuple : [['myhostname', 'ENABLE_NSS_MYHOSTNAME'],
-                 ['systemd',    'ENABLE_NSS_SYSTEMD', 'src/nss-systemd/userdb-glue.c src/nss-systemd/userdb-glue.h'],
+                 ['systemd',    'ENABLE_NSS_SYSTEMD', 'src/nss-systemd/userdb-glue.c src/nss-systemd/userdb-glue.h src/nss-systemd/nss-systemd.h'],
                  ['mymachines', 'ENABLE_NSS_MYMACHINES'],
                  ['resolve',    'ENABLE_NSS_RESOLVE']]
 
@@ -1680,120 +1731,129 @@ endforeach
 
 ############################################################
 
-executable('systemd',
-           systemd_sources,
-           include_directories : includes,
-           link_with : [libcore,
-                        libshared],
-           dependencies : [versiondep,
-                           threads,
-                           librt,
-                           libseccomp,
-                           libselinux,
-                           libmount,
-                           libblkid],
-           install_rpath : rootlibexecdir,
-           install : true,
-           install_dir : rootlibexecdir)
+executable(
+        'systemd',
+        systemd_sources,
+        include_directories : includes,
+        link_with : [libcore,
+                     libshared],
+        dependencies : [versiondep,
+                        threads,
+                        librt,
+                        libseccomp,
+                        libselinux,
+                        libmount,
+                        libblkid],
+        install_rpath : rootlibexecdir,
+        install : true,
+        install_dir : rootlibexecdir)
 
 meson.add_install_script(meson_make_symlink,
                          join_paths(rootlibexecdir, 'systemd'),
                          join_paths(rootsbindir, 'init'))
 
-exe = executable('systemd-analyze',
-                 systemd_analyze_sources,
-                 include_directories : includes,
-                 link_with : [libcore,
-                              libshared],
-                 dependencies : [versiondep,
-                                 threads,
-                                 librt,
-                                 libseccomp,
-                                 libselinux,
-                                 libmount,
-                                 libblkid],
-                 install_rpath : rootlibexecdir,
-                 install : true)
-public_programs += exe
-
-executable('systemd-journald',
-           systemd_journald_sources,
-           include_directories : includes,
-           link_with : [libjournal_core,
-                        libshared],
-           dependencies : [threads,
-                           libxz,
-                           liblz4,
-                           libselinux],
-           install_rpath : rootlibexecdir,
-           install : true,
-           install_dir : rootlibexecdir)
+public_programs += executable(
+        'systemd-analyze',
+        systemd_analyze_sources,
+        include_directories : includes,
+        link_with : [libcore,
+                     libshared],
+        dependencies : [versiondep,
+                        threads,
+                        librt,
+                        libseccomp,
+                        libselinux,
+                        libmount,
+                        libblkid],
+        install_rpath : rootlibexecdir,
+        install : get_option('analyze'))
 
-exe = executable('systemd-cat',
-                 systemd_cat_sources,
-                 include_directories : includes,
-                 link_with : [libjournal_core,
-                              libshared],
-                 dependencies : [threads],
-                 install_rpath : rootlibexecdir,
-                 install : true)
-public_programs += exe
-
-exe = executable('journalctl',
-                 journalctl_sources,
-                 include_directories : includes,
-                 link_with : [libshared],
-                 dependencies : [threads,
-                                 libqrencode,
-                                 libxz,
-                                 liblz4,
-                                 libpcre2],
-                 install_rpath : rootlibexecdir,
-                 install : true,
-                 install_dir : rootbindir)
-public_programs += exe
-
-executable('systemd-getty-generator',
-           'src/getty-generator/getty-generator.c',
-           include_directories : includes,
-           link_with : [libshared],
-           install_rpath : rootlibexecdir,
-           install : true,
-           install_dir : systemgeneratordir)
+executable(
+        'systemd-journald',
+        systemd_journald_sources,
+        include_directories : includes,
+        link_with : [libjournal_core,
+                     libshared],
+        dependencies : [threads,
+                        libxz,
+                        liblz4,
+                        libselinux,
+                        libzstd],
+        install_rpath : rootlibexecdir,
+        install : true,
+        install_dir : rootlibexecdir)
 
-executable('systemd-debug-generator',
-           'src/debug-generator/debug-generator.c',
-           include_directories : includes,
-           link_with : [libshared],
-           install_rpath : rootlibexecdir,
-           install : true,
-           install_dir : systemgeneratordir)
+public_programs += executable(
+        'systemd-cat',
+        systemd_cat_sources,
+        include_directories : includes,
+        link_with : [libjournal_core,
+                     libshared],
+        dependencies : [threads],
+        install_rpath : rootlibexecdir,
+        install : true)
+
+public_programs += executable(
+        'journalctl',
+        journalctl_sources,
+        include_directories : includes,
+        link_with : [libshared],
+        dependencies : [threads,
+                        libqrencode,
+                        libxz,
+                        liblz4,
+                        libpcre2,
+                        libzstd],
+        install_rpath : rootlibexecdir,
+        install : true,
+        install_dir : rootbindir)
 
-executable('systemd-run-generator',
-           'src/run-generator/run-generator.c',
-           include_directories : includes,
-           link_with : [libshared],
-           install_rpath : rootlibexecdir,
-           install : true,
-           install_dir : systemgeneratordir)
+executable(
+        'systemd-getty-generator',
+        'src/getty-generator/getty-generator.c',
+        include_directories : includes,
+        link_with : [libshared],
+        install_rpath : rootlibexecdir,
+        install : true,
+        install_dir : systemgeneratordir)
 
-executable('systemd-fstab-generator',
-           'src/fstab-generator/fstab-generator.c',
-           include_directories : includes,
-           link_with : [libcore_shared,
-                        libshared],
-           install_rpath : rootlibexecdir,
-           install : true,
-           install_dir : systemgeneratordir)
+executable(
+        'systemd-debug-generator',
+        'src/debug-generator/debug-generator.c',
+        include_directories : includes,
+        link_with : [libshared],
+        install_rpath : rootlibexecdir,
+        install : true,
+        install_dir : systemgeneratordir)
+
+executable(
+        'systemd-run-generator',
+        'src/run-generator/run-generator.c',
+        include_directories : includes,
+        link_with : [libshared],
+        install_rpath : rootlibexecdir,
+        install : true,
+        install_dir : systemgeneratordir)
+
+executable(
+        'systemd-fstab-generator',
+        'src/fstab-generator/fstab-generator.c',
+        include_directories : includes,
+        link_with : [libcore_shared,
+                     libshared],
+        install_rpath : rootlibexecdir,
+        install : true,
+        install_dir : systemgeneratordir)
 
 if conf.get('ENABLE_ENVIRONMENT_D') == 1
-        executable('30-systemd-environment-d-generator',
-                   'src/environment-d-generator/environment-d-generator.c',
-                   include_directories : includes,
-                   link_with : [libshared],
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : userenvgeneratordir)
+        executable(
+                '30-systemd-environment-d-generator',
+                'src/environment-d-generator/environment-d-generator.c',
+                include_directories : includes,
+                link_with : [libshared],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : userenvgeneratordir)
 
         meson.add_install_script(meson_make_symlink,
                                  join_paths(sysconfdir, 'environment'),
@@ -1801,111 +1861,117 @@ if conf.get('ENABLE_ENVIRONMENT_D') == 1
 endif
 
 if conf.get('ENABLE_HIBERNATE') == 1
-        executable('systemd-hibernate-resume-generator',
-                   'src/hibernate-resume/hibernate-resume-generator.c',
-                   include_directories : includes,
-                   link_with : [libshared],
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : systemgeneratordir)
-
-        executable('systemd-hibernate-resume',
-                   'src/hibernate-resume/hibernate-resume.c',
-                   include_directories : includes,
-                   link_with : [libshared],
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : rootlibexecdir)
+        executable(
+                'systemd-hibernate-resume-generator',
+                'src/hibernate-resume/hibernate-resume-generator.c',
+                include_directories : includes,
+                link_with : [libshared],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : systemgeneratordir)
+
+        executable(
+                'systemd-hibernate-resume',
+                'src/hibernate-resume/hibernate-resume.c',
+                include_directories : includes,
+                link_with : [libshared],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
 endif
 
 if conf.get('HAVE_BLKID') == 1
-        executable('systemd-gpt-auto-generator',
-                   'src/gpt-auto-generator/gpt-auto-generator.c',
-                   'src/shared/blkid-util.h',
-                   include_directories : includes,
-                   link_with : [libshared],
-                   dependencies : libblkid,
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : systemgeneratordir)
-
-        exe = executable('systemd-dissect',
-                         'src/dissect/dissect.c',
-                         include_directories : includes,
-                         link_with : [libshared],
-                         install_rpath : rootlibexecdir,
-                         install : true,
-                         install_dir : rootlibexecdir)
-        public_programs += exe
+        executable(
+                'systemd-gpt-auto-generator',
+                'src/gpt-auto-generator/gpt-auto-generator.c',
+                'src/shared/blkid-util.h',
+                include_directories : includes,
+                link_with : [libshared],
+                dependencies : libblkid,
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : systemgeneratordir)
+
+        public_programs += executable(
+                'systemd-dissect',
+                'src/dissect/dissect.c',
+                include_directories : includes,
+                link_with : [libshared],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
 endif
 
 if conf.get('ENABLE_RESOLVE') == 1
-        executable('systemd-resolved',
-                   systemd_resolved_sources,
-                   include_directories : includes,
-                   link_with : [libshared,
-                                libbasic_gcrypt,
-                                libsystemd_resolve_core],
-                   dependencies : systemd_resolved_dependencies,
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : rootlibexecdir)
-
-        exe = executable('resolvectl',
-                         resolvectl_sources,
-                         include_directories : includes,
-                         link_with : [libshared,
-                                      libbasic_gcrypt,
-                                      libsystemd_resolve_core],
-                         dependencies : [threads,
-                                         libgpg_error,
-                                         libm,
-                                         libidn],
-                         install_rpath : rootlibexecdir,
-                         install : true)
-        public_programs += exe
+        executable(
+                'systemd-resolved',
+                systemd_resolved_sources,
+                include_directories : includes,
+                link_with : [libshared,
+                             libbasic_gcrypt,
+                             libsystemd_resolve_core],
+                dependencies : systemd_resolved_dependencies,
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
+
+        public_programs += executable(
+                'resolvectl',
+                resolvectl_sources,
+                include_directories : includes,
+                link_with : [libshared,
+                             libbasic_gcrypt,
+                             libsystemd_resolve_core],
+                dependencies : [threads,
+                                libgpg_error,
+                                libm,
+                                libidn],
+                install_rpath : rootlibexecdir,
+                install : true)
 
         meson.add_install_script(meson_make_symlink,
-                         join_paths(bindir, 'resolvectl'),
-                         join_paths(rootsbindir, 'resolvconf'))
+                                 join_paths(bindir, 'resolvectl'),
+                                 join_paths(rootsbindir, 'resolvconf'))
 
         meson.add_install_script(meson_make_symlink,
-                         join_paths(bindir, 'resolvectl'),
-                         join_paths(bindir, 'systemd-resolve'))
+                                 join_paths(bindir, 'resolvectl'),
+                                 join_paths(bindir, 'systemd-resolve'))
 endif
 
 if conf.get('ENABLE_LOGIND') == 1
-        executable('systemd-logind',
-                   systemd_logind_sources,
-                   include_directories : includes,
-                   link_with : [liblogind_core,
-                                libshared],
-                   dependencies : [threads,
-                                   libacl],
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : rootlibexecdir)
-
-        exe = executable('loginctl',
-                         loginctl_sources,
-                         include_directories : includes,
-                         link_with : [libshared],
-                         dependencies : [threads,
-                                         liblz4,
-                                         libxz],
-                         install_rpath : rootlibexecdir,
-                         install : true,
-                         install_dir : rootbindir)
-        public_programs += exe
-
-        exe = executable('systemd-inhibit',
-                         'src/login/inhibit.c',
-                         include_directories : includes,
-                         link_with : [libshared],
-                         install_rpath : rootlibexecdir,
-                         install : true,
-                         install_dir : rootbindir)
-        public_programs += exe
+        executable(
+                'systemd-logind',
+                systemd_logind_sources,
+                include_directories : includes,
+                link_with : [liblogind_core,
+                             libshared],
+                dependencies : [threads,
+                                libacl],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
+
+        public_programs += executable(
+                'loginctl',
+                loginctl_sources,
+                include_directories : includes,
+                link_with : [libshared],
+                dependencies : [threads,
+                                liblz4,
+                                libxz,
+                                libzstd],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootbindir)
+
+        public_programs += executable(
+                'systemd-inhibit',
+                'src/login/inhibit.c',
+                include_directories : includes,
+                link_with : [libshared],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootbindir)
 
         if conf.get('HAVE_PAM') == 1
                 version_script_arg = join_paths(project_source_root, pam_systemd_sym)
@@ -1933,70 +1999,75 @@ if conf.get('ENABLE_LOGIND') == 1
                 endif
         endif
 
-        executable('systemd-user-runtime-dir',
-                   user_runtime_dir_sources,
-                   include_directories : includes,
-                   link_with : [libshared],
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : rootlibexecdir)
+        executable(
+                'systemd-user-runtime-dir',
+                user_runtime_dir_sources,
+                include_directories : includes,
+                link_with : [libshared],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
 endif
 
 if conf.get('HAVE_PAM') == 1
-        executable('systemd-user-sessions',
-                   'src/user-sessions/user-sessions.c',
-                   include_directories : includes,
-                   link_with : [libshared],
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : rootlibexecdir)
+        executable(
+                'systemd-user-sessions',
+                'src/user-sessions/user-sessions.c',
+                include_directories : includes,
+                link_with : [libshared],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
 endif
 
 if conf.get('ENABLE_EFI') == 1 and conf.get('HAVE_BLKID') == 1
-        exe = executable('bootctl',
-                         'src/boot/bootctl.c',
-                         include_directories : includes,
-                         link_with : [libshared],
-                         dependencies : [libblkid],
-                         install_rpath : rootlibexecdir,
-                         install : true)
-        public_programs += exe
-
-        executable('systemd-bless-boot',
-                   'src/boot/bless-boot.c',
-                   include_directories : includes,
-                   link_with : [libshared],
-                   dependencies : [libblkid],
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : rootlibexecdir)
-
-        executable('systemd-bless-boot-generator',
-                   'src/boot/bless-boot-generator.c',
-                   include_directories : includes,
-                   link_with : [libshared],
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : systemgeneratordir)
-endif
-
-executable('systemd-boot-check-no-failures',
-           'src/boot/boot-check-no-failures.c',
-           include_directories : includes,
-           link_with : [libshared],
-           dependencies : [libblkid],
-           install_rpath : rootlibexecdir,
-           install : true,
-           install_dir : rootlibexecdir)
-
-exe = executable('systemd-socket-activate', 'src/activate/activate.c',
-                 include_directories : includes,
-                 link_with : [libshared],
-                 dependencies : [threads],
-                 install_rpath : rootlibexecdir,
-                 install : true)
-public_programs += exe
+        public_programs += executable(
+                'bootctl',
+                'src/boot/bootctl.c',
+                include_directories : includes,
+                link_with : [libshared],
+                dependencies : [libblkid],
+                install_rpath : rootlibexecdir,
+                install : true)
+
+        public_programs += executable(
+                'systemd-bless-boot',
+                'src/boot/bless-boot.c',
+                include_directories : includes,
+                link_with : [libshared],
+                dependencies : [libblkid],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
+
+        executable(
+                'systemd-bless-boot-generator',
+                'src/boot/bless-boot-generator.c',
+                include_directories : includes,
+                link_with : [libshared],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : systemgeneratordir)
+endif
+
+executable(
+        'systemd-boot-check-no-failures',
+        'src/boot/boot-check-no-failures.c',
+        include_directories : includes,
+        link_with : [libshared],
+        dependencies : [libblkid],
+        install_rpath : rootlibexecdir,
+        install : true,
+        install_dir : rootlibexecdir)
 
+public_programs += executable(
+        'systemd-socket-activate',
+        'src/activate/activate.c',
+        include_directories : includes,
+        link_with : [libshared],
+        dependencies : [threads],
+        install_rpath : rootlibexecdir,
+        install : true)
 
 if get_option('link-systemctl-shared')
         systemctl_link_with = [libshared]
@@ -2007,111 +2078,122 @@ else
                                libbasic_gcrypt]
 endif
 
-exe = executable('systemctl',
-                 'src/systemctl/systemctl.c',
-                 'src/systemctl/sysv-compat.h',
-                 'src/systemctl/sysv-compat.c',
-                 include_directories : includes,
-                 link_with : systemctl_link_with,
-                 dependencies : [threads,
-                                 libcap,
-                                 libselinux,
-                                 libxz,
-                                 liblz4],
-                 install_rpath : rootlibexecdir,
-                 install : true,
-                 install_dir : rootbindir)
-public_programs += exe
+public_programs += executable(
+        'systemctl',
+        'src/systemctl/systemctl.c',
+        'src/systemctl/sysv-compat.h',
+        'src/systemctl/sysv-compat.c',
+        include_directories : includes,
+        link_with : systemctl_link_with,
+        dependencies : [threads,
+                        libcap,
+                        libselinux,
+                        libxz,
+                        liblz4,
+                        libzstd],
+        install_rpath : rootlibexecdir,
+        install : true,
+        install_dir : rootbindir)
 
 if conf.get('ENABLE_PORTABLED') == 1
-        executable('systemd-portabled',
-                   systemd_portabled_sources,
-                   include_directories : includes,
-                   link_with : [libshared],
-                   dependencies : [threads],
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : rootlibexecdir)
-
-        exe = executable('portablectl', 'src/portable/portablectl.c',
-                         include_directories : includes,
-                         link_with : [libshared],
-                         dependencies : [threads],
-                         install_rpath : rootlibexecdir,
-                         install : true,
-                         install_dir : rootbindir)
-        public_programs += exe
+        executable(
+                'systemd-portabled',
+                systemd_portabled_sources,
+                include_directories : includes,
+                link_with : [libshared],
+                dependencies : [threads],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
+
+        public_programs += executable(
+                'portablectl',
+                'src/portable/portablectl.c',
+                include_directories : includes,
+                link_with : [libshared],
+                dependencies : [threads],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootbindir)
 endif
 
 if conf.get('ENABLE_USERDB') == 1
-        executable('systemd-userwork',
-                   systemd_userwork_sources,
-                   include_directories : includes,
-                   link_with : [libshared],
-                   dependencies : [threads],
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : rootlibexecdir)
-
-        executable('systemd-userdbd',
-                   systemd_userdbd_sources,
-                   include_directories : includes,
-                   link_with : [libshared],
-                   dependencies : [threads],
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : rootlibexecdir)
-
-        executable('userdbctl',
-                   userdbctl_sources,
-                   include_directories : includes,
-                   link_with : [libshared],
-                   dependencies : [threads],
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : rootbindir)
+        executable(
+                'systemd-userwork',
+                systemd_userwork_sources,
+                include_directories : includes,
+                link_with : [libshared],
+                dependencies : [threads],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
+
+        executable(
+                'systemd-userdbd',
+                systemd_userdbd_sources,
+                include_directories : includes,
+                link_with : [libshared],
+                dependencies : [threads],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
+
+        public_programs += executable(
+                'userdbctl',
+                userdbctl_sources,
+                include_directories : includes,
+                link_with : [libshared],
+                dependencies : [threads],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootbindir)
 endif
 
 if conf.get('ENABLE_HOMED') == 1
-        executable('systemd-homework',
-                   systemd_homework_sources,
-                   include_directories : includes,
-                   link_with : [libshared],
-                   dependencies : [threads,
-                                   libcryptsetup,
-                                   libblkid,
-                                   libcrypt,
-                                   libopenssl,
-                                   libfdisk,
-                                   libp11kit],
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : rootlibexecdir)
-
-        executable('systemd-homed',
-                   systemd_homed_sources,
-                   include_directories : includes,
-                   link_with : [libshared],
-                   dependencies : [threads,
-                                   libcrypt,
-                                   libopenssl,
-                                   libpwquality],
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : rootlibexecdir)
-
-        executable('homectl',
-                   homectl_sources,
-                   include_directories : includes,
-                   link_with : [libshared],
-                   dependencies : [threads,
-                                   libcrypt,
-                                   libopenssl,
-                                   libp11kit,
-                                   libpwquality],
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : rootbindir)
+        executable(
+                'systemd-homework',
+                systemd_homework_sources,
+                include_directories : includes,
+                link_with : [libshared],
+                dependencies : [threads,
+                                libcryptsetup,
+                                libblkid,
+                                libcrypt,
+                                libopenssl,
+                                libfdisk,
+                                libp11kit,
+                                libfido2],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
+
+        executable(
+                'systemd-homed',
+                systemd_homed_sources,
+                include_directories : includes,
+                link_with : [libshared],
+                dependencies : [threads,
+                                libcrypt,
+                                libopenssl,
+                                libpwquality],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
+
+        public_programs += executable(
+                'homectl',
+                homectl_sources,
+                include_directories : includes,
+                link_with : [libshared],
+                dependencies : [threads,
+                                libcrypt,
+                                libopenssl,
+                                libp11kit,
+                                libfido2,
+                                libpwquality],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootbindir)
 
         if conf.get('HAVE_PAM') == 1
                 version_script_arg = join_paths(project_source_root, pam_systemd_home_sym)
@@ -2134,122 +2216,160 @@ if conf.get('ENABLE_HOMED') == 1
         endif
 endif
 
-foreach alias : ['halt', 'poweroff', 'reboot', 'runlevel', 'shutdown', 'telinit']
+foreach alias : (['halt', 'poweroff', 'reboot', 'shutdown'] +
+                (conf.get('HAVE_SYSV_COMPAT') == 1 ? ['runlevel', 'telinit'] : []))
         meson.add_install_script(meson_make_symlink,
                                  join_paths(rootbindir, 'systemctl'),
                                  join_paths(rootsbindir, alias))
 endforeach
 
+meson.add_install_script(meson_make_symlink,
+                         join_paths(rootbindir, 'udevadm'),
+                         join_paths(rootlibexecdir, 'systemd-udevd'))
+
 if conf.get('ENABLE_BACKLIGHT') == 1
-        executable('systemd-backlight',
-                   'src/backlight/backlight.c',
-                   include_directories : includes,
-                   link_with : [libshared],
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : rootlibexecdir)
+        executable(
+                'systemd-backlight',
+                'src/backlight/backlight.c',
+                include_directories : includes,
+                link_with : [libshared],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
 endif
 
 if conf.get('ENABLE_RFKILL') == 1
-        executable('systemd-rfkill',
-                   'src/rfkill/rfkill.c',
-                   include_directories : includes,
-                   link_with : [libshared],
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : rootlibexecdir)
-endif
-
-executable('systemd-system-update-generator',
-           'src/system-update-generator/system-update-generator.c',
-           include_directories : includes,
-           link_with : [libshared],
-           install_rpath : rootlibexecdir,
-           install : true,
-           install_dir : systemgeneratordir)
+        executable(
+                'systemd-rfkill',
+                'src/rfkill/rfkill.c',
+                include_directories : includes,
+                link_with : [libshared],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
+endif
+
+executable(
+        'systemd-system-update-generator',
+        'src/system-update-generator/system-update-generator.c',
+        include_directories : includes,
+        link_with : [libshared],
+        install_rpath : rootlibexecdir,
+        install : true,
+        install_dir : systemgeneratordir)
 
 if conf.get('HAVE_LIBCRYPTSETUP') == 1
         systemd_cryptsetup_sources = files('''
-                src/cryptsetup/cryptsetup.c
                 src/cryptsetup/cryptsetup-pkcs11.h
+                src/cryptsetup/cryptsetup-util.c
+                src/cryptsetup/cryptsetup-util.h
+                src/cryptsetup/cryptsetup.c
 '''.split())
 
         if conf.get('HAVE_P11KIT') == 1
                 systemd_cryptsetup_sources += files('src/cryptsetup/cryptsetup-pkcs11.c')
         endif
 
-        executable('systemd-cryptsetup',
-                   systemd_cryptsetup_sources,
-                   include_directories : includes,
-                   link_with : [libshared],
-                   dependencies : [libcryptsetup,
-                                   libp11kit],
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : rootlibexecdir)
-
-        executable('systemd-cryptsetup-generator',
-                   'src/cryptsetup/cryptsetup-generator.c',
-                   include_directories : includes,
-                   link_with : [libshared],
-                   dependencies : [libcryptsetup],
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : systemgeneratordir)
-
-        executable('systemd-veritysetup',
-                   'src/veritysetup/veritysetup.c',
-                   include_directories : includes,
-                   link_with : [libshared],
-                   dependencies : [libcryptsetup],
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : rootlibexecdir)
-
-        executable('systemd-veritysetup-generator',
-                   'src/veritysetup/veritysetup-generator.c',
-                   include_directories : includes,
-                   link_with : [libshared],
-                   dependencies : [libcryptsetup],
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : systemgeneratordir)
+        executable(
+                'systemd-cryptsetup',
+                systemd_cryptsetup_sources,
+                include_directories : includes,
+                link_with : [libshared],
+                dependencies : [libcryptsetup,
+                                libp11kit],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
+
+        executable(
+                'systemd-cryptsetup-generator',
+                'src/cryptsetup/cryptsetup-generator.c',
+                include_directories : includes,
+                link_with : [libshared],
+                dependencies : [libcryptsetup],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : systemgeneratordir)
+
+        executable(
+                'systemd-veritysetup',
+                'src/veritysetup/veritysetup.c',
+                include_directories : includes,
+                link_with : [libshared],
+                dependencies : [libcryptsetup],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
+
+        executable(
+                'systemd-veritysetup-generator',
+                'src/veritysetup/veritysetup-generator.c',
+                include_directories : includes,
+                link_with : [libshared],
+                dependencies : [libcryptsetup],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : systemgeneratordir)
 endif
 
 if conf.get('HAVE_SYSV_COMPAT') == 1
-        executable('systemd-sysv-generator',
-                   'src/sysv-generator/sysv-generator.c',
-                   include_directories : includes,
-                   link_with : [libshared],
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : systemgeneratordir)
-
-        executable('systemd-rc-local-generator',
-                   'src/rc-local-generator/rc-local-generator.c',
-                   include_directories : includes,
-                   link_with : [libshared],
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : systemgeneratordir)
+        executable(
+                'systemd-sysv-generator',
+                'src/sysv-generator/sysv-generator.c',
+                include_directories : includes,
+                link_with : [libshared],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : systemgeneratordir)
+
+        executable(
+                'systemd-rc-local-generator',
+                'src/rc-local-generator/rc-local-generator.c',
+                include_directories : includes,
+                link_with : [libshared],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : systemgeneratordir)
+endif
+
+if conf.get('ENABLE_XDG_AUTOSTART') == 1
+        executable(
+                'systemd-xdg-autostart-generator',
+                'src/xdg-autostart-generator/xdg-autostart-generator.c',
+                'src/xdg-autostart-generator/xdg-autostart-service.c',
+                include_directories : includes,
+                link_with : [libshared],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : usergeneratordir)
+
+        executable(
+                'systemd-xdg-autostart-condition',
+                'src/xdg-autostart-generator/xdg-autostart-condition.c',
+                include_directories : includes,
+                link_with : [libshared],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
 endif
 
 if conf.get('ENABLE_HOSTNAMED') == 1
-        executable('systemd-hostnamed',
-                   'src/hostname/hostnamed.c',
-                   include_directories : includes,
-                   link_with : [libshared],
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : rootlibexecdir)
-
-        exe = executable('hostnamectl',
-                         'src/hostname/hostnamectl.c',
-                         include_directories : includes,
-                         link_with : [libshared],
-                         install_rpath : rootlibexecdir,
-                         install : true)
-        public_programs += exe
+        executable(
+                'systemd-hostnamed',
+                'src/hostname/hostnamed.c',
+                include_directories : includes,
+                link_with : [libshared],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
+
+        public_programs += executable(
+                'hostnamectl',
+                'src/hostname/hostnamectl.c',
+                include_directories : includes,
+                link_with : [libshared],
+                install_rpath : rootlibexecdir,
+                install : true)
 endif
 
 if conf.get('ENABLE_LOCALED') == 1
@@ -2260,43 +2380,45 @@ if conf.get('ENABLE_LOCALED') == 1
                 deps = []
         endif
 
-        executable('systemd-localed',
-                   systemd_localed_sources,
-                   include_directories : includes,
-                   link_with : [libshared],
-                   dependencies : deps,
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : rootlibexecdir)
-
-        exe = executable('localectl',
-                         localectl_sources,
-                         include_directories : includes,
-                         link_with : [libshared],
-                         install_rpath : rootlibexecdir,
-                         install : true)
-        public_programs += exe
+        executable(
+                'systemd-localed',
+                systemd_localed_sources,
+                include_directories : includes,
+                link_with : [libshared],
+                dependencies : deps,
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
+
+        public_programs += executable(
+                'localectl',
+                localectl_sources,
+                include_directories : includes,
+                link_with : [libshared],
+                install_rpath : rootlibexecdir,
+                install : true)
 endif
 
 if conf.get('ENABLE_TIMEDATED') == 1
-        executable('systemd-timedated',
-                   'src/timedate/timedated.c',
-                   include_directories : includes,
-                   link_with : [libshared],
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : rootlibexecdir)
+        executable(
+                'systemd-timedated',
+                'src/timedate/timedated.c',
+                include_directories : includes,
+                link_with : [libshared],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
 endif
 
 if conf.get('ENABLE_TIMEDATECTL') == 1
-        exe = executable('timedatectl',
-                         'src/timedate/timedatectl.c',
-                         include_directories : includes,
-                         install_rpath : rootlibexecdir,
-                         link_with : [libshared],
-                         dependencies : [libm],
-                         install : true)
-        public_programs += exe
+        public_programs += executable(
+                'timedatectl',
+                'src/timedate/timedatectl.c',
+                include_directories : includes,
+                install_rpath : rootlibexecdir,
+                link_with : [libshared],
+                dependencies : [libm],
+                install : true)
 endif
 
 if conf.get('ENABLE_TIMESYNCD') == 1
@@ -2309,204 +2431,222 @@ if conf.get('ENABLE_TIMESYNCD') == 1
                                        libbasic_gcrypt]
         endif
 
-        executable('systemd-timesyncd',
-                   systemd_timesyncd_sources,
-                   include_directories : includes,
-                   link_with : [timesyncd_link_with],
-                   dependencies : [threads,
-                                   libm],
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : rootlibexecdir)
-
-        executable('systemd-time-wait-sync',
-                   'src/time-wait-sync/time-wait-sync.c',
-                   include_directories : includes,
-                   link_with : [timesyncd_link_with],
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : rootlibexecdir)
+        executable(
+                'systemd-timesyncd',
+                systemd_timesyncd_sources,
+                include_directories : includes,
+                link_with : [timesyncd_link_with],
+                dependencies : [threads,
+                                libm],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
+
+        executable(
+                'systemd-time-wait-sync',
+                'src/time-wait-sync/time-wait-sync.c',
+                include_directories : includes,
+                link_with : [timesyncd_link_with],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
 endif
 
 if conf.get('ENABLE_MACHINED') == 1
-        executable('systemd-machined',
-                   systemd_machined_sources,
-                   include_directories : includes,
-                   link_with : [libmachine_core,
-                                libshared],
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : rootlibexecdir)
-
-        exe = executable('machinectl',
-                         'src/machine/machinectl.c',
-                         include_directories : includes,
-                         link_with : [libshared],
-                         dependencies : [threads,
-                                         libxz,
-                                         liblz4],
-                         install_rpath : rootlibexecdir,
-                         install : true,
-                         install_dir : rootbindir)
-        public_programs += exe
+        executable(
+                'systemd-machined',
+                systemd_machined_sources,
+                include_directories : includes,
+                link_with : [libmachine_core,
+                             libshared],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
+
+        public_programs += executable(
+                'machinectl',
+                'src/machine/machinectl.c',
+                include_directories : includes,
+                link_with : [libshared],
+                dependencies : [threads,
+                                libxz,
+                                liblz4,
+                                libzstd],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootbindir)
 endif
 
 if conf.get('ENABLE_IMPORTD') == 1
-        executable('systemd-importd',
-                   systemd_importd_sources,
-                   include_directories : includes,
-                   link_with : [libshared],
-                   dependencies : [threads],
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : rootlibexecdir)
-
-        systemd_pull = executable('systemd-pull',
-                                  systemd_pull_sources,
-                                  include_directories : includes,
-                                  link_with : [libshared],
-                                  dependencies : [versiondep,
-                                                  libcurl,
-                                                  libz,
-                                                  libbzip2,
-                                                  libxz,
-                                                  libgcrypt],
-                                  install_rpath : rootlibexecdir,
-                                  install : true,
-                                  install_dir : rootlibexecdir)
-
-        systemd_import = executable('systemd-import',
-                                    systemd_import_sources,
-                                    include_directories : includes,
-                                    link_with : [libshared],
-                                    dependencies : [libcurl,
-                                                    libz,
-                                                    libbzip2,
-                                                    libxz],
-                                    install_rpath : rootlibexecdir,
-                                    install : true,
-                                    install_dir : rootlibexecdir)
-
-        systemd_import_fs = executable('systemd-import-fs',
-                                    systemd_import_fs_sources,
-                                    include_directories : includes,
-                                    link_with : [libshared],
-                                    install_rpath : rootlibexecdir,
-                                    install : true,
-                                    install_dir : rootlibexecdir)
-
-        systemd_export = executable('systemd-export',
-                                    systemd_export_sources,
-                                    include_directories : includes,
-                                    link_with : [libshared],
-                                    dependencies : [libcurl,
-                                                    libz,
-                                                    libbzip2,
-                                                    libxz],
-                                    install_rpath : rootlibexecdir,
-                                    install : true,
-                                    install_dir : rootlibexecdir)
+        executable(
+                'systemd-importd',
+                systemd_importd_sources,
+                include_directories : includes,
+                link_with : [libshared],
+                dependencies : [threads],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
+
+        systemd_pull = executable(
+                'systemd-pull',
+                systemd_pull_sources,
+                include_directories : includes,
+                link_with : [libshared],
+                dependencies : [versiondep,
+                                libcurl,
+                                libz,
+                                libbzip2,
+                                libxz,
+                                libgcrypt],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
+
+        systemd_import = executable(
+                'systemd-import',
+                systemd_import_sources,
+                include_directories : includes,
+                link_with : [libshared],
+                dependencies : [libcurl,
+                                libz,
+                                libbzip2,
+                                libxz],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
+
+        systemd_import_fs = executable(
+                'systemd-import-fs',
+                systemd_import_fs_sources,
+                include_directories : includes,
+                link_with : [libshared],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
+
+        systemd_export = executable(
+                'systemd-export',
+                systemd_export_sources,
+                include_directories : includes,
+                link_with : [libshared],
+                dependencies : [libcurl,
+                                libz,
+                                libbzip2,
+                                libxz],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
 
         public_programs += [systemd_pull, systemd_import, systemd_import_fs, systemd_export]
 endif
 
 if conf.get('ENABLE_REMOTE') == 1 and conf.get('HAVE_LIBCURL') == 1
-        exe = executable('systemd-journal-upload',
-                         systemd_journal_upload_sources,
-                         include_directories : includes,
-                         link_with : [libshared],
-                         dependencies : [versiondep,
-                                         threads,
-                                         libcurl,
-                                         libgnutls,
-                                         libxz,
-                                         liblz4],
-                         install_rpath : rootlibexecdir,
-                         install : true,
-                         install_dir : rootlibexecdir)
-        public_programs += exe
+        public_programs += executable(
+                'systemd-journal-upload',
+                systemd_journal_upload_sources,
+                include_directories : includes,
+                link_with : [libshared],
+                dependencies : [versiondep,
+                                threads,
+                                libcurl,
+                                libgnutls,
+                                libxz,
+                                liblz4,
+                                libzstd],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
 endif
 
 if conf.get('ENABLE_REMOTE') == 1 and conf.get('HAVE_MICROHTTPD') == 1
-        s_j_remote = executable('systemd-journal-remote',
-                                systemd_journal_remote_sources,
-                                include_directories : includes,
-                                link_with : [libshared,
-                                             libsystemd_journal_remote],
-                                dependencies : [threads,
-                                                libmicrohttpd,
-                                                libgnutls,
-                                                libxz,
-                                                liblz4],
-                                install_rpath : rootlibexecdir,
-                                install : true,
-                                install_dir : rootlibexecdir)
-
-        s_j_gatewayd = executable('systemd-journal-gatewayd',
-                                  systemd_journal_gatewayd_sources,
-                                  include_directories : includes,
-                                  link_with : [libshared],
-                                  dependencies : [threads,
-                                                  libmicrohttpd,
-                                                  libgnutls,
-                                                  libxz,
-                                                  liblz4],
-                                  install_rpath : rootlibexecdir,
-                                  install : true,
-                                  install_dir : rootlibexecdir)
-        public_programs += [s_j_remote, s_j_gatewayd]
+        public_programs += executable(
+                'systemd-journal-remote',
+                systemd_journal_remote_sources,
+                include_directories : includes,
+                link_with : [libshared,
+                             libsystemd_journal_remote],
+                dependencies : [threads,
+                                libmicrohttpd,
+                                libgnutls,
+                                libxz,
+                                liblz4,
+                                libzstd],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
+
+        public_programs += executable(
+                'systemd-journal-gatewayd',
+                systemd_journal_gatewayd_sources,
+                include_directories : includes,
+                link_with : [libshared],
+                dependencies : [threads,
+                                libmicrohttpd,
+                                libgnutls,
+                                libxz,
+                                liblz4,
+                                libzstd],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
 endif
 
 if conf.get('ENABLE_COREDUMP') == 1
-        executable('systemd-coredump',
-                   systemd_coredump_sources,
-                   include_directories : includes,
-                   link_with : [libshared],
-                   dependencies : [threads,
-                                   libacl,
-                                   libdw,
-                                   libxz,
-                                   liblz4],
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : rootlibexecdir)
-
-        exe = executable('coredumpctl',
-                         coredumpctl_sources,
-                         include_directories : includes,
-                         link_with : [libshared],
-                         dependencies : [threads,
-                                         libxz,
-                                         liblz4],
-                         install_rpath : rootlibexecdir,
-                         install : true)
-        public_programs += exe
+        executable(
+                'systemd-coredump',
+                systemd_coredump_sources,
+                include_directories : includes,
+                link_with : [libshared],
+                dependencies : [threads,
+                                libacl,
+                                libdw,
+                                libxz,
+                                liblz4,
+                                libzstd],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
+
+        public_programs += executable(
+                'coredumpctl',
+                coredumpctl_sources,
+                include_directories : includes,
+                link_with : [libshared],
+                dependencies : [threads,
+                                libxz,
+                                liblz4,
+                                libzstd],
+                install_rpath : rootlibexecdir,
+                install : true)
 endif
 
 if conf.get('ENABLE_PSTORE') == 1
-        executable('systemd-pstore',
-                   systemd_pstore_sources,
-                   include_directories : includes,
-                   link_with : [libshared],
-                   dependencies : [threads,
-                                   libacl,
-                                   libdw,
-                                   libxz,
-                                   liblz4],
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : rootlibexecdir)
+        executable(
+                'systemd-pstore',
+                systemd_pstore_sources,
+                include_directories : includes,
+                link_with : [libshared],
+                dependencies : [threads,
+                                libacl,
+                                libdw,
+                                libxz,
+                                liblz4,
+                                libzstd],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
 endif
 
 if conf.get('ENABLE_BINFMT') == 1
-        exe = executable('systemd-binfmt',
-                         'src/binfmt/binfmt.c',
-                         include_directories : includes,
-                         link_with : [libshared],
-                         install_rpath : rootlibexecdir,
-                         install : true,
-                         install_dir : rootlibexecdir)
-        public_programs += exe
+        public_programs += executable(
+                'systemd-binfmt',
+                'src/binfmt/binfmt.c',
+                include_directories : includes,
+                link_with : [libshared],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
 
         meson.add_install_script('sh', '-c',
                                  mkdir_p.format(binfmtdir))
@@ -2515,76 +2655,89 @@ if conf.get('ENABLE_BINFMT') == 1
 endif
 
 if conf.get('ENABLE_REPART') == 1
-        executable('systemd-repart',
-                   systemd_repart_sources,
-                   include_directories : includes,
-                   link_with : [libshared],
-                   dependencies : [threads,
-                                   libcryptsetup,
-                                   libblkid,
-                                   libfdisk,
-                                   libopenssl],
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : rootbindir)
+        exe = executable(
+                'systemd-repart',
+                systemd_repart_sources,
+                include_directories : includes,
+                link_with : [libshared],
+                dependencies : [threads,
+                                libcryptsetup,
+                                libblkid,
+                                libfdisk,
+                                libopenssl],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootbindir)
+
+        if want_tests != 'false'
+                test('test-repart',
+                     test_repart_sh,
+                     args : exe.full_path())
+        endif
 endif
 
 if conf.get('ENABLE_VCONSOLE') == 1
-        executable('systemd-vconsole-setup',
-                   'src/vconsole/vconsole-setup.c',
-                   include_directories : includes,
-                   link_with : [libshared],
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : rootlibexecdir)
+        executable(
+                'systemd-vconsole-setup',
+                'src/vconsole/vconsole-setup.c',
+                include_directories : includes,
+                link_with : [libshared],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
 endif
 
 if conf.get('ENABLE_RANDOMSEED') == 1
-        executable('systemd-random-seed',
-                   'src/random-seed/random-seed.c',
-                   include_directories : includes,
-                   link_with : [libshared],
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : rootlibexecdir)
+        executable(
+                'systemd-random-seed',
+                'src/random-seed/random-seed.c',
+                include_directories : includes,
+                link_with : [libshared],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
 endif
 
 if conf.get('ENABLE_FIRSTBOOT') == 1
-        executable('systemd-firstboot',
-                   'src/firstboot/firstboot.c',
-                   include_directories : includes,
-                   link_with : [libshared],
-                   dependencies : [libcrypt],
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : rootbindir)
-endif
-
-executable('systemd-remount-fs',
-           'src/remount-fs/remount-fs.c',
-           include_directories : includes,
-           link_with : [libcore_shared,
-                        libshared],
-           install_rpath : rootlibexecdir,
-           install : true,
-           install_dir : rootlibexecdir)
+        executable(
+                'systemd-firstboot',
+                'src/firstboot/firstboot.c',
+                include_directories : includes,
+                link_with : [libshared],
+                dependencies : [libcrypt],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootbindir)
+endif
+
+executable(
+        'systemd-remount-fs',
+        'src/remount-fs/remount-fs.c',
+        include_directories : includes,
+        link_with : [libcore_shared,
+                     libshared],
+        install_rpath : rootlibexecdir,
+        install : true,
+        install_dir : rootlibexecdir)
 
-executable('systemd-machine-id-setup',
-           'src/machine-id-setup/machine-id-setup-main.c',
-           include_directories : includes,
-           link_with : [libcore_shared,
-                        libshared],
-           install_rpath : rootlibexecdir,
-           install : true,
-           install_dir : rootbindir)
+executable(
+        'systemd-machine-id-setup',
+        'src/machine-id-setup/machine-id-setup-main.c',
+        include_directories : includes,
+        link_with : [libcore_shared,
+                     libshared],
+        install_rpath : rootlibexecdir,
+        install : true,
+        install_dir : rootbindir)
 
-executable('systemd-fsck',
-           'src/fsck/fsck.c',
-           include_directories : includes,
-           link_with : [libshared],
-           install_rpath : rootlibexecdir,
-           install : true,
-           install_dir : rootlibexecdir)
+executable(
+        'systemd-fsck',
+        'src/fsck/fsck.c',
+        include_directories : includes,
+        link_with : [libshared],
+        install_rpath : rootlibexecdir,
+        install : true,
+        install_dir : rootlibexecdir)
 
 executable('systemd-growfs',
            'src/partition/growfs.c',
@@ -2595,217 +2748,239 @@ executable('systemd-growfs',
            install : true,
            install_dir : rootlibexecdir)
 
-executable('systemd-makefs',
-           'src/partition/makefs.c',
-           include_directories : includes,
-           link_with : [libshared],
-           install_rpath : rootlibexecdir,
-           install : true,
-           install_dir : rootlibexecdir)
+executable(
+        'systemd-makefs',
+        'src/partition/makefs.c',
+        include_directories : includes,
+        link_with : [libshared],
+        install_rpath : rootlibexecdir,
+        install : true,
+        install_dir : rootlibexecdir)
 
-executable('systemd-sleep',
-           'src/sleep/sleep.c',
-           include_directories : includes,
-           link_with : [libshared],
-           install_rpath : rootlibexecdir,
-           install : true,
-           install_dir : rootlibexecdir)
+executable(
+        'systemd-sleep',
+        'src/sleep/sleep.c',
+        include_directories : includes,
+        link_with : [libshared],
+        install_rpath : rootlibexecdir,
+        install : true,
+        install_dir : rootlibexecdir)
 
 install_data('src/sleep/sleep.conf',
              install_dir : pkgsysconfdir)
 
-exe = executable('systemd-sysctl',
-                 'src/sysctl/sysctl.c',
-                 include_directories : includes,
-                 link_with : [libshared],
-                 install_rpath : rootlibexecdir,
-                 install : true,
-                 install_dir : rootlibexecdir)
-public_programs += exe
-
-executable('systemd-ac-power',
-           'src/ac-power/ac-power.c',
-           include_directories : includes,
-           link_with : [libshared],
-           install_rpath : rootlibexecdir,
-           install : true,
-           install_dir : rootlibexecdir)
+public_programs += executable(
+        'systemd-sysctl',
+        'src/sysctl/sysctl.c',
+        include_directories : includes,
+        link_with : [libshared],
+        install_rpath : rootlibexecdir,
+        install : true,
+        install_dir : rootlibexecdir)
 
-exe = executable('systemd-detect-virt',
-                 'src/detect-virt/detect-virt.c',
-                 include_directories : includes,
-                 link_with : [libshared],
-                 install_rpath : rootlibexecdir,
-                 install : true)
-public_programs += exe
-
-exe = executable('systemd-delta',
-                 'src/delta/delta.c',
-                 include_directories : includes,
-                 link_with : [libshared],
-                 install_rpath : rootlibexecdir,
-                 install : true)
-public_programs += exe
-
-exe = executable('systemd-escape',
-                 'src/escape/escape.c',
-                 include_directories : includes,
-                 link_with : [libshared],
-                 install_rpath : rootlibexecdir,
-                 install : true,
-                 install_dir : rootbindir)
-public_programs += exe
-
-exe = executable('systemd-notify',
-                 'src/notify/notify.c',
-                 include_directories : includes,
-                 link_with : [libshared],
-                 install_rpath : rootlibexecdir,
-                 install : true,
-                 install_dir : rootbindir)
-public_programs += exe
-
-executable('systemd-volatile-root',
-           'src/volatile-root/volatile-root.c',
-           include_directories : includes,
-           link_with : [libshared],
-           install_rpath : rootlibexecdir,
-           install : true,
-           install_dir : rootlibexecdir)
+executable(
+        'systemd-ac-power',
+        'src/ac-power/ac-power.c',
+        include_directories : includes,
+        link_with : [libshared],
+        install_rpath : rootlibexecdir,
+        install : true,
+        install_dir : rootlibexecdir)
 
-executable('systemd-cgroups-agent',
-           'src/cgroups-agent/cgroups-agent.c',
-           include_directories : includes,
-           link_with : [libshared],
-           install_rpath : rootlibexecdir,
-           install : true,
-           install_dir : rootlibexecdir)
+public_programs += executable(
+        'systemd-detect-virt',
+        'src/detect-virt/detect-virt.c',
+        include_directories : includes,
+        link_with : [libshared],
+        install_rpath : rootlibexecdir,
+        install : true)
 
-exe = executable('systemd-id128',
-                 'src/id128/id128.c',
-                 include_directories : includes,
-                 link_with : [libshared],
-                 install_rpath : rootlibexecdir,
-                 install : true)
-public_programs += exe
-
-exe = executable('systemd-path',
-                 'src/path/path.c',
-                 include_directories : includes,
-                 link_with : [libshared],
-                 install_rpath : rootlibexecdir,
-                 install : true)
-public_programs += exe
-
-exe = executable('systemd-ask-password',
-                 'src/ask-password/ask-password.c',
-                 include_directories : includes,
-                 link_with : [libshared],
-                 install_rpath : rootlibexecdir,
-                 install : true,
-                 install_dir : rootbindir)
-public_programs += exe
-
-executable('systemd-reply-password',
-           'src/reply-password/reply-password.c',
-           include_directories : includes,
-           link_with : [libshared],
-           install_rpath : rootlibexecdir,
-           install : true,
-           install_dir : rootlibexecdir)
+public_programs += executable(
+        'systemd-delta',
+        'src/delta/delta.c',
+        include_directories : includes,
+        link_with : [libshared],
+        install_rpath : rootlibexecdir,
+        install : true)
 
-exe = executable('systemd-tty-ask-password-agent',
-                 'src/tty-ask-password-agent/tty-ask-password-agent.c',
-                 include_directories : includes,
-                 link_with : [libshared],
-                 install_rpath : rootlibexecdir,
-                 install : true,
-                 install_dir : rootbindir)
-public_programs += exe
-
-exe = executable('systemd-cgls',
-                 'src/cgls/cgls.c',
-                 include_directories : includes,
-                 link_with : [libshared],
-                 install_rpath : rootlibexecdir,
-                 install : true)
-public_programs += exe
-
-exe = executable('systemd-cgtop',
-                 'src/cgtop/cgtop.c',
-                 include_directories : includes,
-                 link_with : [libshared],
-                 install_rpath : rootlibexecdir,
-                 install : true)
-public_programs += exe
-
-executable('systemd-initctl',
-           'src/initctl/initctl.c',
-           include_directories : includes,
-           link_with : [libshared],
-           install_rpath : rootlibexecdir,
-           install : true,
-           install_dir : rootlibexecdir)
+public_programs += executable(
+        'systemd-escape',
+        'src/escape/escape.c',
+        include_directories : includes,
+        link_with : [libshared],
+        install_rpath : rootlibexecdir,
+        install : true,
+        install_dir : rootbindir)
 
-exe = executable('systemd-mount',
-                 'src/mount/mount-tool.c',
-                 include_directories : includes,
-                 link_with : [libshared],
-                 dependencies: [libmount],
-                 install_rpath : rootlibexecdir,
-                 install : true)
-public_programs += exe
+public_programs += executable(
+        'systemd-notify',
+        'src/notify/notify.c',
+        include_directories : includes,
+        link_with : [libshared],
+        install_rpath : rootlibexecdir,
+        install : true,
+        install_dir : rootbindir)
+
+executable(
+        'systemd-volatile-root',
+        'src/volatile-root/volatile-root.c',
+        include_directories : includes,
+        link_with : [libshared],
+        install_rpath : rootlibexecdir,
+        install : conf.get('ENABLE_INITRD') == 1,
+        install_dir : rootlibexecdir)
+
+executable(
+        'systemd-cgroups-agent',
+        'src/cgroups-agent/cgroups-agent.c',
+        include_directories : includes,
+        link_with : [libshared],
+        install_rpath : rootlibexecdir,
+        install : true,
+        install_dir : rootlibexecdir)
+
+public_programs += executable(
+        'systemd-id128',
+        'src/id128/id128.c',
+        include_directories : includes,
+        link_with : [libshared],
+        install_rpath : rootlibexecdir,
+        install : true)
+
+public_programs += executable(
+        'systemd-path',
+        'src/path/path.c',
+        include_directories : includes,
+        link_with : [libshared],
+        install_rpath : rootlibexecdir,
+        install : true)
+
+public_programs += executable(
+        'systemd-ask-password',
+        'src/ask-password/ask-password.c',
+        include_directories : includes,
+        link_with : [libshared],
+        install_rpath : rootlibexecdir,
+        install : true,
+        install_dir : rootbindir)
+
+executable(
+        'systemd-reply-password',
+        'src/reply-password/reply-password.c',
+        include_directories : includes,
+        link_with : [libshared],
+        install_rpath : rootlibexecdir,
+        install : true,
+        install_dir : rootlibexecdir)
+
+public_programs += executable(
+        'systemd-tty-ask-password-agent',
+        'src/tty-ask-password-agent/tty-ask-password-agent.c',
+        include_directories : includes,
+        link_with : [libshared],
+        install_rpath : rootlibexecdir,
+        install : true,
+        install_dir : rootbindir)
+
+public_programs += executable(
+        'systemd-cgls',
+        'src/cgls/cgls.c',
+        include_directories : includes,
+        link_with : [libshared],
+        install_rpath : rootlibexecdir,
+        install : true)
+
+public_programs += executable(
+        'systemd-cgtop',
+        'src/cgtop/cgtop.c',
+        include_directories : includes,
+        link_with : [libshared],
+        install_rpath : rootlibexecdir,
+        install : true)
+
+executable(
+        'systemd-initctl',
+        'src/initctl/initctl.c',
+        include_directories : includes,
+        link_with : [libshared],
+        install_rpath : rootlibexecdir,
+        install : (conf.get('HAVE_SYSV_COMPAT') == 1),
+        install_dir : rootlibexecdir)
+
+public_programs += executable(
+        'systemd-mount',
+        'src/mount/mount-tool.c',
+        include_directories : includes,
+        link_with : [libshared],
+        dependencies: [libmount],
+        install_rpath : rootlibexecdir,
+        install : true)
 
 meson.add_install_script(meson_make_symlink,
                          'systemd-mount', join_paths(bindir, 'systemd-umount'))
 
-exe = executable('systemd-run',
-                 'src/run/run.c',
-                 include_directories : includes,
-                 link_with : [libshared],
-                 install_rpath : rootlibexecdir,
-                 install : true)
-public_programs += exe
-
-exe = executable('systemd-stdio-bridge',
-                 'src/stdio-bridge/stdio-bridge.c',
-                 include_directories : includes,
-                 link_with : [libshared],
-                 dependencies : [versiondep],
-                 install_rpath : rootlibexecdir,
-                 install : true)
-public_programs += exe
-
-exe = executable('busctl',
-                 'src/busctl/busctl.c',
-                 'src/busctl/busctl-introspect.c',
-                 'src/busctl/busctl-introspect.h',
-                 include_directories : includes,
-                 link_with : [libshared],
-                 install_rpath : rootlibexecdir,
-                 install : true)
-public_programs += exe
+public_programs += executable(
+        'systemd-run',
+        'src/run/run.c',
+        include_directories : includes,
+        link_with : [libshared],
+        install_rpath : rootlibexecdir,
+        install : true)
+
+public_programs += executable(
+        'systemd-stdio-bridge',
+        'src/stdio-bridge/stdio-bridge.c',
+        include_directories : includes,
+        link_with : [libshared],
+        dependencies : [versiondep],
+        install_rpath : rootlibexecdir,
+        install : true)
+
+public_programs += executable(
+        'busctl',
+        'src/busctl/busctl.c',
+        'src/busctl/busctl-introspect.c',
+        'src/busctl/busctl-introspect.h',
+        include_directories : includes,
+        link_with : [libshared],
+        install_rpath : rootlibexecdir,
+        install : true)
 
 if conf.get('ENABLE_SYSUSERS') == 1
-        exe = executable('systemd-sysusers',
-                         'src/sysusers/sysusers.c',
-                         include_directories : includes,
-                         link_with : [libshared],
-                         install_rpath : rootlibexecdir,
-                         install : true,
-                         install_dir : rootbindir)
-        public_programs += exe
+        public_programs += executable(
+                'systemd-sysusers',
+                'src/sysusers/sysusers.c',
+                include_directories : includes,
+                link_with : [libshared],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootbindir)
+
+        if have_standalone_binaries
+                public_programs += executable(
+                        'systemd-sysusers.standalone',
+                        'src/sysusers/sysusers.c',
+                        include_directories : includes,
+                        link_with : [libshared_static,
+                                     libbasic,
+                                     libbasic_gcrypt,
+                                     libsystemd_static,
+                                     libjournal_client],
+                        install : true,
+                        install_dir : rootbindir)
+        endif
 endif
 
 if conf.get('ENABLE_TMPFILES') == 1
-        exe = executable('systemd-tmpfiles',
-                         'src/tmpfiles/tmpfiles.c',
-                         include_directories : includes,
-                         link_with : [libshared],
-                         dependencies : [libacl],
-                         install_rpath : rootlibexecdir,
-                         install : true,
-                         install_dir : rootbindir)
+        exe = executable(
+                'systemd-tmpfiles',
+                systemd_tmpfiles_sources,
+                include_directories : includes,
+                link_with : [libshared],
+                dependencies : [libacl],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootbindir)
         public_programs += exe
 
         if want_tests != 'false'
@@ -2814,112 +2989,114 @@ if conf.get('ENABLE_TMPFILES') == 1
                      # https://github.com/mesonbuild/meson/issues/2681
                      args : exe.full_path())
         endif
+
+        if have_standalone_binaries
+                public_programs += executable(
+                        'systemd-tmpfiles.standalone',
+                        systemd_tmpfiles_sources,
+                        include_directories : includes,
+                        link_with : [libshared_static,
+                                     libbasic,
+                                     libbasic_gcrypt,
+                                     libsystemd_static,
+                                     libjournal_client],
+                        dependencies : [libacl],
+                        install : true,
+                        install_dir : rootbindir)
+        endif
 endif
 
 if conf.get('ENABLE_HWDB') == 1
-        exe = executable('systemd-hwdb',
-                         'src/hwdb/hwdb.c',
-                         'src/libsystemd/sd-hwdb/hwdb-internal.h',
-                         include_directories : includes,
-                         link_with : [libudev_static],
-                         install_rpath : udev_rpath,
-                         install : true,
-                         install_dir : rootbindir)
-        public_programs += exe
+        public_programs += executable(
+                'systemd-hwdb',
+                'src/hwdb/hwdb.c',
+                'src/libsystemd/sd-hwdb/hwdb-internal.h',
+                include_directories : includes,
+                link_with : [libudev_static],
+                install_rpath : udev_rpath,
+                install : true,
+                install_dir : rootbindir)
 endif
 
 if conf.get('ENABLE_QUOTACHECK') == 1
-        executable('systemd-quotacheck',
-                   'src/quotacheck/quotacheck.c',
-                   include_directories : includes,
-                   link_with : [libshared],
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : rootlibexecdir)
-endif
-
-exe = executable('systemd-socket-proxyd',
-                 'src/socket-proxy/socket-proxyd.c',
-                 include_directories : includes,
-                 link_with : [libshared],
-                 dependencies : [threads],
-                 install_rpath : rootlibexecdir,
-                 install : true,
-                 install_dir : rootlibexecdir)
-public_programs += exe
-
-exe = executable('systemd-udevd',
-                 systemd_udevd_sources,
-                 include_directories : includes,
-                 c_args : '-DLOG_REALM=LOG_REALM_UDEV',
-                 link_with : [libudev_core,
-                              libsystemd_network,
-                              libudev_static],
-                 dependencies : [versiondep,
-                                 threads,
-                                 libkmod,
-                                 libidn,
-                                 libacl,
-                                 libblkid],
-                 install_rpath : udev_rpath,
-                 install : true,
-                 install_dir : rootlibexecdir)
-public_programs += exe
-
-exe = executable('udevadm',
-                 udevadm_sources,
-                 c_args : '-DLOG_REALM=LOG_REALM_UDEV',
-                 include_directories : includes,
-                 link_with : [libudev_core,
-                              libsystemd_network,
-                              libudev_static],
-                 dependencies : [versiondep,
-                                 threads,
-                                 libkmod,
-                                 libidn,
-                                 libacl,
-                                 libblkid],
-                 install_rpath : udev_rpath,
-                 install : true,
-                 install_dir : rootbindir)
-public_programs += exe
-
-executable('systemd-shutdown',
-           systemd_shutdown_sources,
-           include_directories : includes,
-           link_with : [libcore_shared,
-                        libshared],
-           dependencies : [libmount],
-           install_rpath : rootlibexecdir,
-           install : true,
-           install_dir : rootlibexecdir)
+        executable(
+                'systemd-quotacheck',
+                'src/quotacheck/quotacheck.c',
+                include_directories : includes,
+                link_with : [libshared],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
+endif
+
+public_programs += executable(
+        'systemd-socket-proxyd',
+        'src/socket-proxy/socket-proxyd.c',
+        include_directories : includes,
+        link_with : [libshared],
+        dependencies : [threads],
+        install_rpath : rootlibexecdir,
+        install : true,
+        install_dir : rootlibexecdir)
 
-executable('systemd-update-done',
-           'src/update-done/update-done.c',
-           include_directories : includes,
-           link_with : [libshared],
-           install_rpath : rootlibexecdir,
-           install : true,
-           install_dir : rootlibexecdir)
+public_programs += executable(
+        'udevadm',
+        udevadm_sources,
+        c_args : '-DLOG_REALM=LOG_REALM_UDEV',
+        include_directories : includes,
+        link_with : [libudev_core,
+                     libsystemd_network,
+                     libudev_static],
+        dependencies : [versiondep,
+                        threads,
+                        libkmod,
+                        libidn,
+                        libacl,
+                        libblkid],
+        install_rpath : udev_rpath,
+        install : true,
+        install_dir : rootbindir)
 
-executable('systemd-update-utmp',
-           'src/update-utmp/update-utmp.c',
-           include_directories : includes,
-           link_with : [libshared],
-           dependencies : [libaudit],
-           install_rpath : rootlibexecdir,
-           install : true,
-           install_dir : rootlibexecdir)
+executable(
+        'systemd-shutdown',
+        systemd_shutdown_sources,
+        include_directories : includes,
+        link_with : [libcore_shared,
+                     libshared],
+        dependencies : [libmount],
+        install_rpath : rootlibexecdir,
+        install : true,
+        install_dir : rootlibexecdir)
+
+executable(
+        'systemd-update-done',
+        'src/update-done/update-done.c',
+        include_directories : includes,
+        link_with : [libshared],
+        install_rpath : rootlibexecdir,
+        install : true,
+        install_dir : rootlibexecdir)
+
+executable(
+        'systemd-update-utmp',
+        'src/update-utmp/update-utmp.c',
+        include_directories : includes,
+        link_with : [libshared],
+        dependencies : [libaudit],
+        install_rpath : rootlibexecdir,
+        install : (conf.get('ENABLE_UTMP') == 1),
+        install_dir : rootlibexecdir)
 
 if conf.get('HAVE_KMOD') == 1
-        executable('systemd-modules-load',
-                   'src/modules-load/modules-load.c',
-                   include_directories : includes,
-                   link_with : [libshared],
-                   dependencies : [libkmod],
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : rootlibexecdir)
+        executable(
+                'systemd-modules-load',
+                'src/modules-load/modules-load.c',
+                include_directories : includes,
+                link_with : [libshared],
+                dependencies : [libkmod],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
 
         meson.add_install_script('sh', '-c',
                                  mkdir_p.format(modulesloaddir))
@@ -2927,66 +3104,77 @@ if conf.get('HAVE_KMOD') == 1
                                  mkdir_p.format(join_paths(sysconfdir, 'modules-load.d')))
 endif
 
-exe = executable('systemd-nspawn',
-                 systemd_nspawn_sources,
-                 include_directories : includes,
-                 link_with : [libcore_shared,
-                              libnspawn_core,
-                              libshared],
-                 dependencies : [libblkid,
-                                 libseccomp],
-                 install_rpath : rootlibexecdir,
-                 install : true)
-public_programs += exe
+public_programs += executable(
+        'systemd-nspawn',
+        systemd_nspawn_sources,
+        include_directories : includes,
+        link_with : [libcore_shared,
+                     libnspawn_core,
+                     libshared],
+        dependencies : [libblkid,
+                        libseccomp],
+        install_rpath : rootlibexecdir,
+        install : true)
 
 if conf.get('ENABLE_NETWORKD') == 1
-        executable('systemd-networkd',
-                   systemd_networkd_sources,
-                   include_directories : network_include_dir,
-                   link_with : [libnetworkd_core,
-                                libsystemd_network,
-                                libudev_static,
-                                networkd_link_with],
-                   dependencies : [threads],
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : rootlibexecdir)
-
-        executable('systemd-networkd-wait-online',
-                   systemd_networkd_wait_online_sources,
-                   include_directories : includes,
-                   link_with : [libnetworkd_core,
-                                networkd_link_with],
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : rootlibexecdir)
-
-        exe = executable('networkctl',
-                   networkctl_sources,
-                   include_directories : includes,
-                   link_with : [libsystemd_network,
-                              networkd_link_with],
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : rootbindir)
-        public_programs += exe
+        executable(
+                'systemd-networkd',
+                systemd_networkd_sources,
+                include_directories : network_include_dir,
+                link_with : [libnetworkd_core,
+                             libsystemd_network,
+                             libudev_static,
+                             networkd_link_with],
+                dependencies : [threads],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
+
+        executable(
+                'systemd-networkd-wait-online',
+                systemd_networkd_wait_online_sources,
+                include_directories : includes,
+                link_with : [libnetworkd_core,
+                             networkd_link_with],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
+
+        public_programs += executable(
+                'networkctl',
+                networkctl_sources,
+                include_directories : includes,
+                link_with : [libsystemd_network,
+                             networkd_link_with],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootbindir)
+
+        exe = executable(
+                'systemd-network-generator',
+                network_generator_sources,
+                include_directories : includes,
+                link_with : [networkd_link_with],
+                install_rpath : rootlibexecdir,
+                install : true,
+                install_dir : rootlibexecdir)
 
-        executable('systemd-network-generator',
-                   network_generator_sources,
-                   include_directories : includes,
-                   link_with : [networkd_link_with],
-                   install_rpath : rootlibexecdir,
-                   install : true,
-                   install_dir : rootlibexecdir)
+        if want_tests != 'false'
+                test('test-network-generator-conversion',
+                     test_network_generator_conversion_sh,
+                     # https://github.com/mesonbuild/meson/issues/2681
+                     args : exe.full_path())
+        endif
 endif
 
-executable('systemd-sulogin-shell',
-           ['src/sulogin-shell/sulogin-shell.c'],
-           include_directories : includes,
-           link_with : [libshared],
-           install_rpath : rootlibexecdir,
-           install : true,
-           install_dir : rootlibexecdir)
+executable(
+        'systemd-sulogin-shell',
+        ['src/sulogin-shell/sulogin-shell.c'],
+        include_directories : includes,
+        link_with : [libshared],
+        install_rpath : rootlibexecdir,
+        install : true,
+        install_dir : rootlibexecdir)
 
 ############################################################
 
@@ -2995,7 +3183,7 @@ custom_target(
         output : 'systemd-runtest.env',
         command : ['sh', '-c', '{ ' +
                    'echo SYSTEMD_TEST_DATA=@0@; '.format(join_paths(project_source_root, 'test')) +
-                   'echo SYSTEMD_CATALOG_DIR=@0@; '.format(join_paths(meson.current_build_dir(), 'catalog')) +
+                   'echo SYSTEMD_CATALOG_DIR=@0@; '.format(join_paths(project_build_root, 'catalog')) +
                    '} >@OUTPUT@'],
         build_by_default : true)
 
@@ -3100,45 +3288,42 @@ endif
 fuzzer_exes = []
 
 if get_option('tests') != 'false'
-foreach tuple : fuzzers
-        sources = tuple[0]
-        link_with = tuple[1].length() > 0 ? tuple[1] : [libshared]
-        dependencies = tuple[2]
-        defs = tuple.length() >= 4 ? tuple[3] : []
-        incs = tuple.length() >= 5 ? tuple[4] : includes
-        link_args = []
-
-        if want_ossfuzz or want_fuzzbuzz
-                dependencies += fuzzing_engine
-        elif want_libfuzzer
-                if fuzzing_engine.found()
+        foreach tuple : fuzzers
+                sources = tuple[0]
+                link_with = tuple[1].length() > 0 ? tuple[1] : [libshared]
+                dependencies = tuple[2]
+                defs = tuple.length() >= 4 ? tuple[3] : []
+                incs = tuple.length() >= 5 ? tuple[4] : includes
+                link_args = []
+
+                if want_ossfuzz
                         dependencies += fuzzing_engine
+                elif want_libfuzzer
+                        if fuzzing_engine.found()
+                                dependencies += fuzzing_engine
+                        else
+                                link_args += ['-fsanitize=fuzzer']
+                        endif
                 else
-                        link_args += ['-fsanitize=fuzzer']
+                        sources += 'src/fuzz/fuzz-main.c'
                 endif
-        else
-                sources += 'src/fuzz/fuzz-main.c'
-        endif
 
-        if want_fuzzbuzz
-                sources += 'src/fuzz/fuzzer-entry-point.c'
-        endif
+                name = sources[0].split('/')[-1].split('.')[0]
 
-        name = sources[0].split('/')[-1].split('.')[0]
-
-        fuzzer_exes += executable(
-                name,
-                sources,
-                include_directories : [incs, include_directories('src/fuzz')],
-                link_with : link_with,
-                dependencies : dependencies,
-                c_args : defs,
-                link_args: link_args,
-                install : false)
-endforeach
+                fuzzer_exes += executable(
+                        name,
+                        sources,
+                        include_directories : [incs, include_directories('src/fuzz')],
+                        link_with : link_with,
+                        dependencies : dependencies,
+                        c_args : defs,
+                        link_args: link_args,
+                        install : false)
+        endforeach
 endif
 
-run_target('fuzzers',
+run_target(
+        'fuzzers',
         depends : fuzzer_exes,
         command : ['true'])
 
@@ -3147,8 +3332,8 @@ run_target('fuzzers',
 make_directive_index_py = find_program('tools/make-directive-index.py')
 make_man_index_py = find_program('tools/make-man-index.py')
 xml_helper_py = find_program('tools/xml_helper.py')
-hwdb_update_sh = find_program('tools/meson-hwdb-update.sh')
-autosuspend_update_sh = find_program('tools/meson-autosuspend-update.sh')
+hwdb_update_sh = find_program('tools/hwdb-update.sh')
+autosuspend_update_sh = find_program('tools/autosuspend-update.sh')
 
 subdir('sysctl.d')
 subdir('sysusers.d')
@@ -3189,13 +3374,13 @@ meson.add_install_script('sh', '-c', 'touch $DESTDIR@0@'.format(prefixdir))
 
 ############################################################
 
-meson_check_help = find_program('tools/meson-check-help.sh')
+check_help = find_program('tools/check-help.sh')
 
 foreach exec : public_programs
         name = exec.full_path().split('/')[-1]
         if want_tests != 'false'
                 test('check-help-' + name,
-                     meson_check_help,
+                     check_help,
                      args : exec.full_path())
         endif
 endforeach
@@ -3228,7 +3413,7 @@ foreach tuple : sanitizers
                         if name != prev
                                 if want_tests == 'false'
                                         message('Not compiling @0@ because tests is set to false'.format(name))
-                                elif slow_tests
+                                elif slow_tests or fuzz_tests
                                         exe = custom_target(
                                                 name,
                                                 output : name,
@@ -3238,14 +3423,16 @@ foreach tuple : sanitizers
                                                            '@OUTPUT@'],
                                                 build_by_default : true)
                                 else
-                                        message('Not compiling @0@ because slow-tests is set to false'.format(name))
+                                        message('Not compiling @0@ because slow-tests/fuzz-tests is set to false'.format(name))
                                 endif
                         endif
                         prev = name
 
-                        if want_tests != 'false' and slow_tests
+                        if want_tests != 'false' and (slow_tests or fuzz_tests)
                                 test('@0@:@1@:@2@'.format(b, c, sanitizer),
                                      env,
+                                     env : ['UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1'],
+                                     timeout : 60,
                                      args : [exe.full_path(),
                                              join_paths(project_source_root, p)])
                         endif
@@ -3274,10 +3461,10 @@ if git.found()
 endif
 
 if git.found()
-        meson_git_contrib_sh = find_program('tools/meson-git-contrib.sh')
+        git_contrib_sh = find_program('tools/git-contrib.sh')
         run_target(
                 'git-contrib',
-                command : [meson_git_contrib_sh])
+                command : [git_contrib_sh])
 endif
 
 if git.found()
@@ -3301,11 +3488,11 @@ endif
 
 ############################################################
 
-meson_check_api_docs_sh = find_program('tools/meson-check-api-docs.sh')
+check_api_docs_sh = find_program('tools/check-api-docs.sh')
 run_target(
         'check-api-docs',
         depends : [man, libsystemd, libudev],
-        command : [meson_check_api_docs_sh, libsystemd.full_path(), libudev.full_path()])
+        command : [check_api_docs_sh, libsystemd.full_path(), libudev.full_path()])
 
 ############################################################
 watchdog_opt = service_watchdog == '' ? 'disabled' : service_watchdog
@@ -3354,6 +3541,8 @@ status = [
 
         'default DNSSEC mode:               @0@'.format(default_dnssec),
         'default DNS-over-TLS mode:         @0@'.format(default_dns_over_tls),
+        'default mDNS mode:                 @0@'.format(default_mdns),
+        'default LLMNR mode:                @0@'.format(default_llmnr),
         'default cgroup hierarchy:          @0@'.format(default_hierarchy),
         'default net.naming-scheme setting: @0@'.format(default_net_naming_scheme),
         'default KillUserProcesses setting: @0@'.format(kill_user_processes),
@@ -3403,6 +3592,7 @@ foreach tuple : [
         ['pwquality'],
         ['libfdisk'],
         ['p11kit'],
+        ['libfido2'],
         ['AUDIT'],
         ['IMA'],
         ['AppArmor'],
@@ -3411,6 +3601,7 @@ foreach tuple : [
         ['SMACK'],
         ['zlib'],
         ['xz'],
+        ['zstd'],
         ['lz4'],
         ['bzip2'],
         ['ACL'],
@@ -3421,6 +3612,7 @@ foreach tuple : [
         ['openssl'],
         ['libcurl'],
         ['idn'],
+        ['initrd'],
         ['libidn2'],
         ['libidn'],
         ['libiptc'],
@@ -3436,6 +3628,7 @@ foreach tuple : [
         ['randomseed'],
         ['backlight'],
         ['rfkill'],
+        ['xdg-autostart'],
         ['logind'],
         ['machined'],
         ['portabled'],
@@ -3483,10 +3676,13 @@ foreach tuple : [
         ['debug siphash'],
         ['valgrind',         conf.get('VALGRIND') == 1],
         ['trace logging',    conf.get('LOG_TRACE') == 1],
+        ['install tests',    install_tests],
         ['link-udev-shared',      get_option('link-udev-shared')],
         ['link-systemctl-shared', get_option('link-systemctl-shared')],
         ['link-networkd-shared',  get_option('link-networkd-shared')],
         ['link-timesyncd-shared', get_option('link-timesyncd-shared')],
+        ['kernel-install',        get_option('kernel-install')],
+        ['systemd-analyze',       get_option('analyze')],
 ]
 
         if tuple.length() >= 2
index 212724d7dac681f03571676d3ce25d0fbe60ca94..fd73d5e681f4e50b53b560c8f6a7d6bc4c8de990 100644 (file)
@@ -26,6 +26,8 @@ option('static-libsystemd', type : 'combo',
 option('static-libudev', type : 'combo',
        choices : ['false', 'true', 'pic', 'no-pic'],
        description : '''install a static library for libudev''')
+option('standalone-binaries', type : 'boolean', value : 'false',
+       description : '''also build standalone versions of supported binaries''')
 
 option('sysvinit-path', type : 'string', value : '/etc/init.d',
        description : 'the directory where the SysV init scripts are located')
@@ -35,6 +37,8 @@ option('telinit-path', type : 'string', value : '/lib/sysvinit/telinit',
        description : 'path to telinit')
 option('rc-local', type : 'string',
        value : '/etc/rc.local')
+option('initrd', type: 'boolean',
+       description : 'install services for use when running systemd in initrd')
 
 option('quotaon-path', type : 'string', description : 'path to quotaon')
 option('quotacheck-path', type : 'string', description : 'path to quotacheck')
@@ -140,6 +144,8 @@ option('hwdb', type : 'boolean',
        description : 'support for the hardware database')
 option('rfkill', type : 'boolean',
        description : 'support for the rfkill tools')
+option('xdg-autostart', type : 'boolean',
+       description : 'install the xdg-autostart-generator and unit')
 option('man', type : 'combo', choices : ['auto', 'true', 'false'],
        value : 'false',
        description : 'build and install man pages')
@@ -230,6 +236,14 @@ option('default-dns-over-tls', type : 'combo',
        description : 'default DNS-over-TLS mode',
        choices : ['yes', 'opportunistic', 'no'],
        value : 'no')
+option('default-mdns', type : 'combo',
+       choices : ['yes', 'resolve', 'no'],
+       description : 'default MulticastDNS mode',
+       value : 'yes')
+option('default-llmnr', type : 'combo',
+       choices : ['yes', 'resolve', 'no'],
+       description : 'default LLMNR mode',
+       value : 'yes')
 option('dns-over-tls', type : 'combo', choices : ['auto', 'gnutls', 'openssl', 'true', 'false'],
        description : 'DNS-over-TLS support')
 option('dns-servers', type : 'string',
@@ -281,7 +295,7 @@ option('libcryptsetup', type : 'combo', choices : ['auto', 'true', 'false'],
 option('libcurl', type : 'combo', choices : ['auto', 'true', 'false'],
        description : 'libcurl support')
 option('idn', type : 'boolean',
-       description : 'use IDN when printing host names')
+       description : 'use IDN when printing hostnames')
 option('libidn2', type : 'combo', choices : ['auto', 'true', 'false'],
        description : 'libidn2 support')
 option('libidn', type : 'combo', choices : ['auto', 'true', 'false'],
@@ -298,6 +312,8 @@ option('openssl', type : 'combo', choices : ['auto', 'true', 'false'],
        description : 'openssl support')
 option('p11kit', type : 'combo', choices : ['auto', 'true', 'false'],
        description : 'p11kit support')
+option('libfido2', type : 'combo', choices : ['auto', 'true', 'false'],
+       description : 'FIDO2 support')
 option('elfutils', type : 'combo', choices : ['auto', 'true', 'false'],
        description : 'elfutils support')
 option('zlib', type : 'combo', choices : ['auto', 'true', 'false'],
@@ -308,6 +324,8 @@ option('xz', type : 'combo', choices : ['auto', 'true', 'false'],
        description : 'xz compression support')
 option('lz4', type : 'combo', choices : ['auto', 'true', 'false'],
        description : 'lz4 compression support')
+option('zstd', type : 'combo', choices : ['auto', 'true', 'false'],
+       description : 'zstd compression support')
 option('xkbcommon', type : 'combo', choices : ['auto', 'true', 'false'],
        description : 'xkbcommon keymap support')
 option('pcre2', type : 'combo', choices : ['auto', 'true', 'false'],
@@ -339,6 +357,8 @@ option('tests', type : 'combo', choices : ['true', 'unsafe', 'false'],
        description : 'enable extra tests with =unsafe')
 option('slow-tests', type : 'boolean', value : 'false',
        description : 'run the slow tests by default')
+option('fuzz-tests', type : 'boolean', value : 'false',
+       description : 'run the fuzzer regression tests by default')
 option('install-tests', type : 'boolean', value : 'false',
        description : 'install test executables')
 
@@ -354,9 +374,7 @@ option('oss-fuzz', type : 'boolean', value : 'false',
        description : 'build against oss-fuzz')
 option('llvm-fuzz', type : 'boolean', value : 'false',
        description : 'build against LLVM libFuzzer')
-option('fuzzbuzz', type : 'boolean', value : 'false',
-       description : 'build against FuzzBuzz')
-option('fuzzbuzz-engine', type : 'string',
-       description : 'the name of the FuzzBuzz fuzzing engine')
-option('fuzzbuzz-engine-dir', type : 'string',
-       description : 'the directory where the FuzzBuzz fuzzing engine is')
+option('kernel-install', type: 'boolean', value: 'true',
+       description : 'install kernel-install and associated files')
+option('analyze', type: 'boolean', value: 'true',
+       description : 'install systemd-analyze')
index 16ac2e9d0d6952949b1be240962d94167666df77..4a13f1075eb47987528b85997653dc7151a0e5b5 100755 (executable)
@@ -42,6 +42,14 @@ fi
 if [ ! -f "$BUILDDIR"/build.ninja ] ; then
         sysvinit_path=`realpath /etc/init.d`
 
+        init_path=`realpath /sbin/init 2>/dev/null`
+        if [ -z "$init_path" ] ; then
+            rootprefix=""
+        else
+            rootprefix=${init_path%/lib/systemd/systemd}
+            rootprefix=/${rootprefix#/}
+        fi
+
         nobody_user=`id -u -n 65534 2> /dev/null`
         if [ "$nobody_user" != "" ] ; then
                 # Validate that we can translate forth and back
@@ -76,11 +84,17 @@ if [ ! -f "$BUILDDIR"/build.ninja ] ; then
                 fi
         fi
 
-        meson "$BUILDDIR" -D "sysvinit-path=$sysvinit_path" -D default-hierarchy=unified -D man=false -D "nobody-user=$nobody_user" -D "nobody-group=$nobody_group"
+        meson "$BUILDDIR" -D "sysvinit-path=$sysvinit_path" -D "rootprefix=$rootprefix" -D default-hierarchy=unified -D man=false -D "nobody-user=$nobody_user" -D "nobody-group=$nobody_group"
 fi
 
 ninja -C "$BUILDDIR" all
-[ "$WITH_TESTS" = 0 ] || ninja -C "$BUILDDIR" test
+if [ "$WITH_TESTS" = 1 ] ; then
+        for id in 1 2 3; do
+                groupadd -g $id testgroup$id || :
+        done
+
+        ninja -C "$BUILDDIR" test
+fi
 ninja -C "$BUILDDIR" install
 
 mkdir -p "$DESTDIR"/etc
diff --git a/mkosi.default b/mkosi.default
deleted file mode 120000 (symlink)
index 2718c9e..0000000
+++ /dev/null
@@ -1 +0,0 @@
-.mkosi/mkosi.fedora
\ No newline at end of file
diff --git a/network/80-vm-vt.network b/network/80-vm-vt.network
new file mode 100644 (file)
index 0000000..4144034
--- /dev/null
@@ -0,0 +1,24 @@
+#  SPDX-License-Identifier: LGPL-2.1+
+#
+#  This file is part of systemd.
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU Lesser General Public License as published by
+#  the Free Software Foundation; either version 2.1 of the License, or
+#  (at your option) any later version.
+
+# This network file matches vt-* TUN/TAP devices and applies a similar
+# configuration as ve-* to provide NAT/DHCP to VMs.
+
+[Match]
+Name=vt-*
+Driver=tun
+
+[Network]
+# Default to using a /28 prefix, giving up to 13 addresses per VM.
+Address=0.0.0.0/28
+LinkLocalAddressing=yes
+DHCPServer=yes
+IPMasquerade=yes
+LLDP=yes
+EmitLLDP=customer-bridge
index 544dcf4387c6334dd55570f5f2e7c652fadbedaf..99a650eac3a683f780d3958f2b3ef341c0bca7cd 100644 (file)
@@ -4,6 +4,7 @@ if conf.get('ENABLE_NETWORKD') == 1
         install_data('80-container-host0.network',
                      '80-container-ve.network',
                      '80-container-vz.network',
+                     '80-vm-vt.network',
                      '80-wifi-adhoc.network',
                      '80-wifi-ap.network.example',
                      '80-wifi-station.network.example',
index 55d3c82287039a50f41bd56eba223237cfc121ec..65e5064fe06c16ba0751bb8b22085560f88259f3 100644 (file)
--- a/po/be.po
+++ b/po/be.po
@@ -70,21 +70,21 @@ msgid "Authentication is required to reload the systemd state."
 msgstr "Неабходна аўтэнтыфікацыя для перачытання стану systemd."
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:1
-msgid "Set host name"
+msgid "Set hostname"
 msgstr "Усталяваць імя вузла"
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:2
-msgid "Authentication is required to set the local host name."
+msgid "Authentication is required to set the local hostname."
 msgstr "Неабходна аўтэнтыфікацыя для ўсталявання імя вузла."
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:3
-msgid "Set static host name"
+msgid "Set static hostname"
 msgstr "Усталяваць статычнае імя вузла"
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:4
 msgid ""
-"Authentication is required to set the statically configured local host name, "
-"as well as the pretty host name."
+"Authentication is required to set the statically configured local hostname, "
+"as well as the pretty hostname."
 msgstr ""
 "Неабходна аўтэнтыфікацыя для ўсталявання як статычнага так і прыгожага імя "
 "вузла."
index bbd9223b748763bf772671c22f462e4eca486844..1ce3efa8f532c3bdafcc1b952e11251dad85df27 100644 (file)
@@ -70,21 +70,21 @@ msgid "Authentication is required to reload the systemd state."
 msgstr "Nieabchodna aŭtentyfikacyja dlia pieračytannia stanu systemd."
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:1
-msgid "Set host name"
+msgid "Set hostname"
 msgstr "Ustaliavać imia vuzla"
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:2
-msgid "Authentication is required to set the local host name."
+msgid "Authentication is required to set the local hostname."
 msgstr "Nieabchodna aŭtentyfikacyja dlia ŭstaliavannia imia vuzla."
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:3
-msgid "Set static host name"
+msgid "Set static hostname"
 msgstr "Ustaliavać statyčnaje imia vuzla"
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:4
 msgid ""
-"Authentication is required to set the statically configured local host name, "
-"as well as the pretty host name."
+"Authentication is required to set the statically configured local hostname, "
+"as well as the pretty hostname."
 msgstr ""
 "Nieabchodna aŭtentyfikacyja dlia ŭstaliavannia jak statyčnaha tak i "
 "pryhožaha imia vuzla."
index 38b952782489768318819a718b5f4cabc990910a..0e1f50736d610dc9ca5ac3c88ae8c42c6cb2c37c 100644 (file)
--- a/po/bg.po
+++ b/po/bg.po
@@ -69,21 +69,21 @@ msgid "Authentication is required to reload the systemd state."
 msgstr "За презареждане на състоянието на systemd е необходима идентификация."
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:1
-msgid "Set host name"
+msgid "Set hostname"
 msgstr "Задаване на име на машината"
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:2
-msgid "Authentication is required to set the local host name."
+msgid "Authentication is required to set the local hostname."
 msgstr "За задаване на име на локалната машина е необходима идентификация."
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:3
-msgid "Set static host name"
+msgid "Set static hostname"
 msgstr "Задаване на статично име на машината"
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:4
 msgid ""
-"Authentication is required to set the statically configured local host name, "
-"as well as the pretty host name."
+"Authentication is required to set the statically configured local hostname, "
+"as well as the pretty hostname."
 msgstr ""
 "За задаване на статично име на локалната машина е необходима идентификация."
 
index dd8cc56e343b9dd85908e515307ad4f549ffc665..68123ca5da6d292c5be9799f9c6dfde7a926d999 100644 (file)
--- a/po/ca.po
+++ b/po/ca.po
@@ -73,21 +73,21 @@ msgid "Authentication is required to reload the systemd state."
 msgstr "Es requereix autenticació per tornar a carregar l'estat de systemd."
 
 #: src/hostname/org.freedesktop.hostname1.policy:22
-msgid "Set host name"
+msgid "Set hostname"
 msgstr "Estableix el nom d'amfitrió"
 
 #: src/hostname/org.freedesktop.hostname1.policy:23
-msgid "Authentication is required to set the local host name."
+msgid "Authentication is required to set the local hostname."
 msgstr "Es requereix autenticació per establir el nom d'amfitrió local."
 
 #: src/hostname/org.freedesktop.hostname1.policy:32
-msgid "Set static host name"
+msgid "Set static hostname"
 msgstr "Estableix el nom d'amfitrió estàtic"
 
 #: src/hostname/org.freedesktop.hostname1.policy:33
 msgid ""
-"Authentication is required to set the statically configured local host name, "
-"as well as the pretty host name."
+"Authentication is required to set the statically configured local hostname, "
+"as well as the pretty hostname."
 msgstr ""
 "Es requereix autenticació per establir el nom d'amfitrió local configurat "
 "estàticament, així com el nom bonic d'amfitrió."
index 819800132a4d30af65d9df283fa748dabee66316..8805dce0dbb53c24be67efd925e9a617148f9c63 100644 (file)
--- a/po/cs.po
+++ b/po/cs.po
@@ -7,8 +7,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: systemd master\n"
 "Report-Msgid-Bugs-To: https://github.com/systemd/systemd/issues\n"
-"POT-Creation-Date: 2020-02-29 15:12+0000\n"
-"PO-Revision-Date: 2020-03-01 13:58+0100\n"
+"POT-Creation-Date: 2020-05-30 13:27+0000\n"
+"PO-Revision-Date: 2020-07-01 16:40+0200\n"
 "Last-Translator: Daniel Rusek <mail@asciiwolf.com>\n"
 "Language-Team: Czech\n"
 "Language: cs\n"
@@ -17,7 +17,7 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
 "|| n%100>=20) ? 1 : 2);\n"
-"X-Generator: Poedit 2.3\n"
+"X-Generator: Poedit 2.3.1\n"
 
 #: src/core/org.freedesktop.systemd1.policy.in:22
 msgid "Send passphrase back to system"
@@ -120,21 +120,21 @@ msgid ""
 msgstr "Pro změnu hesla domovského adresáře uživatele je vyžadováno ověření."
 
 #: src/hostname/org.freedesktop.hostname1.policy:20
-msgid "Set host name"
+msgid "Set hostname"
 msgstr "Nastavit název stroje"
 
 #: src/hostname/org.freedesktop.hostname1.policy:21
-msgid "Authentication is required to set the local host name."
+msgid "Authentication is required to set the local hostname."
 msgstr "Pro nastavení lokálního názvu stroje je vyžadováno ověření."
 
 #: src/hostname/org.freedesktop.hostname1.policy:30
-msgid "Set static host name"
+msgid "Set static hostname"
 msgstr "Nastavit statický název stroje"
 
 #: src/hostname/org.freedesktop.hostname1.policy:31
 msgid ""
-"Authentication is required to set the statically configured local host name, "
-"as well as the pretty host name."
+"Authentication is required to set the statically configured local hostname, "
+"as well as the pretty hostname."
 msgstr ""
 "Pro nastavení staticky konfigurovaného názvu lokálního stroje, stejně tak "
 "pro změnu uživatelsky přívětivého jména je vyžadováno ověření."
@@ -740,26 +740,34 @@ msgid "Authentication is required to reset DNS settings."
 msgstr "Pro resetování nastavení DNS je vyžadováno ověření."
 
 #: src/network/org.freedesktop.network1.policy:143
+msgid "DHCP server sends force renew message"
+msgstr "DHCP server posílá zprávu vynuceného obnovení"
+
+#: src/network/org.freedesktop.network1.policy:144
+msgid "Authentication is required to send force renew message."
+msgstr "Pro poslání zprávy vynuceného obnovení je vyžadováno ověření."
+
+#: src/network/org.freedesktop.network1.policy:154
 msgid "Renew dynamic addresses"
 msgstr "Obnovit dynamické adresy"
 
-#: src/network/org.freedesktop.network1.policy:144
+#: src/network/org.freedesktop.network1.policy:155
 msgid "Authentication is required to renew dynamic addresses."
 msgstr "Pro obnovení dynamických adres je vyžadováno ověření."
 
-#: src/network/org.freedesktop.network1.policy:154
+#: src/network/org.freedesktop.network1.policy:165
 msgid "Reload network settings"
 msgstr "Znovu načíst nastavení sítě"
 
-#: src/network/org.freedesktop.network1.policy:155
+#: src/network/org.freedesktop.network1.policy:166
 msgid "Authentication is required to reload network settings."
 msgstr "Pro opětovné načtení nastavení sítě je vyžadováno ověření."
 
-#: src/network/org.freedesktop.network1.policy:165
+#: src/network/org.freedesktop.network1.policy:176
 msgid "Reconfigure network interface"
 msgstr "Přenastavit síťové rozhraní"
 
-#: src/network/org.freedesktop.network1.policy:166
+#: src/network/org.freedesktop.network1.policy:177
 msgid "Authentication is required to reconfigure network interface."
 msgstr "Pro přenastavení síťového rozhraní je vyžadováno ověření."
 
@@ -853,40 +861,47 @@ msgid ""
 "shall be enabled."
 msgstr "Pro kontrolu synchronizace času ze sítě je vyžadováno ověření."
 
-#: src/core/dbus-unit.c:356
+#: src/core/dbus-unit.c:358
 msgid "Authentication is required to start '$(unit)'."
 msgstr "Pro spuštění „$(unit)” je vyžadováno ověření."
 
-#: src/core/dbus-unit.c:357
+#: src/core/dbus-unit.c:359
 msgid "Authentication is required to stop '$(unit)'."
 msgstr "Pro vypnutí „$(unit)” je vyžadováno ověření."
 
-#: src/core/dbus-unit.c:358
+#: src/core/dbus-unit.c:360
 msgid "Authentication is required to reload '$(unit)'."
 msgstr "Pro opětovné načtení „$(unit)” je vyžadováno ověření."
 
-#: src/core/dbus-unit.c:359 src/core/dbus-unit.c:360
+#: src/core/dbus-unit.c:361 src/core/dbus-unit.c:362
 msgid "Authentication is required to restart '$(unit)'."
 msgstr "Pro restart „$(unit)” je vyžadováno ověření."
 
-#: src/core/dbus-unit.c:532
+#: src/core/dbus-unit.c:534
 msgid ""
 "Authentication is required to send a UNIX signal to the processes of "
 "'$(unit)'."
 msgstr "Pro odeslání UNIX signálu procesům „$(unit)” je vyžadováno ověření."
 
-#: src/core/dbus-unit.c:563
+#: src/core/dbus-unit.c:565
 msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
 msgstr "Pro resetování chybného stavu „$(unit)” je vyžadováno ověření."
 
-#: src/core/dbus-unit.c:596
+#: src/core/dbus-unit.c:598
 msgid "Authentication is required to set properties on '$(unit)'."
 msgstr "Pro nastavení vlastností na „$(unit)” je vyžadováno ověření."
 
-#: src/core/dbus-unit.c:705
+#: src/core/dbus-unit.c:707
 msgid ""
 "Authentication is required to delete files and directories associated with "
 "'$(unit)'."
 msgstr ""
 "Pro odstranění souborů nebo adresářů souvisejících s „$(unit)” je vyžadováno "
 "ověření."
+
+#: src/core/dbus-unit.c:756
+msgid ""
+"Authentication is required to freeze or thaw the processes of '$(unit)' unit."
+msgstr ""
+"Pro zmrazení nebo rozmrazení procesů jednotky „$(unit)” je vyžadováno "
+"ověření."
index 7dee612867656dad3a9cbe49187c8e550a0a3d9e..276c9e10ce6973ca1a326db3c9caac24275dd294 100644 (file)
--- a/po/da.po
+++ b/po/da.po
@@ -66,21 +66,21 @@ msgid "Authentication is required to reload the systemd state."
 msgstr "Autentificering er nødvendig for at genindlæse systemd tilstanden."
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:1
-msgid "Set host name"
+msgid "Set hostname"
 msgstr "Sæt værtsnavn"
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:2
-msgid "Authentication is required to set the local host name."
+msgid "Authentication is required to set the local hostname."
 msgstr "Autentificering er nødvendig for at sætte værtsnavn."
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:3
-msgid "Set static host name"
+msgid "Set static hostname"
 msgstr "Sæt statisk værstnavn"
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:4
 msgid ""
-"Authentication is required to set the statically configured local host name, "
-"as well as the pretty host name."
+"Authentication is required to set the statically configured local hostname, "
+"as well as the pretty hostname."
 msgstr ""
 "Autentificering er nødvendig for at sætte det statisk konfigurerede lokale "
 "værtsnavn, lige så vel som det pæne værtsnavn."
index cca79207d417bf5a4c903d7b776599eb0c5217b9..c5c753bb09362497030f8cbfe7e06a59d9669309 100644 (file)
--- a/po/de.po
+++ b/po/de.po
@@ -71,21 +71,21 @@ msgid "Authentication is required to reload the systemd state."
 msgstr "Legitimierung ist zum erneuten Laden des systemd-Zustands notwendig."
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:1
-msgid "Set host name"
+msgid "Set hostname"
 msgstr "Rechnername festlegen"
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:2
-msgid "Authentication is required to set the local host name."
+msgid "Authentication is required to set the local hostname."
 msgstr "Legitimierung ist zum Festlegen des lokalen Rechnernamens notwendig"
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:3
-msgid "Set static host name"
+msgid "Set static hostname"
 msgstr "Statischen Rechnernamen festlegen"
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:4
 msgid ""
-"Authentication is required to set the statically configured local host name, "
-"as well as the pretty host name."
+"Authentication is required to set the statically configured local hostname, "
+"as well as the pretty hostname."
 msgstr ""
 "Authentifizierung ist erforderlich, um den statisch geänderten, lokalen "
 "Rechnernamen, sowie den beschönigten Rechnernamen festzulegen."
index d96bf16264fa1d48df724c3171a12e0fc06cba75..c4225334dfccfb9576fa5a208af40f1c7d3a6f41 100644 (file)
--- a/po/el.po
+++ b/po/el.po
@@ -76,21 +76,21 @@ msgid "Authentication is required to reload the systemd state."
 msgstr "Απαιτείται πιστοποίηση για να ορίσετε την ώρα του συστήματος."
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:1
-msgid "Set host name"
+msgid "Set hostname"
 msgstr "Ορισμός ονόματος οικοδεσπότη"
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:2
-msgid "Authentication is required to set the local host name."
+msgid "Authentication is required to set the local hostname."
 msgstr "Απαιτείται πιστοποίηση για να ορίσετε τοπικά όνομα οικοδεσπότη."
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:3
-msgid "Set static host name"
+msgid "Set static hostname"
 msgstr "Ορισμός στατικού ονόματος οικοδεσπότη"
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:4
 msgid ""
-"Authentication is required to set the statically configured local host name, "
-"as well as the pretty host name."
+"Authentication is required to set the statically configured local hostname, "
+"as well as the pretty hostname."
 msgstr ""
 "Απαιτείται πιστοποίηση για να ορίσετε το στατικά ρυθμισμένο όνομα τοπικού "
 "οικοδεσπότη, καθώς και το pretty όνομα οικοδεσπότη."
index aa586a729d9998b2a5fa5106a4066b7a5bb61387..adfe827bc4f7b1e84d4be1ba5e0a72ecadaffebe 100644 (file)
--- a/po/es.po
+++ b/po/es.po
@@ -71,21 +71,21 @@ msgid "Authentication is required to reload the systemd state."
 msgstr "Se requiere autenticación para recargar el estado de systemd."
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:1
-msgid "Set host name"
+msgid "Set hostname"
 msgstr "Establecer el nombre del equipo"
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:2
-msgid "Authentication is required to set the local host name."
+msgid "Authentication is required to set the local hostname."
 msgstr "Se requiere autenticación para establecer el nombre del equipo local."
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:3
-msgid "Set static host name"
+msgid "Set static hostname"
 msgstr "Establecer nombre estático del equipo"
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:4
 msgid ""
-"Authentication is required to set the statically configured local host name, "
-"as well as the pretty host name."
+"Authentication is required to set the statically configured local hostname, "
+"as well as the pretty hostname."
 msgstr ""
 "Se requiere autenticación para establecer el nombre estático de equipo "
 "local, así como el nombre visible del equipo."
index 0c5e0530184a0e7e8fa2e58f8eecd93494e326db..0999cf9dce766cbc87f14c6813816cedd26962e9 100644 (file)
--- a/po/fr.po
+++ b/po/fr.po
@@ -125,21 +125,21 @@ msgstr ""
 "d'un utilisateur."
 
 #: src/hostname/org.freedesktop.hostname1.policy:20
-msgid "Set host name"
+msgid "Set hostname"
 msgstr "Définir le nom d'hôte"
 
 #: src/hostname/org.freedesktop.hostname1.policy:21
-msgid "Authentication is required to set the local host name."
+msgid "Authentication is required to set the local hostname."
 msgstr "Authentification requise pour définir le nom d'hôte local."
 
 #: src/hostname/org.freedesktop.hostname1.policy:30
-msgid "Set static host name"
+msgid "Set static hostname"
 msgstr "Définir le nom d'hôte statique"
 
 #: src/hostname/org.freedesktop.hostname1.policy:31
 msgid ""
-"Authentication is required to set the statically configured local host name, "
-"as well as the pretty host name."
+"Authentication is required to set the statically configured local hostname, "
+"as well as the pretty hostname."
 msgstr ""
 "Authentification requise pour définir le nom d'hôte local de manière "
 "statique, tout comme le nom d'hôte familier."
index 7796154aa7a495ffa50eed58213a21a25474b138..147d34da845543f1fad933f7a4f7e83652304c9a 100644 (file)
--- a/po/gl.po
+++ b/po/gl.po
@@ -70,21 +70,21 @@ msgid "Authentication is required to reload the systemd state."
 msgstr "Requírese autenticación para recargar o estado de systemd."
 
 #: src/hostname/org.freedesktop.hostname1.policy:20
-msgid "Set host name"
+msgid "Set hostname"
 msgstr "Estabelecer o nome do equipo"
 
 #: src/hostname/org.freedesktop.hostname1.policy:21
-msgid "Authentication is required to set the local host name."
+msgid "Authentication is required to set the local hostname."
 msgstr "Requírese autenticación para estabelecer o nome local do equiupo."
 
 #: src/hostname/org.freedesktop.hostname1.policy:30
-msgid "Set static host name"
+msgid "Set static hostname"
 msgstr "Estabelecer o nome do equipo estático"
 
 #: src/hostname/org.freedesktop.hostname1.policy:31
 msgid ""
-"Authentication is required to set the statically configured local host name, "
-"as well as the pretty host name."
+"Authentication is required to set the statically configured local hostname, "
+"as well as the pretty hostname."
 msgstr ""
 "Requírese autenticación para estabelecer de forma o nome do equipo local "
 "estabelecido de forma estática, así como o nome do equipo lexíbel por "
index d77fee7df939a63c7b14ba1d0c5c1c90416bffc0..8385d5ae94b6aaa8e28839f4df52346fadf33c2a 100644 (file)
--- a/po/hr.po
+++ b/po/hr.po
@@ -118,21 +118,21 @@ msgid ""
 msgstr "Potrebna je ovjera za promjenu lozinke osobnog prostora korisnika."
 
 #: src/hostname/org.freedesktop.hostname1.policy:20
-msgid "Set host name"
+msgid "Set hostname"
 msgstr "Postavi naziv računala"
 
 #: src/hostname/org.freedesktop.hostname1.policy:21
-msgid "Authentication is required to set the local host name."
+msgid "Authentication is required to set the local hostname."
 msgstr "Potrebna je ovjera za postavljanje naziva lokalnog računala."
 
 #: src/hostname/org.freedesktop.hostname1.policy:30
-msgid "Set static host name"
+msgid "Set static hostname"
 msgstr "Postavi nepromjenjivi naziv račumala"
 
 #: src/hostname/org.freedesktop.hostname1.policy:31
 msgid ""
-"Authentication is required to set the statically configured local host name, "
-"as well as the pretty host name."
+"Authentication is required to set the statically configured local hostname, "
+"as well as the pretty hostname."
 msgstr ""
 "Potrebna je ovjera za postavljenje nepromjenjivog naziva lokalnog računala, "
 "kao i prijatnog naziva računala."
index c9c8bc786faf6f3fa6eaeb34c36df14d3fa807c4..e4e5d540b72f8b8ee66f54d6384196c236593fcc 100644 (file)
--- a/po/hu.po
+++ b/po/hu.po
@@ -71,21 +71,21 @@ msgid "Authentication is required to reload the systemd state."
 msgstr "Hitelesítés szükséges a systemd állapotának újratöltéséhez."
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:1
-msgid "Set host name"
+msgid "Set hostname"
 msgstr "Gépnév beállítása"
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:2
-msgid "Authentication is required to set the local host name."
+msgid "Authentication is required to set the local hostname."
 msgstr "Hitelesítés szükséges a helyi gépnév beállításához."
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:3
-msgid "Set static host name"
+msgid "Set static hostname"
 msgstr "Statikus gépnév beállítása"
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:4
 msgid ""
-"Authentication is required to set the statically configured local host name, "
-"as well as the pretty host name."
+"Authentication is required to set the statically configured local hostname, "
+"as well as the pretty hostname."
 msgstr ""
 "Hitelesítés szükséges a statikusan megadott helyi gépnév, valamint a szép "
 "gépnév beállításához."
index 66dd11ec77699d32cbfd7939a11f20d112e2e1e3..efc2ad700edd3b024ea583a3fcbf2cc9a90002c7 100644 (file)
--- a/po/id.po
+++ b/po/id.po
@@ -66,21 +66,21 @@ msgid "Authentication is required to reload the systemd state."
 msgstr "Otentikasi diperlukan untuk memuat ulang keadaan systemd."
 
 #: src/hostname/org.freedesktop.hostname1.policy:22
-msgid "Set host name"
+msgid "Set hostname"
 msgstr "Setel nama host"
 
 #: src/hostname/org.freedesktop.hostname1.policy:23
-msgid "Authentication is required to set the local host name."
+msgid "Authentication is required to set the local hostname."
 msgstr "Otentikasi diperlukan untuk menata nama host lokal."
 
 #: src/hostname/org.freedesktop.hostname1.policy:32
-msgid "Set static host name"
+msgid "Set static hostname"
 msgstr "Setel nama host statik"
 
 #: src/hostname/org.freedesktop.hostname1.policy:33
 msgid ""
-"Authentication is required to set the statically configured local host name, "
-"as well as the pretty host name."
+"Authentication is required to set the statically configured local hostname, "
+"as well as the pretty hostname."
 msgstr ""
 "Otentikasi diperlukan untuk menata nama host lokal yang dikonfigurasi "
 "statik, maupun nama host cantik."
index 1410586999b2c95c567563db3873337d9ceea924..a2f45bd1d981ddf813c0da1b0db88639d16cabe6 100644 (file)
--- a/po/it.po
+++ b/po/it.po
@@ -124,21 +124,21 @@ msgstr ""
 "dell'utente."
 
 #: src/hostname/org.freedesktop.hostname1.policy:20
-msgid "Set host name"
+msgid "Set hostname"
 msgstr "Configura il nome host"
 
 #: src/hostname/org.freedesktop.hostname1.policy:21
-msgid "Authentication is required to set the local host name."
+msgid "Authentication is required to set the local hostname."
 msgstr "Autenticazione richiesta per configurare il nome host locale."
 
 #: src/hostname/org.freedesktop.hostname1.policy:30
-msgid "Set static host name"
+msgid "Set static hostname"
 msgstr "Configura il nome host statico"
 
 #: src/hostname/org.freedesktop.hostname1.policy:31
 msgid ""
-"Authentication is required to set the statically configured local host name, "
-"as well as the pretty host name."
+"Authentication is required to set the statically configured local hostname, "
+"as well as the pretty hostname."
 msgstr ""
 "Autenticazione richiesta per configurare staticamente il nome host locale e "
 "il nome host descrittivo."
index 0847af1f71080f9a6e2dd8fbb8029e4d5ca05685..7662bec2e994e2be3df25ccc186a773bbcfb9afb 100644 (file)
--- a/po/ja.po
+++ b/po/ja.po
@@ -110,21 +110,21 @@ msgid "Authentication is required to change the password of a user's home area."
 msgstr "ユーザのホーム領域のパスワードを変更するには認証が必要です。"
 
 #: src/hostname/org.freedesktop.hostname1.policy:20
-msgid "Set host name"
+msgid "Set hostname"
 msgstr "ホスト名の設定"
 
 #: src/hostname/org.freedesktop.hostname1.policy:21
-msgid "Authentication is required to set the local host name."
+msgid "Authentication is required to set the local hostname."
 msgstr "ホスト名を設定するには認証が必要です。"
 
 #: src/hostname/org.freedesktop.hostname1.policy:30
-msgid "Set static host name"
+msgid "Set static hostname"
 msgstr "静的なホスト名の設定"
 
 #: src/hostname/org.freedesktop.hostname1.policy:31
 msgid ""
-"Authentication is required to set the statically configured local host name, "
-"as well as the pretty host name."
+"Authentication is required to set the statically configured local hostname, "
+"as well as the pretty hostname."
 msgstr "静的なホスト名を設定するには認証が必要です。"
 
 #: src/hostname/org.freedesktop.hostname1.policy:41
index 53899fd9efc9ed921405c31c3feb0a5935f2c035..301a322406f548312e120e1324aad7bb22f7e92e 100644 (file)
--- a/po/ko.po
+++ b/po/ko.po
@@ -66,21 +66,21 @@ msgid "Authentication is required to reload the systemd state."
 msgstr "systemd 상태를 다시 불러오려면 인증이 필요합니다."
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:1
-msgid "Set host name"
+msgid "Set hostname"
 msgstr "호스트 이름 설정"
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:2
-msgid "Authentication is required to set the local host name."
+msgid "Authentication is required to set the local hostname."
 msgstr "로컬 호스트 이름을 설정하려면 인증이 필요합니다."
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:3
-msgid "Set static host name"
+msgid "Set static hostname"
 msgstr "정적 호스트 이름 설정"
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:4
 msgid ""
-"Authentication is required to set the statically configured local host name, "
-"as well as the pretty host name."
+"Authentication is required to set the statically configured local hostname, "
+"as well as the pretty hostname."
 msgstr ""
 "로컬 호스트 이름을 모양새를 갖춘 호스트 이름 처럼  정적으로 설정하려면 인증"
 "이 필요합니다."
index 3350b56ee169026f9f149758ab0bca49d2d49344..2f03fd65eca205d103f628abfe920b2e7e53c5e8 100644 (file)
--- a/po/lt.po
+++ b/po/lt.po
@@ -68,22 +68,22 @@ msgid "Authentication is required to reload the systemd state."
 msgstr "Norint iš naujo įkelti systemd būseną, reikia patvirtinti tapatybę."
 
 #: src/hostname/org.freedesktop.hostname1.policy:20
-msgid "Set host name"
+msgid "Set hostname"
 msgstr "Nustatyti serverio pavadinimą"
 
 #: src/hostname/org.freedesktop.hostname1.policy:21
-msgid "Authentication is required to set the local host name."
+msgid "Authentication is required to set the local hostname."
 msgstr ""
 "Norint nustatyti vietinio serverio pavadinimą, reikia nustatyti tapatybę."
 
 #: src/hostname/org.freedesktop.hostname1.policy:30
-msgid "Set static host name"
+msgid "Set static hostname"
 msgstr "Nustatyti statinį serverio pavadinimą"
 
 #: src/hostname/org.freedesktop.hostname1.policy:31
 msgid ""
-"Authentication is required to set the statically configured local host name, "
-"as well as the pretty host name."
+"Authentication is required to set the statically configured local hostname, "
+"as well as the pretty hostname."
 msgstr ""
 "Norint nustatyti statiškai sukonfigūruotą serverio pavadinimą, o taip pat "
 "lengvai įsimenamą serverio pavadinimą, reikia nustatyti tapatybę."
index 4e865742139232acbf0348efb40d8e18e75e1df2..e942255985d70414621dae8decf133090414171e 100644 (file)
--- a/po/pl.po
+++ b/po/pl.po
@@ -6,8 +6,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: systemd\n"
 "Report-Msgid-Bugs-To: https://github.com/systemd/systemd/issues\n"
-"POT-Creation-Date: 2020-02-29 15:12+0000\n"
-"PO-Revision-Date: 2020-03-01 14:45+0100\n"
+"POT-Creation-Date: 2020-05-01 15:36+0000\n"
+"PO-Revision-Date: 2020-05-03 13:50+0200\n"
 "Last-Translator: Piotr Drąg <piotrdrag@gmail.com>\n"
 "Language-Team: Polish <trans-pl@lists.fedoraproject.org>\n"
 "Language: pl\n"
@@ -130,21 +130,21 @@ msgstr ""
 "użytkownika."
 
 #: src/hostname/org.freedesktop.hostname1.policy:20
-msgid "Set host name"
+msgid "Set hostname"
 msgstr "Ustawienie nazwy komputera"
 
 #: src/hostname/org.freedesktop.hostname1.policy:21
-msgid "Authentication is required to set the local host name."
+msgid "Authentication is required to set the local hostname."
 msgstr "Wymagane jest uwierzytelnienie, aby ustawić nazwę lokalnego komputera."
 
 #: src/hostname/org.freedesktop.hostname1.policy:30
-msgid "Set static host name"
+msgid "Set static hostname"
 msgstr "Ustawienie statycznej nazwy komputera"
 
 #: src/hostname/org.freedesktop.hostname1.policy:31
 msgid ""
-"Authentication is required to set the statically configured local host name, "
-"as well as the pretty host name."
+"Authentication is required to set the statically configured local hostname, "
+"as well as the pretty hostname."
 msgstr ""
 "Wymagane jest uwierzytelnienie, aby ustawić statycznie skonfigurowaną nazwę "
 "lokalnego komputera, a także jego nazwę czytelną dla człowieka."
@@ -776,26 +776,35 @@ msgid "Authentication is required to reset DNS settings."
 msgstr "Wymagane jest uwierzytelnienie, aby przywrócić ustawienia DNS."
 
 #: src/network/org.freedesktop.network1.policy:143
+msgid "DHCP server sends force renew message"
+msgstr "Serwer DHCP wysyła komunikat wymuszonego odnowienia"
+
+#: src/network/org.freedesktop.network1.policy:144
+msgid "Authentication is required to send force renew message."
+msgstr ""
+"Wymagane jest uwierzytelnienie, aby wysłać komunikat wymuszonego odnowienia."
+
+#: src/network/org.freedesktop.network1.policy:154
 msgid "Renew dynamic addresses"
 msgstr "Odnowienie adresów dynamicznych"
 
-#: src/network/org.freedesktop.network1.policy:144
+#: src/network/org.freedesktop.network1.policy:155
 msgid "Authentication is required to renew dynamic addresses."
 msgstr "Wymagane jest uwierzytelnienie, aby odnowić adresy dynamiczne."
 
-#: src/network/org.freedesktop.network1.policy:154
+#: src/network/org.freedesktop.network1.policy:165
 msgid "Reload network settings"
 msgstr "Ponowne wczytanie ustawień sieci"
 
-#: src/network/org.freedesktop.network1.policy:155
+#: src/network/org.freedesktop.network1.policy:166
 msgid "Authentication is required to reload network settings."
 msgstr "Wymagane jest uwierzytelnienie, aby ponownie wczytać ustawienia sieci."
 
-#: src/network/org.freedesktop.network1.policy:165
+#: src/network/org.freedesktop.network1.policy:176
 msgid "Reconfigure network interface"
 msgstr "Ponowna konfiguracja interfejsu sieciowego"
 
-#: src/network/org.freedesktop.network1.policy:166
+#: src/network/org.freedesktop.network1.policy:177
 msgid "Authentication is required to reconfigure network interface."
 msgstr ""
 "Wymagane jest uwierzytelnienie, aby ponownie skonfigurować interfejs "
@@ -896,25 +905,25 @@ msgstr ""
 "Wymagane jest uwierzytelnienie, aby kontrolować, czy włączyć synchronizację "
 "czasu przez sieć."
 
-#: src/core/dbus-unit.c:356
+#: src/core/dbus-unit.c:358
 msgid "Authentication is required to start '$(unit)'."
 msgstr "Wymagane jest uwierzytelnienie, aby uruchomić jednostkę „$(unit)”."
 
-#: src/core/dbus-unit.c:357
+#: src/core/dbus-unit.c:359
 msgid "Authentication is required to stop '$(unit)'."
 msgstr "Wymagane jest uwierzytelnienie, aby zatrzymać jednostkę „$(unit)”."
 
-#: src/core/dbus-unit.c:358
+#: src/core/dbus-unit.c:360
 msgid "Authentication is required to reload '$(unit)'."
 msgstr ""
 "Wymagane jest uwierzytelnienie, aby ponownie wczytać jednostkę „$(unit)”."
 
-#: src/core/dbus-unit.c:359 src/core/dbus-unit.c:360
+#: src/core/dbus-unit.c:361 src/core/dbus-unit.c:362
 msgid "Authentication is required to restart '$(unit)'."
 msgstr ""
 "Wymagane jest uwierzytelnienie, aby ponownie uruchomić jednostkę „$(unit)”."
 
-#: src/core/dbus-unit.c:532
+#: src/core/dbus-unit.c:534
 msgid ""
 "Authentication is required to send a UNIX signal to the processes of "
 "'$(unit)'."
@@ -922,18 +931,18 @@ msgstr ""
 "Wymagane jest uwierzytelnienie, aby wysłać sygnał uniksowy do procesów "
 "jednostki „$(unit)”."
 
-#: src/core/dbus-unit.c:563
+#: src/core/dbus-unit.c:565
 msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
 msgstr ""
 "Wymagane jest uwierzytelnienie, aby przywrócić stan „failed” (niepowodzenia) "
 "jednostki „$(unit)”."
 
-#: src/core/dbus-unit.c:596
+#: src/core/dbus-unit.c:598
 msgid "Authentication is required to set properties on '$(unit)'."
 msgstr ""
 "Wymagane jest uwierzytelnienie, aby ustawić właściwości jednostki „$(unit)”."
 
-#: src/core/dbus-unit.c:705
+#: src/core/dbus-unit.c:707
 msgid ""
 "Authentication is required to delete files and directories associated with "
 "'$(unit)'."
@@ -941,6 +950,13 @@ msgstr ""
 "Wymagane jest uwierzytelnienie, aby usunąć pliki i katalogi powiązane "
 "z jednostką „$(unit)”."
 
+#: src/core/dbus-unit.c:756
+msgid ""
+"Authentication is required to freeze or thaw the processes of '$(unit)' unit."
+msgstr ""
+"Wymagane jest uwierzytelnienie, aby zamrozić lub odmrozić procesy jednostki "
+"„$(unit)”."
+
 #~ msgid "Press Ctrl+C to cancel all filesystem checks in progress"
 #~ msgstr ""
 #~ "Naciśnięcie klawiszy Ctrl+C anuluje wszystkie trwające procesy "
index 06706fe8fca447cfa07d5c12afbd9a8f81b858f9..ef61e47ca5913bbb524147fda80e5f5d63a46977 100644 (file)
@@ -3,14 +3,14 @@
 # Brazilian Portuguese translation for systemd.
 # Enrico Nicoletto <liverig@gmail.com>, 2014.
 # Filipe Brandenburger <filbranden@gmail.com>, 2018.
-# Rafael Fontenelle <rafaelff@gnome.org>, 2015-2019.
+# Rafael Fontenelle <rafaelff@gnome.org>, 2015-2020.
 #
 msgid ""
 msgstr ""
 "Project-Id-Version: systemd\n"
 "Report-Msgid-Bugs-To: https://github.com/systemd/systemd/issues\n"
-"POT-Creation-Date: 2019-09-08 15:28+0000\n"
-"PO-Revision-Date: 2019-09-08 19:00-0300\n"
+"POT-Creation-Date: 2020-05-29 03:32+0000\n"
+"PO-Revision-Date: 2020-05-30 09:10-0300\n"
 "Last-Translator: Rafael Fontenelle <rafaelff@gnome.org>\n"
 "Language-Team: Brazilian Portuguese <gnome-pt_br-list@gnome.org>\n"
 "Language: pt_BR\n"
@@ -18,7 +18,7 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n > 1)\n"
-"X-Generator: Gtranslator 3.32.0\n"
+"X-Generator: Gtranslator 3.36.0\n"
 
 #: src/core/org.freedesktop.systemd1.policy.in:22
 msgid "Send passphrase back to system"
@@ -73,22 +73,76 @@ msgstr "Recarregar o estado do sistema"
 msgid "Authentication is required to reload the systemd state."
 msgstr "É necessária autenticação para recarregar o estado do sistema."
 
+#: src/home/org.freedesktop.home1.policy:13
+msgid "Create a home area"
+msgstr "Criar uma área home"
+
+#: src/home/org.freedesktop.home1.policy:14
+msgid "Authentication is required to create a user's home area."
+msgstr "É necessária autenticação para criar a área home de um usuário."
+
+#: src/home/org.freedesktop.home1.policy:23
+msgid "Remove a home area"
+msgstr "Remover uma área home"
+
+#: src/home/org.freedesktop.home1.policy:24
+msgid "Authentication is required to remove a user's home area."
+msgstr "É necessária autenticação para remover a área home de um usuário."
+
+#: src/home/org.freedesktop.home1.policy:33
+msgid "Check credentials of a home area"
+msgstr "Verificar credenciais de uma área home"
+
+#: src/home/org.freedesktop.home1.policy:34
+msgid ""
+"Authentication is required to check credentials against a user's home area."
+msgstr ""
+"É necessária autenticação para verificar credenciais da área home de um "
+"usuário."
+
+#: src/home/org.freedesktop.home1.policy:43
+msgid "Update a home area"
+msgstr "Atualizar uma área home"
+
+#: src/home/org.freedesktop.home1.policy:44
+msgid "Authentication is required to update a user's home area."
+msgstr "É necessária autenticação para atualizar a área home de um usuário."
+
+#: src/home/org.freedesktop.home1.policy:53
+msgid "Resize a home area"
+msgstr "Redimensionar uma área home"
+
+#: src/home/org.freedesktop.home1.policy:54
+msgid "Authentication is required to resize a user's home area."
+msgstr ""
+"É necessária autenticação para redimensionar a área home de um usuário."
+
+#: src/home/org.freedesktop.home1.policy:63
+msgid "Change password of a home area"
+msgstr "Alterar senha de uma área home"
+
+#: src/home/org.freedesktop.home1.policy:64
+msgid ""
+"Authentication is required to change the password of a user's home area."
+msgstr ""
+"É necessária autenticação para alterar a senha da área home de um usuário."
+
 #: src/hostname/org.freedesktop.hostname1.policy:20
-msgid "Set host name"
+msgid "Set hostname"
 msgstr "Definir nome de máquina"
 
 #: src/hostname/org.freedesktop.hostname1.policy:21
-msgid "Authentication is required to set the local host name."
-msgstr "É necessária autenticação para definir nome de máquina local."
+msgid "Authentication is required to set the local hostname."
+msgstr "É necessária autenticação para definir nome de máquina local."
 
 #: src/hostname/org.freedesktop.hostname1.policy:30
-msgid "Set static host name"
+msgid "Set static hostname"
 msgstr "Definir nome estático de máquina"
 
 #: src/hostname/org.freedesktop.hostname1.policy:31
 msgid ""
-"Authentication is required to set the statically configured local host name, "
-"as well as the pretty host name."
+"Authentication is required to set the statically configured local hostname, "
+"as well as the pretty hostname."
 msgstr ""
 "É necessária autenticação para definir o nome de máquina local configurado "
 "estaticamente, assim como o nome apresentável de máquina."
@@ -293,8 +347,7 @@ msgid "Flush device to seat attachments"
 msgstr "Liberar dispositivo para conexões da estação"
 
 #: src/login/org.freedesktop.login1.policy:149
-msgid ""
-"Authentication is required to reset how devices are attached to seats."
+msgid "Authentication is required to reset how devices are attached to seats."
 msgstr ""
 "É necessária autenticação para redefinir a quantidade de dispositivos "
 "conectados na estação."
@@ -325,8 +378,8 @@ msgstr "Desligar o sistema enquanto um aplicativo solicitou inibição"
 
 #: src/login/org.freedesktop.login1.policy:181
 msgid ""
-"Authentication is required to power off the system while an application "
-"is inhibiting this."
+"Authentication is required to power off the system while an application is "
+"inhibiting this."
 msgstr ""
 "É necessária autenticação para desligar o sistema enquanto um aplicativo "
 "solicitou inibição."
@@ -345,8 +398,8 @@ msgstr "Reiniciar o sistema enquanto outros usuários estiverem conectados"
 
 #: src/login/org.freedesktop.login1.policy:203
 msgid ""
-"Authentication is required to reboot the system while other users are "
-"logged in."
+"Authentication is required to reboot the system while other users are logged "
+"in."
 msgstr ""
 "É necessária autenticação para reiniciar o sistema enquanto outros usuários "
 "estiverem conectados."
@@ -357,8 +410,8 @@ msgstr "Reiniciar o sistema enquanto um aplicativo solicitou inibição"
 
 #: src/login/org.freedesktop.login1.policy:214
 msgid ""
-"Authentication is required to reboot the system while an application "
-"is inhibiting this."
+"Authentication is required to reboot the system while an application is "
+"inhibiting this."
 msgstr ""
 "É necessária autenticação para reiniciar o sistema enquanto um aplicativo "
 "solicitou inibição."
@@ -377,8 +430,8 @@ msgstr "Parar o sistema enquanto outros usuários estão logados"
 
 #: src/login/org.freedesktop.login1.policy:236
 msgid ""
-"Authentication is required to halt the system while other users are "
-"logged in."
+"Authentication is required to halt the system while other users are logged "
+"in."
 msgstr ""
 "É necessária autenticação para parar o sistema enquanto outros usuários "
 "estejam logados."
@@ -389,8 +442,8 @@ msgstr "Parar o sistema enquanto um aplicativo solicitou inibição"
 
 #: src/login/org.freedesktop.login1.policy:247
 msgid ""
-"Authentication is required to halt the system while an application asked "
-"to inhibit it."
+"Authentication is required to halt the system while an application is "
+"inhibiting this."
 msgstr ""
 "É necessária autenticação para parar o sistema enquanto um aplicativo "
 "solicitou inibição."
@@ -421,8 +474,8 @@ msgstr "Suspender o sistema enquanto um aplicativo solicitou inibição"
 
 #: src/login/org.freedesktop.login1.policy:279
 msgid ""
-"Authentication is required to suspend the system while an application "
-"is inhibiting this."
+"Authentication is required to suspend the system while an application is "
+"inhibiting this."
 msgstr ""
 "É necessária autenticação para suspender o sistema enquanto um aplicativo "
 "solicitou inibição."
@@ -453,8 +506,8 @@ msgstr "Hibernar o sistema enquanto um aplicativo solicitou inibição"
 
 #: src/login/org.freedesktop.login1.policy:311
 msgid ""
-"Authentication is required to hibernate the system while an application "
-"is inhibiting this."
+"Authentication is required to hibernate the system while an application is "
+"inhibiting this."
 msgstr ""
 "É necessária autenticação para hibernar o sistema enquanto um aplicativo "
 "solicitou inibição."
@@ -464,8 +517,7 @@ msgid "Manage active sessions, users and seats"
 msgstr "Gerenciar estações, usuários e sessões ativas"
 
 #: src/login/org.freedesktop.login1.policy:322
-msgid ""
-"Authentication is required to manage active sessions, users and seats."
+msgid "Authentication is required to manage active sessions, users and seats."
 msgstr ""
 "É necessária autenticação para gerenciar estações, usuários e sessões ativas."
 
@@ -509,7 +561,7 @@ msgid ""
 "boot loader menu."
 msgstr ""
 "É necessária autenticação para indicar para o carregador de inicialização "
-"iniciar seu menu"
+"iniciar seu menu."
 
 #: src/login/org.freedesktop.login1.policy:374
 msgid "Indicate to the boot loader to boot a specific entry"
@@ -522,7 +574,7 @@ msgid ""
 "specific boot loader entry."
 msgstr ""
 "É necessária autenticação para indicar para o carregador de inicializar "
-"iniciar uma entrada específica"
+"iniciar uma entrada específica."
 
 #: src/login/org.freedesktop.login1.policy:385
 msgid "Set a wall message"
@@ -532,6 +584,14 @@ msgstr "Definir uma mensagem de parede"
 msgid "Authentication is required to set a wall message"
 msgstr "É necessária autenticação para definir uma mensagem de parede"
 
+#: src/login/org.freedesktop.login1.policy:395
+msgid "Change Session"
+msgstr "Alterar sessão"
+
+#: src/login/org.freedesktop.login1.policy:396
+msgid "Authentication is required to change the virtual terminal."
+msgstr "É necessária autenticação para alterar o terminal virtual."
+
 #: src/machine/org.freedesktop.machine1.policy:22
 msgid "Log into a local container"
 msgstr "Conectar a um contêiner local"
@@ -701,16 +761,48 @@ msgid "Revert NTP settings"
 msgstr "Reverter configurações de NTP"
 
 #: src/network/org.freedesktop.network1.policy:122
-msgid "Authentication is required to revert NTP settings."
-msgstr "É necessária autenticação para reverter as configurações de NTP."
+msgid "Authentication is required to reset NTP settings."
+msgstr "É necessária autenticação para redefinir as configurações de NTP."
 
 #: src/network/org.freedesktop.network1.policy:132
 msgid "Revert DNS settings"
 msgstr "Reverter configurações de DNS"
 
 #: src/network/org.freedesktop.network1.policy:133
-msgid "Authentication is required to revert DNS settings."
-msgstr "É necessária autenticação para reverter as configurações de DNS."
+msgid "Authentication is required to reset DNS settings."
+msgstr "É necessária autenticação para redefinir as configurações de DNS."
+
+#: src/network/org.freedesktop.network1.policy:143
+msgid "DHCP server sends force renew message"
+msgstr "Servidor DHCP envia mensagem de renovação forçada"
+
+#: src/network/org.freedesktop.network1.policy:144
+msgid "Authentication is required to send force renew message."
+msgstr "É necessária autenticação para enviar mensagem de renovação forçada."
+
+#: src/network/org.freedesktop.network1.policy:154
+msgid "Renew dynamic addresses"
+msgstr "Renovar endereços dinâmicos"
+
+#: src/network/org.freedesktop.network1.policy:155
+msgid "Authentication is required to renew dynamic addresses."
+msgstr "É necessária autenticação para renovar endereços dinâmicos."
+
+#: src/network/org.freedesktop.network1.policy:165
+msgid "Reload network settings"
+msgstr "Recarregar configurações de rede"
+
+#: src/network/org.freedesktop.network1.policy:166
+msgid "Authentication is required to reload network settings."
+msgstr "É necessária autenticação para recarregar as configurações de rede."
+
+#: src/network/org.freedesktop.network1.policy:176
+msgid "Reconfigure network interface"
+msgstr "Reconfigurar interface de rede"
+
+#: src/network/org.freedesktop.network1.policy:177
+msgid "Authentication is required to reconfigure network interface."
+msgstr "É necessária autenticação para reconfigurar a interface de rede."
 
 #: src/portable/org.freedesktop.portable1.policy:13
 msgid "Inspect a portable service image"
@@ -764,9 +856,9 @@ msgid "Revert name resolution settings"
 msgstr "Reverter configurações de resolução de nome"
 
 #: src/resolve/org.freedesktop.resolve1.policy:133
-msgid "Authentication is required to revert name resolution settings."
+msgid "Authentication is required to reset name resolution settings."
 msgstr ""
-"É necessária autenticação para reverter as configurações de resolução de "
+"É necessária autenticação para redefinir as configurações de resolução de "
 "nome."
 
 #: src/timedate/org.freedesktop.timedate1.policy:22
@@ -787,7 +879,7 @@ msgstr "É necessária autenticação para definir o fuso horário do sistema."
 
 #: src/timedate/org.freedesktop.timedate1.policy:43
 msgid "Set RTC to local timezone or UTC"
-msgstr "Definir o relógio do sistema (RTC) para fuso horário local ou UTC"
+msgstr "Definir RTC para fuso horário local ou UTC"
 
 #: src/timedate/org.freedesktop.timedate1.policy:44
 msgid ""
@@ -809,23 +901,23 @@ msgstr ""
 "É necessária autenticação para controlar se deve ser habilitada, ou não, a "
 "sincronização de horário através de rede."
 
-#: src/core/dbus-unit.c:354
+#: src/core/dbus-unit.c:358
 msgid "Authentication is required to start '$(unit)'."
 msgstr "É necessária autenticação para iniciar “$(unit)”."
 
-#: src/core/dbus-unit.c:355
+#: src/core/dbus-unit.c:359
 msgid "Authentication is required to stop '$(unit)'."
 msgstr "É necessária autenticação para parar “$(unit)”."
 
-#: src/core/dbus-unit.c:356
+#: src/core/dbus-unit.c:360
 msgid "Authentication is required to reload '$(unit)'."
 msgstr "É necessária autenticação para recarregar “$(unit)”."
 
-#: src/core/dbus-unit.c:357 src/core/dbus-unit.c:358
+#: src/core/dbus-unit.c:361 src/core/dbus-unit.c:362
 msgid "Authentication is required to restart '$(unit)'."
 msgstr "É necessária autenticação para reiniciar “$(unit)”."
 
-#: src/core/dbus-unit.c:530
+#: src/core/dbus-unit.c:534
 msgid ""
 "Authentication is required to send a UNIX signal to the processes of "
 "'$(unit)'."
@@ -833,16 +925,16 @@ msgstr ""
 "É necessária autenticação para enviar um sinal UNIX para os processos de "
 "“$(unit)”."
 
-#: src/core/dbus-unit.c:561
+#: src/core/dbus-unit.c:565
 msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
 msgstr ""
 "É necessária autenticação para reiniciar o estado “failed” de “$(unit)”."
 
-#: src/core/dbus-unit.c:594
+#: src/core/dbus-unit.c:598
 msgid "Authentication is required to set properties on '$(unit)'."
 msgstr "É necessária autenticação para definir propriedades em “$(unit)”."
 
-#: src/core/dbus-unit.c:703
+#: src/core/dbus-unit.c:707
 msgid ""
 "Authentication is required to delete files and directories associated with "
 "'$(unit)'."
@@ -850,5 +942,9 @@ msgstr ""
 "É necessária autenticação para excluir arquivos e diretórios associados com "
 "“$(unit)”."
 
-#~ msgid "Authentication is required to kill '$(unit)'."
-#~ msgstr "É necessária autenticação para matar “$(unit)”."
+#: src/core/dbus-unit.c:756
+msgid ""
+"Authentication is required to freeze or thaw the processes of '$(unit)' unit."
+msgstr ""
+"É necessária autenticação para congelar ou descongelar os processos da "
+"unidade “$(unit)”."
index e22f1a110a68c222af0f7f92234baa3503af005d..869cf6b625ff0afe4064dd84612a68dc81e9d6b9 100644 (file)
--- a/po/ro.po
+++ b/po/ro.po
@@ -73,21 +73,21 @@ msgid "Authentication is required to reload the systemd state."
 msgstr "Autentificarea este necesară pentru a reîncărca starea systemd."
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:1
-msgid "Set host name"
+msgid "Set hostname"
 msgstr "Stabilește numele de server"
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:2
-msgid "Authentication is required to set the local host name."
+msgid "Authentication is required to set the local hostname."
 msgstr "Autentificarea este necesară pentru a stabili numele de server local."
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:3
-msgid "Set static host name"
+msgid "Set static hostname"
 msgstr "Stabilește numele de server static"
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:4
 msgid ""
-"Authentication is required to set the statically configured local host name, "
-"as well as the pretty host name."
+"Authentication is required to set the statically configured local hostname, "
+"as well as the pretty hostname."
 msgstr ""
 "Autentificarea este necesara pentru a stabili numele de server static "
 "configurat local, precum și numele lung de server."
index 35da66eefb90fe4c6ecdcdda5fc249a42f1052f3..f13c51f302d4d0d9650b387eeae5496efd14d20e 100644 (file)
--- a/po/ru.po
+++ b/po/ru.po
@@ -134,21 +134,21 @@ msgstr ""
 " пройти аутентификацию."
 
 #: src/hostname/org.freedesktop.hostname1.policy:20
-msgid "Set host name"
+msgid "Set hostname"
 msgstr "Настроить имя компьютера"
 
 #: src/hostname/org.freedesktop.hostname1.policy:21
-msgid "Authentication is required to set the local host name."
+msgid "Authentication is required to set the local hostname."
 msgstr "Чтобы настроить имя компьютера, необходимо пройти аутентификацию."
 
 #: src/hostname/org.freedesktop.hostname1.policy:30
-msgid "Set static host name"
+msgid "Set static hostname"
 msgstr "Настроить статическое имя компьютера"
 
 #: src/hostname/org.freedesktop.hostname1.policy:31
 msgid ""
-"Authentication is required to set the statically configured local host name, "
-"as well as the pretty host name."
+"Authentication is required to set the statically configured local hostname, "
+"as well as the pretty hostname."
 msgstr ""
 "Чтобы настроить статическое имя компьютера, а также его «красивое» имя, "
 "необходимо пройти аутентификацию."
index ced96e8045ab326e8359ebce7012bc49a1f25208..971df90b28c089922b27fd0fa88d14b91c3375c1 100644 (file)
--- a/po/sk.po
+++ b/po/sk.po
@@ -72,21 +72,21 @@ msgstr ""
 "Vyžaduje sa overenie totožnosti na znovu načítanie stavu systému systemd."
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:1
-msgid "Set host name"
+msgid "Set hostname"
 msgstr "Nastavenie názvu hostiteľa"
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:2
-msgid "Authentication is required to set the local host name."
+msgid "Authentication is required to set the local hostname."
 msgstr "Vyžaduje sa overenie totožnosti na nastavenie názvu hostiteľa."
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:3
-msgid "Set static host name"
+msgid "Set static hostname"
 msgstr "Nastavenie nemenného názvu hostiteľa"
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:4
 msgid ""
-"Authentication is required to set the statically configured local host name, "
-"as well as the pretty host name."
+"Authentication is required to set the statically configured local hostname, "
+"as well as the pretty hostname."
 msgstr ""
 "Vyžaduje sa overenie totožnosti na nastavenie pevne určeného názvu miestneho "
 "hostiteľa, známeho ako zrozumiteľný názov hostiteľa."
index 3d0bebd503619a3fff9d43156b0d9441f71510ef..a62f5cffe60f7b20e1d3a63831ed78eaf821f57b 100644 (file)
--- a/po/sr.po
+++ b/po/sr.po
@@ -72,21 +72,21 @@ msgstr ""
 "Потребно је да се идентификујете да бисте поново учитали стање систем-деа."
 
 #: src/hostname/org.freedesktop.hostname1.policy.in:22
-msgid "Set host name"
+msgid "Set hostname"
 msgstr "Постави назив машине"
 
 #: src/hostname/org.freedesktop.hostname1.policy.in:23
-msgid "Authentication is required to set the local host name."
+msgid "Authentication is required to set the local hostname."
 msgstr "Потребно је да се идентификујете да бисте поставили назив машине."
 
 #: src/hostname/org.freedesktop.hostname1.policy.in:32
-msgid "Set static host name"
+msgid "Set static hostname"
 msgstr "Постави статички назив машине"
 
 #: src/hostname/org.freedesktop.hostname1.policy.in:33
 msgid ""
-"Authentication is required to set the statically configured local host name, "
-"as well as the pretty host name."
+"Authentication is required to set the statically configured local hostname, "
+"as well as the pretty hostname."
 msgstr ""
 "Потребно је да се идентификујете да бисте поставили статички назив машине и "
 "да бисте поставили леп назив машине."
index 5f40d455ab3c20deb798018820b0942754025d26..c8bd2ae2742980d3a82a70284f569c40ebde3a49 100644 (file)
--- a/po/sv.po
+++ b/po/sv.po
@@ -68,21 +68,21 @@ msgid "Authentication is required to reload the systemd state."
 msgstr "Autentisering krävs för att läsa om tillståndet för systemd."
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:1
-msgid "Set host name"
+msgid "Set hostname"
 msgstr "Ange värdnamn"
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:2
-msgid "Authentication is required to set the local host name."
+msgid "Authentication is required to set the local hostname."
 msgstr "Autentisering krävs för att ställa in lokalt värdnamn."
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:3
-msgid "Set static host name"
+msgid "Set static hostname"
 msgstr "Ange statiskt värdnamn"
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:4
 msgid ""
-"Authentication is required to set the statically configured local host name, "
-"as well as the pretty host name."
+"Authentication is required to set the statically configured local hostname, "
+"as well as the pretty hostname."
 msgstr ""
 "Autentisering krävs för att ställa in det statiskt konfigurerade lokala "
 "värdnamnet såväl som det stiliga värdnamnet."
index 86c13448e23290009568f82d59a8ffdd2b867f40..b2af9e900450418f12563f87a29e782dd6e2d68a 100644 (file)
--- a/po/tr.po
+++ b/po/tr.po
@@ -70,21 +70,21 @@ msgid "Authentication is required to reload the systemd state."
 msgstr "systemd durumunu yeniden yüklemek kimlik doğrulaması gerektiriyor."
 
 #: src/hostname/org.freedesktop.hostname1.policy:20
-msgid "Set host name"
+msgid "Set hostname"
 msgstr "Makine adını ayarla"
 
 #: src/hostname/org.freedesktop.hostname1.policy:21
-msgid "Authentication is required to set the local host name."
+msgid "Authentication is required to set the local hostname."
 msgstr "Yerel makine adını ayarlamak kimlik doğrulaması gerektiriyor."
 
 #: src/hostname/org.freedesktop.hostname1.policy:30
-msgid "Set static host name"
+msgid "Set static hostname"
 msgstr "Statik makine adı ayarla"
 
 #: src/hostname/org.freedesktop.hostname1.policy:31
 msgid ""
-"Authentication is required to set the statically configured local host name, "
-"as well as the pretty host name."
+"Authentication is required to set the statically configured local hostname, "
+"as well as the pretty hostname."
 msgstr ""
 "Statik olarak yapılandırılmış konak makine adını ve yerel makine adını "
 "ayarlamak kimlik doğrulaması gerektiriyor."
index c2902797dbbbb8aae054e9c77b3424022946e699..f0a702802f9c4b4d8b6718932235a19d7cb76d44 100644 (file)
--- a/po/uk.po
+++ b/po/uk.po
@@ -7,8 +7,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: systemd master\n"
 "Report-Msgid-Bugs-To: https://github.com/systemd/systemd/issues\n"
-"POT-Creation-Date: 2020-01-30 15:31+0000\n"
-"PO-Revision-Date: 2020-02-07 12:37+0200\n"
+"POT-Creation-Date: 2020-03-22 04:04+0000\n"
+"PO-Revision-Date: 2020-03-25 18:40+0200\n"
 "Last-Translator: Yuri Chornoivan <yurchor@ukr.net>\n"
 "Language-Team: Ukrainian <kde-i18n-uk@kde.org>\n"
 "Language: uk\n"
@@ -94,8 +94,8 @@ msgstr "Перевірка реєстраційних даних для дост
 msgid ""
 "Authentication is required to check credentials against a user's home area."
 msgstr ""
-"Для перевірки реєстраційних даних для доступу до домашньої теки користувача"
-" слід пройти розпізнавання."
+"Для перевірки реєстраційних даних для доступу до домашньої теки користувача "
+"слід пройти розпізнавання."
 
 #: src/home/org.freedesktop.home1.policy:43
 msgid "Update a home area"
@@ -119,27 +119,28 @@ msgid "Change password of a home area"
 msgstr "Зміна пароля до домашньої теки"
 
 #: src/home/org.freedesktop.home1.policy:64
-msgid "Authentication is required to change the password of a user's home area."
+msgid ""
+"Authentication is required to change the password of a user's home area."
 msgstr ""
-"Для зміни пароля для доступу до домашньої теки користувача слід пройти"
-" розпізнавання."
+"Для зміни пароля для доступу до домашньої теки користувача слід пройти "
+"розпізнавання."
 
 #: src/hostname/org.freedesktop.hostname1.policy:20
-msgid "Set host name"
+msgid "Set hostname"
 msgstr "Встановити назву вузла"
 
 #: src/hostname/org.freedesktop.hostname1.policy:21
-msgid "Authentication is required to set the local host name."
+msgid "Authentication is required to set the local hostname."
 msgstr "Потрібна автентифікація, щоб встановити назву локального вузла."
 
 #: src/hostname/org.freedesktop.hostname1.policy:30
-msgid "Set static host name"
+msgid "Set static hostname"
 msgstr "Встановити статичну назву вузла"
 
 #: src/hostname/org.freedesktop.hostname1.policy:31
 msgid ""
-"Authentication is required to set the statically configured local host name, "
-"as well as the pretty host name."
+"Authentication is required to set the statically configured local hostname, "
+"as well as the pretty hostname."
 msgstr ""
 "Потрібна автентифікація, щоб вказати статично налаштовану назву локального "
 "вузла, так само й форматовану."
@@ -345,8 +346,7 @@ msgid "Flush device to seat attachments"
 msgstr "Очисний пристрій для під'єднань до місця"
 
 #: src/login/org.freedesktop.login1.policy:149
-msgid ""
-"Authentication is required to reset how devices are attached to seats."
+msgid "Authentication is required to reset how devices are attached to seats."
 msgstr ""
 "Потрібна автентифікація, щоб перезапустити спосіб під'єднання до місць."
 
@@ -375,8 +375,8 @@ msgstr "Вимкнути систему, коли програми намага
 
 #: src/login/org.freedesktop.login1.policy:181
 msgid ""
-"Authentication is required to power off the system while an application "
-"is inhibiting this."
+"Authentication is required to power off the system while an application is "
+"inhibiting this."
 msgstr ""
 "Потрібна автентифікація, щоб вимкнути систему, коли програми намагаються "
 "перешкодити цьому."
@@ -395,8 +395,8 @@ msgstr "Перезавантажити, якщо інші користувачі
 
 #: src/login/org.freedesktop.login1.policy:203
 msgid ""
-"Authentication is required to reboot the system while other users are "
-"logged in."
+"Authentication is required to reboot the system while other users are logged "
+"in."
 msgstr ""
 "Потрібна автентифікація, щоб перезапустити систему, коли інші користувачі в "
 "ній."
@@ -407,8 +407,8 @@ msgstr "Перезапустити систему, коли програми н
 
 #: src/login/org.freedesktop.login1.policy:214
 msgid ""
-"Authentication is required to reboot the system while an application "
-"is inhibiting this."
+"Authentication is required to reboot the system while an application is "
+"inhibiting this."
 msgstr ""
 "Потрібна автентифікація, щоб перезапустити систему, коли програми "
 "намагаються перешкодити цьому."
@@ -427,8 +427,8 @@ msgstr "Зупинити систему, коли інші користувач
 
 #: src/login/org.freedesktop.login1.policy:236
 msgid ""
-"Authentication is required to halt the system while other users are "
-"logged in."
+"Authentication is required to halt the system while other users are logged "
+"in."
 msgstr ""
 "Потрібна автентифікація, щоб зупинити систему, коли інші користувачі в ній."
 
@@ -438,10 +438,11 @@ msgstr "Зупинити систему, коли програми намага
 
 #: src/login/org.freedesktop.login1.policy:247
 msgid ""
-"Authentication is required to halt the system while an application asked "
-"to inhibit it."
+"Authentication is required to halt the system while an application is "
+"inhibiting this."
 msgstr ""
-"Потрібна автентифікація, щоб зупинити систему, коли програми намагаються "
+"Потрібна автентифікація, щоб зупинити систему, коли програма"
+" намагається "
 "перешкодити цьому."
 
 #: src/login/org.freedesktop.login1.policy:257
@@ -470,8 +471,8 @@ msgstr "Призупинити систему, коли програми нам
 
 #: src/login/org.freedesktop.login1.policy:279
 msgid ""
-"Authentication is required to suspend the system while an application "
-"is inhibiting this."
+"Authentication is required to suspend the system while an application is "
+"inhibiting this."
 msgstr ""
 "Потрібна автентифікація, щоб призупинити систему, коли програми намагаються "
 "перешкодити цьому."
@@ -501,8 +502,8 @@ msgstr "Приспати систему, коли програми намага
 
 #: src/login/org.freedesktop.login1.policy:311
 msgid ""
-"Authentication is required to hibernate the system while an application "
-"is inhibiting this."
+"Authentication is required to hibernate the system while an application is "
+"inhibiting this."
 msgstr ""
 "Потрібна автентифікація, щоб приспати систему, коли програми намагаються "
 "перешкодити цьому."
@@ -512,8 +513,7 @@ msgid "Manage active sessions, users and seats"
 msgstr "Керувати сеансами, користувачами і робочими місцями"
 
 #: src/login/org.freedesktop.login1.policy:322
-msgid ""
-"Authentication is required to manage active sessions, users and seats."
+msgid "Authentication is required to manage active sessions, users and seats."
 msgstr ""
 "Потрібна автентифікація, щоб керувати сеансами, користувачами і робочими "
 "місцями."
@@ -767,26 +767,35 @@ msgid "Authentication is required to reset DNS settings."
 msgstr "Для скидання параметрів DNS до типових слід пройти розпізнавання."
 
 #: src/network/org.freedesktop.network1.policy:143
+msgid "DHCP server sends force renew message"
+msgstr "Сервер DHCP надсилає повідомлення щодо примусового оновлення"
+
+#: src/network/org.freedesktop.network1.policy:144
+msgid "Authentication is required to send force renew message."
+msgstr ""
+"Потрібна автентифікація, щоб надіслати повідомлення щодо примусового оновлення"
+
+#: src/network/org.freedesktop.network1.policy:154
 msgid "Renew dynamic addresses"
 msgstr "Оновлення динамічних адрес"
 
-#: src/network/org.freedesktop.network1.policy:144
+#: src/network/org.freedesktop.network1.policy:155
 msgid "Authentication is required to renew dynamic addresses."
 msgstr "Для оновлення динамічних адрес слід пройти розпізнавання."
 
-#: src/network/org.freedesktop.network1.policy:154
+#: src/network/org.freedesktop.network1.policy:165
 msgid "Reload network settings"
 msgstr "Перезавантаження параметрів мережі"
 
-#: src/network/org.freedesktop.network1.policy:155
+#: src/network/org.freedesktop.network1.policy:166
 msgid "Authentication is required to reload network settings."
 msgstr "Для перезавантаження параметрів мережі слід пройти розпізнавання."
 
-#: src/network/org.freedesktop.network1.policy:165
+#: src/network/org.freedesktop.network1.policy:176
 msgid "Reconfigure network interface"
 msgstr "Переналаштування інтерфейсу мережі"
 
-#: src/network/org.freedesktop.network1.policy:166
+#: src/network/org.freedesktop.network1.policy:177
 msgid "Authentication is required to reconfigure network interface."
 msgstr "Для зміни налаштувань інтерфейсу мережі слід пройти розпізнавання."
 
@@ -842,8 +851,8 @@ msgstr "Повернення до початкових параметрів ви
 #: src/resolve/org.freedesktop.resolve1.policy:133
 msgid "Authentication is required to reset name resolution settings."
 msgstr ""
-"Для повернення до початкових параметрів визначення назв вузлів за адресами"
-" слід пройти розпізнавання."
+"Для повернення до початкових параметрів визначення назв вузлів за адресами "
+"слід пройти розпізнавання."
 
 #: src/timedate/org.freedesktop.timedate1.policy:22
 msgid "Set system time"
@@ -885,38 +894,38 @@ msgstr ""
 "Потрібна автентифікація, щоб контролювати, чи синхронізування часу через "
 "мережу запущено."
 
-#: src/core/dbus-unit.c:355
+#: src/core/dbus-unit.c:356
 msgid "Authentication is required to start '$(unit)'."
 msgstr "Потрібна автентифікація, щоб запустити «$(unit)»."
 
-#: src/core/dbus-unit.c:356
+#: src/core/dbus-unit.c:357
 msgid "Authentication is required to stop '$(unit)'."
 msgstr "Потрібна автентифікація, щоб зупинити «$(unit)»."
 
-#: src/core/dbus-unit.c:357
+#: src/core/dbus-unit.c:358
 msgid "Authentication is required to reload '$(unit)'."
 msgstr "Потрібна автентифікація, щоб перезавантажити «$(unit)»."
 
-#: src/core/dbus-unit.c:358 src/core/dbus-unit.c:359
+#: src/core/dbus-unit.c:359 src/core/dbus-unit.c:360
 msgid "Authentication is required to restart '$(unit)'."
 msgstr "Потрібна автентифікація, щоб перезапустити «$(unit)»."
 
-#: src/core/dbus-unit.c:531
+#: src/core/dbus-unit.c:532
 msgid ""
 "Authentication is required to send a UNIX signal to the processes of "
 "'$(unit)'."
 msgstr ""
 "Потрібна автентифікація, щоб надіслати сигнал UNIX до процесів «$(unit)»."
 
-#: src/core/dbus-unit.c:562
+#: src/core/dbus-unit.c:563
 msgid "Authentication is required to reset the \"failed\" state of '$(unit)'."
 msgstr "Потрібна автентифікація, щоб скинути «пошкоджений» стан з «$(unit)»."
 
-#: src/core/dbus-unit.c:595
+#: src/core/dbus-unit.c:596
 msgid "Authentication is required to set properties on '$(unit)'."
 msgstr "Потрібна автентифікація, щоб вказати властивості на «$(unit)»."
 
-#: src/core/dbus-unit.c:704
+#: src/core/dbus-unit.c:705
 msgid ""
 "Authentication is required to delete files and directories associated with "
 "'$(unit)'."
index 7196cc50bff2f95e52656bdb99c88c1c30d86569..2655f62c694d07f13f8322fbcb0c16ce8619d76d 100644 (file)
@@ -64,15 +64,15 @@ msgid "Authentication is required to reload the systemd state."
 msgstr "重新载入 systemd 状态需要认证。"
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:1
-msgid "Set host name"
+msgid "Set hostname"
 msgstr "设置主机名"
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:2
-msgid "Authentication is required to set the local host name."
+msgid "Authentication is required to set the local hostname."
 msgstr "设置本地主机名需要认证。"
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:3
-msgid "Set static host name"
+msgid "Set static hostname"
 msgstr "设置静态主机名"
 
 # For pretty hostname, the zh_CN/zh_TW translation should be discussed again.
@@ -81,8 +81,8 @@ msgstr "设置静态主机名"
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:4
 #, fuzzy
 msgid ""
-"Authentication is required to set the statically configured local host name, "
-"as well as the pretty host name."
+"Authentication is required to set the statically configured local hostname, "
+"as well as the pretty hostname."
 msgstr "设置静态本地主机名或美观主机名需要认证。"
 
 #: ../src/hostname/org.freedesktop.hostname1.policy.in.h:5
index d14a338e5c4989609decea125f30676d2946905c..ac6cd99efa42337d16d59a83dc319c754fedd628 100644 (file)
@@ -62,21 +62,21 @@ msgid "Authentication is required to reload the systemd state."
 msgstr "重新載入 systemd 狀態需要驗證。"
 
 #: src/hostname/org.freedesktop.hostname1.policy:20
-msgid "Set host name"
+msgid "Set hostname"
 msgstr "設定主機名稱"
 
 #: src/hostname/org.freedesktop.hostname1.policy:21
-msgid "Authentication is required to set the local host name."
+msgid "Authentication is required to set the local hostname."
 msgstr "設定主機名稱需要驗證。"
 
 #: src/hostname/org.freedesktop.hostname1.policy:30
-msgid "Set static host name"
+msgid "Set static hostname"
 msgstr "設定靜態主機名稱"
 
 #: src/hostname/org.freedesktop.hostname1.policy:31
 msgid ""
-"Authentication is required to set the statically configured local host name, "
-"as well as the pretty host name."
+"Authentication is required to set the statically configured local hostname, "
+"as well as the pretty hostname."
 msgstr "設定靜態預先設定或 pretty 本地主機名稱需要身份驗證。"
 
 #: src/hostname/org.freedesktop.hostname1.policy:41
index dc9d02f0b937ac7cb145e79915154ed441d46667..dd268ae1b5fdb710f758daae0aeceeeb388962c1 100644 (file)
@@ -19,6 +19,9 @@ enable getty@.service
 enable systemd-timesyncd.service
 enable systemd-networkd.service
 enable systemd-resolved.service
+enable systemd-homed.service
+enable systemd-userdbd.socket
+enable systemd-pstore.service
 
 disable console-getty.service
 disable debug-shell.service
@@ -30,25 +33,13 @@ enable reboot.target
 disable rescue.target
 disable exit.target
 
+disable systemd-networkd-wait-online.service
+disable systemd-time-wait-sync.service
+disable systemd-boot-check-no-failures.service
+disable systemd-network-generator.service
+
 disable syslog.socket
 
 disable systemd-journal-gatewayd.*
 disable systemd-journal-remote.*
 disable systemd-journal-upload.*
-
-enable systemd-pstore.service
-
-# Passive targets: always off by default, since they should only be pulled in
-# by dependent units.
-
-disable cryptsetup-pre.target
-disable getty-pre.target
-disable local-fs-pre.target
-disable network.target
-disable network-pre.target
-disable nss-lookup.target
-disable nss-user-lookup.target
-disable remote-fs-pre.target
-disable rpcbind.target
-disable time-set.target
-disable time-sync.target
index fd402c8c11cf04fb01c6979e8c3113dd5e4e6feb..5ee969090ddae4f1fe35e6f8b8ce31336756f19c 100644 (file)
@@ -13,9 +13,3 @@
 
 enable systemd-tmpfiles-setup.service
 enable systemd-tmpfiles-clean.timer
-
-# Passive targets: always off by default, since they should only be pulled in
-# by dependent units.
-
-disable graphical-session-pre.target
-disable graphical-session.target
index 50747a19881d8adbe1433978123507a3f26bdcfc..cef78f9d687abb1d4f92c78c8a495f5c179c6860 100644 (file)
@@ -10,8 +10,9 @@ SUBSYSTEM=="virtio-ports", KERNEL=="vport*", ATTR{name}=="?*", SYMLINK+="virtio-
 SUBSYSTEM=="rtc", ATTR{hctosys}=="1", SYMLINK+="rtc"
 SUBSYSTEM=="rtc", KERNEL=="rtc0", SYMLINK+="rtc", OPTIONS+="link_priority=-100"
 
-SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", IMPORT{builtin}="usb_id", IMPORT{builtin}="hwdb --subsystem=usb"
+SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", IMPORT{builtin}="usb_id", IMPORT{builtin}="hwdb --subsystem=usb", GOTO="default_hwdb_imported"
 ENV{MODALIAS}!="", IMPORT{builtin}="hwdb --subsystem=$env{SUBSYSTEM}"
+LABEL="default_hwdb_imported"
 
 ACTION!="add", GOTO="default_end"
 
diff --git a/rules.d/60-autosuspend.rules b/rules.d/60-autosuspend.rules
new file mode 100644 (file)
index 0000000..1f9ebef
--- /dev/null
@@ -0,0 +1,14 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION!="add", GOTO="autosuspend_end"
+
+# I2C rules
+SUBSYSTEM=="i2c", ATTR{name}=="cyapa", \
+  ATTR{power/control}="on", GOTO="autosuspend_end"
+
+# Enable autosuspend if hwdb says so. Here we are relying on
+# the hwdb import done earlier based on MODALIAS.
+ENV{ID_AUTOSUSPEND}=="1", TEST=="power/control", \
+  ATTR{power/control}="auto"
+
+LABEL="autosuspend_end"
index 90847927ec1b2974478ab340a14798bf21a73c00..fc7f733e20058504635dbd8a32e5e645eb795d2b 100644 (file)
@@ -94,6 +94,9 @@ ENV{DEVTYPE}=="disk", DEVPATH!="*/virtual/*", IMPORT{builtin}="path_id"
 KERNEL=="mmcblk[0-9]boot[0-9]", ENV{DEVTYPE}=="disk", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH}-boot%n"
 KERNEL!="mmcblk[0-9]boot[0-9]", ENV{DEVTYPE}=="disk", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH}"
 ENV{DEVTYPE}=="partition", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH}-part%n"
+# compatible links for ATA devices
+KERNEL!="mmcblk[0-9]boot[0-9]", ENV{DEVTYPE}=="disk", ENV{ID_PATH_ATA_COMPAT}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH_ATA_COMPAT}"
+ENV{DEVTYPE}=="partition", ENV{ID_PATH_ATA_COMPAT}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH_ATA_COMPAT}-part%n"
 
 # legacy virtio-pci by-path links (deprecated)
 KERNEL=="vd*[!0-9]", ENV{ID_PATH}=="pci-*", SYMLINK+="disk/by-path/virtio-$env{ID_PATH}"
index f303e27fd54cfa4c82804cf2b3486202284548d0..b1626650b7f2e4b0be4c1629958a95c83817a63a 100644 (file)
@@ -4,8 +4,9 @@ ACTION=="remove", GOTO="serial_end"
 SUBSYSTEM!="tty", GOTO="serial_end"
 
 SUBSYSTEMS=="pci", ENV{ID_BUS}="pci", ENV{ID_VENDOR_ID}="$attr{vendor}", ENV{ID_MODEL_ID}="$attr{device}"
-SUBSYSTEMS=="pci", IMPORT{builtin}="hwdb --subsystem=pci"
-SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id", IMPORT{builtin}="hwdb --subsystem=usb"
+# We already ran the hwdb builtin for devices with MODALIAS in 50-default.rules.
+# Let's cover the remaining case here, where we walk up the tree to find a node with $MODALIAS.
+ENV{MODALIAS}=="", SUBSYSTEMS=="pci", IMPORT{builtin}="hwdb --subsystem=pci"
 
 # /dev/serial/by-path/, /dev/serial/by-id/ for USB devices
 KERNEL!="ttyUSB[0-9]*|ttyACM[0-9]*", GOTO="serial_end"
diff --git a/rules.d/61-autosuspend-manual.rules b/rules.d/61-autosuspend-manual.rules
deleted file mode 100644 (file)
index 5a16727..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-# This udev rule is for any devices that should enter automatic suspend
-# but are not already included in generated rules from Chromium OS via
-# tools/make-autosuspend-rules.py
-#
-
-ACTION!="add", GOTO="autosuspend_manual_end"
-SUBSYSTEM!="usb", GOTO="autosuspend_manual_end"
-
-SUBSYSTEM=="usb", GOTO="autosuspend_manual_usb"
-
-# USB rules
-LABEL="autosuspend_manual_usb"
-ATTR{idVendor}=="056a", ATTR{idProduct}=="51a0", GOTO="autosuspend_manual_enable"
-ATTR{idVendor}=="058f", ATTR{idProduct}=="9540", GOTO="autosuspend_manual_enable"
-GOTO="autosuspend_manual_end"
-
-# Enable autosuspend
-LABEL="autosuspend_manual_enable"
-TEST=="power/control", ATTR{power/control}="auto", GOTO="autosuspend_manual_end"
-
-LABEL="autosuspend_manual_end"
index 13d1d330cf5e2c67d43479b59aacb782101a2306..ca4445d7748e08e6af3d4faf73b8373fcb906eec 100644 (file)
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: LGPL-2.1+
 
 rules = files('''
+        60-autosuspend.rules
         60-block.rules
         60-cdrom_id.rules
         60-drm.rules
@@ -14,7 +15,6 @@ rules = files('''
         60-persistent-v4l.rules
         60-sensor.rules
         60-serial.rules
-        61-autosuspend-manual.rules
         70-joystick.rules
         70-mouse.rules
         70-touchpad.rules
@@ -45,11 +45,3 @@ foreach file : rules_in
                      install_dir : udevrulesdir)
         all_rules += gen
 endforeach
-
-auto_suspend_rules = custom_target(
-        '60-autosuspend-chromiumos.rules',
-        output : '60-autosuspend-chromiumos.rules',
-        command : make_autosuspend_rules_py,
-        capture : true,
-        install : true,
-        install_dir: [udevrulesdir])
index 2bf8ce0d52eaea880c965696472f271b3b91de37..abffb205889656d60034354f8f406a712a7d03cc 100755 (executable)
@@ -4,8 +4,8 @@ set -eux
 
 # default to Debian testing
 DISTRO=${DISTRO:-debian}
-RELEASE=${RELEASE:-buster}
-BRANCH=${BRANCH:-debian/master}
+RELEASE=${RELEASE:-bullseye}
+BRANCH=${BRANCH:-upstream-ci}
 ARCH=${ARCH:-amd64}
 CONTAINER=${RELEASE}-${ARCH}
 CACHE_DIR=${SEMAPHORE_CACHE_DIR:=/tmp}
@@ -13,11 +13,12 @@ AUTOPKGTEST_DIR="${CACHE_DIR}/autopkgtest"
 # semaphore cannot expose these, but useful for interactive/local runs
 ARTIFACTS_DIR=/tmp/artifacts
 PHASES=(${@:-SETUP RUN})
+UBUNTU_RELEASE="$(lsb_release -cs)"
 
 create_container() {
     # create autopkgtest LXC image; this sometimes fails with "Unable to fetch
     # GPG key from keyserver", so retry a few times
-    for retry in $(seq 5); do
+    for retry in {1..5}; do
         sudo lxc-create -n $CONTAINER -t download -- -d $DISTRO -r $RELEASE -a $ARCH --keyserver hkp://keyserver.ubuntu.com:80 && break
         sleep $((retry*retry))
     done
@@ -36,7 +37,7 @@ apt-get -q --allow-releaseinfo-change update
 apt-get -y dist-upgrade
 apt-get install -y eatmydata
 # The following four are needed as long as these deps are not covered by Debian's own packaging
-apt-get install -y libfdisk-dev libp11-kit-dev libssl-dev libpwquality-dev
+apt-get install -y fdisk tree libfdisk-dev libp11-kit-dev libssl-dev libpwquality-dev
 apt-get purge --auto-remove -y unattended-upgrades
 systemctl unmask systemd-networkd
 systemctl enable systemd-networkd
@@ -51,9 +52,9 @@ for phase in "${PHASES[@]}"; do
             sudo rm -f /etc/apt/sources.list.d/*
 
             # enable backports for latest LXC
-            echo 'deb http://archive.ubuntu.com/ubuntu xenial-backports main restricted universe multiverse' | sudo tee -a /etc/apt/sources.list.d/backports.list
+            echo "deb http://archive.ubuntu.com/ubuntu $UBUNTU_RELEASE-backports main restricted universe multiverse" | sudo tee -a /etc/apt/sources.list.d/backports.list
             sudo apt-get -q update
-            sudo apt-get install -y -t xenial-backports lxc
+            sudo apt-get install -y -t "$UBUNTU_RELEASE-backports" lxc
             sudo apt-get install -y python3-debian git dpkg-dev fakeroot
 
             [ -d $AUTOPKGTEST_DIR ] || git clone --quiet --depth=1 https://salsa.debian.org/ci-team/autopkgtest.git "$AUTOPKGTEST_DIR"
@@ -66,9 +67,9 @@ for phase in "${PHASES[@]}"; do
             git checkout FETCH_HEAD debian
 
             # craft changelog
-            UPSTREAM_VER=$(git describe | sed 's/^v//')
+            UPSTREAM_VER=$(git describe | sed 's/^v//;s/-/./g')
             cat << EOF > debian/changelog.new
-systemd (${UPSTREAM_VER}-0) UNRELEASED; urgency=low
+systemd (${UPSTREAM_VER}.0) UNRELEASED; urgency=low
 
   * Automatic build for upstream test
 
index 9c3189f89fee124f8cf7c65839fb63ff6e41356f..9fc6cb3df544d50dcba42d88bc8f6740ee23adae 100644 (file)
@@ -59,6 +59,7 @@ _bootctl() {
         # systemd-efi-options takes an argument, but it is free-form, so we cannot complete it
         [STANDALONE]='help status install update remove is-installed random-seed systemd-efi-options list'
         [BOOTENTRY]='set-default set-oneshot'
+        [BOOLEAN]='reboot-to-firmware'
     )
 
     for ((i=0; i < COMP_CWORD; i++)); do
index f81dafba8ee4a6bd3e61789599308f7d104e078b..fdbe32e5f7aeeed3ac967c6d411c34dadfdb45b4 100644 (file)
@@ -7,7 +7,7 @@
 
 __systemctl() {
     local mode=$1; shift 1
-    systemctl $mode --full --no-legend --no-pager "$@" 2>/dev/null
+    systemctl $mode --full --no-legend --no-pager --plain "$@" 2>/dev/null
 }
 
 __systemd_properties() {
index 1b4f1b0d10403d6f9c6c11a7ce769c534de21834..0c61f54504c65341959955a2c7c48491acaae152 100644 (file)
@@ -31,7 +31,7 @@ __get_machines() {
 }
 
 __get_services() {
-    systemctl list-units --no-legend --no-pager -t service --all $1 | \
+    systemctl list-units --no-legend --no-pager --plain -t service --all $1 | \
         { while read -r a b c; do [[ $b == "loaded" ]]; echo " $a"; done }
 }
 
index 10f6b38fcc5aec569b71646d496d61ffbc1072a2..ae41f8ba5a44299379794f4e7eeabff5409dd4e6 100644 (file)
@@ -29,7 +29,7 @@ __get_machines() {
 }
 
 __get_units_have_cgroup() {
-    systemctl $1 list-units | {
+    systemctl $1 --full --no-legend --no-pager --plain list-units | {
         while read -r a b c d; do
             [[ $c == "active" && ${a##*.} =~ (service|socket|mount|swap|slice|scope) ]] && echo " $a"
         done
index d263fd5dd93b0b936fd7d9473a49ffea336a4cd6..a8bd406fb3118a11fd1dafc1b1c94e843324a367 100644 (file)
@@ -30,7 +30,7 @@ __get_users() {
 
 __get_slices() {
     local a b
-    systemctl list-units -t slice --no-legend --no-pager | { while read a b; do echo " $a"; done; };
+    systemctl list-units -t slice --no-legend --no-pager --plain | { while read a b; do echo " $a"; done; };
 }
 
 __get_machines() {
@@ -71,7 +71,7 @@ _systemd_nspawn() {
                       --pivot-root --property --private-users --network-namespace-path --network-ipvlan
                       --network-veth-extra --network-zone -p --port --system-call-filter --overlay --overlay-ro
                       --settings --rlimit --hostname --no-new-privileges --oom-score-adjust --cpu-affinity
-                      --resolv-conf --timezone'
+                      --resolv-conf --timezone --root-hash-sig'
     )
 
     _init_completion || return
@@ -183,6 +183,10 @@ _systemd_nspawn() {
             --timezone)
                 comps=$( systemd-nspawn --timezone=help 2>/dev/null )
                 ;;
+            --root-hash-sig)
+                compopt -o nospace
+                comps=$( compgen -A file -- "$cur" )
+                ;;
         esac
         COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
         return 0
index 71692aa19c9ac12d46b3fe5ca15f2160a62ad820..88f4443ad9727f00493404f16935a989241ce5b8 100644 (file)
@@ -18,7 +18,7 @@
 
 __systemctl() {
     local mode=$1; shift 1
-    systemctl $mode --full --no-legend "$@"
+    systemctl $mode --full --no-legend --no-pager --plain "$@"
 }
 
 __get_slice_units () { __systemctl $1 list-units --all -t slice \
index cb12c25538f1d94995fba72045b10c9d147d3b92..76126a63297c79cd21649319c3a4c79433d8def5 100644 (file)
@@ -38,7 +38,7 @@ __get_all_devs() {
 }
 
 __get_all_device_units() {
-    systemctl list-units -t device --full --no-legend --no-pager 2>/dev/null | \
+    systemctl list-units -t device --full --no-legend --no-pager --plain 2>/dev/null | \
         { while read -r a b; do echo "$a"; done; }
 }
 
index 6a903c562b05e02f496338b4ef533990a0892b78..fc051bd87d05a908d5ab30b8d8e7f247f4f507cb 100644 (file)
@@ -42,6 +42,7 @@ _bootctl_reboot-to-firmware() {
         "is-installed:Test whether systemd-boot is installed in the ESP"
         "random-seed:Initialize random seed in ESP and EFI variables"
         "systemd-efi-options:Query or set system options string in EFI variable"
+        "reboot-to-firmware:Query or set reboot-to-firmware EFI flag"
         "list:List boot loader entries"
         "set-default:Set the default boot loader entry"
         "set-oneshot:Set the default boot loader entry only for the next boot"
index 1b6f9f0833ad512d4b5dfc677b74319385869dfe..582d469c350e660a3eb68cd5f9cb19a2bb0678d6 100644 (file)
 # @todo _systemd-run has a helper with the same name, so we must redefine
 __systemctl()
 {
-    systemctl $_sys_service_mgr --full --no-legend --no-pager "$@" 2>/dev/null
+    systemctl $_sys_service_mgr --full --no-legend --no-pager --plain "$@" 2>/dev/null
 }
 
 
index ca0faa14844dd2e3fc1224992c921361a6626e81..22b82d66fd814dcd9ad001df081f2f990e854ffa 100644 (file)
@@ -5,7 +5,7 @@
 __systemctl() {
     local -a _modes
     _modes=("--user" "--system")
-    systemctl ${words:*_modes} --full --no-legend --no-pager "$@" 2>/dev/null
+    systemctl ${words:*_modes} --full --no-legend --no-pager --plain "$@" 2>/dev/null
 }
 
 (( $+functions[__systemd-run_get_slices] )) ||
index d0cefa099202d08587eea631f05986e537ee5489..52ad382637f0b2816ab23347d3b47999f5beae25 100644 (file)
@@ -21,6 +21,7 @@ static const condition_definition condition_definitions[] = {
         { "ConditionPathIsSymbolicLink",     config_parse_unit_condition_path,   CONDITION_PATH_IS_SYMBOLIC_LINK    },
         { "ConditionPathIsMountPoint",       config_parse_unit_condition_path,   CONDITION_PATH_IS_MOUNT_POINT      },
         { "ConditionPathIsReadWrite",        config_parse_unit_condition_path,   CONDITION_PATH_IS_READ_WRITE       },
+        { "ConditionPathIsEncrypted",        config_parse_unit_condition_path,   CONDITION_PATH_IS_ENCRYPTED        },
         { "ConditionDirectoryNotEmpty",      config_parse_unit_condition_path,   CONDITION_DIRECTORY_NOT_EMPTY      },
         { "ConditionFileNotEmpty",           config_parse_unit_condition_path,   CONDITION_FILE_NOT_EMPTY           },
         { "ConditionFileIsExecutable",       config_parse_unit_condition_path,   CONDITION_FILE_IS_EXECUTABLE       },
@@ -44,6 +45,7 @@ static const condition_definition condition_definitions[] = {
         { "AssertPathIsSymbolicLink",        config_parse_unit_condition_path,   CONDITION_PATH_IS_SYMBOLIC_LINK    },
         { "AssertPathIsMountPoint",          config_parse_unit_condition_path,   CONDITION_PATH_IS_MOUNT_POINT      },
         { "AssertPathIsReadWrite",           config_parse_unit_condition_path,   CONDITION_PATH_IS_READ_WRITE       },
+        { "AssertPathIsEncrypted",           config_parse_unit_condition_path,   CONDITION_PATH_IS_ENCRYPTED        },
         { "AssertDirectoryNotEmpty",         config_parse_unit_condition_path,   CONDITION_DIRECTORY_NOT_EMPTY      },
         { "AssertFileNotEmpty",              config_parse_unit_condition_path,   CONDITION_FILE_NOT_EMPTY           },
         { "AssertFileIsExecutable",          config_parse_unit_condition_path,   CONDITION_FILE_IS_EXECUTABLE       },
@@ -83,11 +85,14 @@ static int parse_condition(Unit *u, const char *line) {
                 p = startswith(line, c->name);
                 if (!p)
                         continue;
+
                 p += strspn(p, WHITESPACE);
+
                 if (*p != '=')
-                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected \"=\" in \"%s\".", line);
+                        continue;
+                p++;
 
-                p += 1 + strspn(p + 1, WHITESPACE);
+                p += strspn(p, WHITESPACE);
 
                 return c->parser(NULL, "(stdin)", 0, NULL, 0, c->name, c->type, p, target, u);
         }
@@ -143,11 +148,11 @@ int verify_conditions(char **lines, UnitFileScope scope) {
                         return r;
         }
 
-        r = condition_test_list(u->asserts, assert_type_to_string, log_helper, u);
+        r = condition_test_list(u->asserts, environ, assert_type_to_string, log_helper, u);
         if (u->asserts)
                 log_notice("Asserts %s.", r > 0 ? "succeeded" : "failed");
 
-        q = condition_test_list(u->conditions, condition_type_to_string, log_helper, u);
+        q = condition_test_list(u->conditions, environ, condition_type_to_string, log_helper, u);
         if (u->conditions)
                 log_notice("Conditions %s.", q > 0 ? "succeeded" : "failed");
 
index 5e7756bff2cca5ba84c36f383dcf6b4127db8bb4..d4996c3c6552f477d5c5d80241ffb651373dbfef 100644 (file)
@@ -4,6 +4,7 @@
 
 #include "analyze-security.h"
 #include "bus-error.h"
+#include "bus-map-properties.h"
 #include "bus-unit-util.h"
 #include "bus-util.h"
 #include "env-util.h"
@@ -91,7 +92,7 @@ struct security_info {
 
         char **system_call_architectures;
 
-        bool system_call_filter_whitelist;
+        bool system_call_filter_allow_list;
         Set *system_call_filter;
 
         uint32_t _umask;
@@ -141,7 +142,7 @@ static void security_info_free(struct security_info *i) {
         strv_free(i->supplementary_groups);
         strv_free(i->system_call_architectures);
 
-        set_free_free(i->system_call_filter);
+        set_free(i->system_call_filter);
 }
 
 static bool security_info_runs_privileged(const struct security_info *i)  {
@@ -492,7 +493,7 @@ static int assess_system_call_architectures(
 
 #if HAVE_SECCOMP
 
-static bool syscall_names_in_filter(Set *s, bool whitelist, const SyscallFilterSet *f) {
+static bool syscall_names_in_filter(Set *s, bool allow_list, const SyscallFilterSet *f) {
         const char *syscall;
 
         NULSTR_FOREACH(syscall, f->value) {
@@ -502,7 +503,7 @@ static bool syscall_names_in_filter(Set *s, bool whitelist, const SyscallFilterS
                         const SyscallFilterSet *g;
 
                         assert_se(g = syscall_filter_set_find(syscall));
-                        if (syscall_names_in_filter(s, whitelist, g))
+                        if (syscall_names_in_filter(s, allow_list, g))
                                 return true; /* bad! */
 
                         continue;
@@ -513,7 +514,7 @@ static bool syscall_names_in_filter(Set *s, bool whitelist, const SyscallFilterS
                 if (id < 0)
                         continue;
 
-                if (set_contains(s, syscall) == whitelist) {
+                if (set_contains(s, syscall) == allow_list) {
                         log_debug("Offending syscall filter item: %s", syscall);
                         return true; /* bad! */
                 }
@@ -541,30 +542,30 @@ static int assess_system_call_filter(
         assert(a->parameter < _SYSCALL_FILTER_SET_MAX);
         f = syscall_filter_sets + a->parameter;
 
-        if (!info->system_call_filter_whitelist && set_isempty(info->system_call_filter)) {
+        if (!info->system_call_filter_allow_list && set_isempty(info->system_call_filter)) {
                 d = strdup("Service does not filter system calls");
                 b = 10;
         } else {
                 bool bad;
 
                 log_debug("Analyzing system call filter, checking against: %s", f->name);
-                bad = syscall_names_in_filter(info->system_call_filter, info->system_call_filter_whitelist, f);
+                bad = syscall_names_in_filter(info->system_call_filter, info->system_call_filter_allow_list, f);
                 log_debug("Result: %s", bad ? "bad" : "good");
 
-                if (info->system_call_filter_whitelist) {
+                if (info->system_call_filter_allow_list) {
                         if (bad) {
-                                (void) asprintf(&d, "System call whitelist defined for service, and %s is included", f->name);
+                                (void) asprintf(&d, "System call allow list defined for service, and %s is included", f->name);
                                 b = 9;
                         } else {
-                                (void) asprintf(&d, "System call whitelist defined for service, and %s is not included", f->name);
+                                (void) asprintf(&d, "System call allow list defined for service, and %s is not included", f->name);
                                 b = 0;
                         }
                 } else {
                         if (bad) {
-                                (void) asprintf(&d, "System call blacklist defined for service, and %s is not included", f->name);
+                                (void) asprintf(&d, "System call deny list defined for service, and %s is not included", f->name);
                                 b = 10;
                         } else {
-                                (void) asprintf(&d, "System call blacklist defined for service, and %s is included", f->name);
+                                (void) asprintf(&d, "System call deny list defined for service, and %s is included", f->name);
                                 b = 5;
                         }
                 }
@@ -599,13 +600,13 @@ static int assess_ip_address_allow(
                 d = strdup("Service defines custom ingress/egress IP filters with BPF programs");
                 b = 0;
         } else if (!info->ip_address_deny_all) {
-                d = strdup("Service does not define an IP address whitelist");
+                d = strdup("Service does not define an IP address allow list");
                 b = 10;
         } else if (info->ip_address_allow_other) {
-                d = strdup("Service defines IP address whitelist with non-localhost entries");
+                d = strdup("Service defines IP address allow list with non-localhost entries");
                 b = 5;
         } else if (info->ip_address_allow_localhost) {
-                d = strdup("Service defines IP address whitelist with only localhost entries");
+                d = strdup("Service defines IP address allow list with only localhost entries");
                 b = 2;
         } else {
                 d = strdup("Service blocks all IP address ranges");
@@ -1639,7 +1640,7 @@ static int property_read_restrict_address_families(
                 void *userdata) {
 
         struct security_info *info = userdata;
-        int whitelist, r;
+        int allow_list, r;
 
         assert(bus);
         assert(member);
@@ -1649,7 +1650,7 @@ static int property_read_restrict_address_families(
         if (r < 0)
                 return r;
 
-        r = sd_bus_message_read(m, "b", &whitelist);
+        r = sd_bus_message_read(m, "b", &allow_list);
         if (r < 0)
                 return r;
 
@@ -1657,7 +1658,7 @@ static int property_read_restrict_address_families(
                 info->restrict_address_family_unix =
                 info->restrict_address_family_netlink =
                 info->restrict_address_family_packet =
-                info->restrict_address_family_other = whitelist;
+                info->restrict_address_family_other = allow_list;
 
         r = sd_bus_message_enter_container(m, 'a', "s");
         if (r < 0)
@@ -1673,15 +1674,15 @@ static int property_read_restrict_address_families(
                         break;
 
                 if (STR_IN_SET(name, "AF_INET", "AF_INET6"))
-                        info->restrict_address_family_inet = !whitelist;
+                        info->restrict_address_family_inet = !allow_list;
                 else if (streq(name, "AF_UNIX"))
-                        info->restrict_address_family_unix = !whitelist;
+                        info->restrict_address_family_unix = !allow_list;
                 else if (streq(name, "AF_NETLINK"))
-                        info->restrict_address_family_netlink = !whitelist;
+                        info->restrict_address_family_netlink = !allow_list;
                 else if (streq(name, "AF_PACKET"))
-                        info->restrict_address_family_packet = !whitelist;
+                        info->restrict_address_family_packet = !allow_list;
                 else
-                        info->restrict_address_family_other = !whitelist;
+                        info->restrict_address_family_other = !allow_list;
         }
 
         r = sd_bus_message_exit_container(m);
@@ -1699,7 +1700,7 @@ static int property_read_system_call_filter(
                 void *userdata) {
 
         struct security_info *info = userdata;
-        int whitelist, r;
+        int allow_list, r;
 
         assert(bus);
         assert(member);
@@ -1709,11 +1710,11 @@ static int property_read_system_call_filter(
         if (r < 0)
                 return r;
 
-        r = sd_bus_message_read(m, "b", &whitelist);
+        r = sd_bus_message_read(m, "b", &allow_list);
         if (r < 0)
                 return r;
 
-        info->system_call_filter_whitelist = whitelist;
+        info->system_call_filter_allow_list = allow_list;
 
         r = sd_bus_message_enter_container(m, 'a', "s");
         if (r < 0)
@@ -1728,11 +1729,7 @@ static int property_read_system_call_filter(
                 if (r == 0)
                         break;
 
-                r = set_ensure_allocated(&info->system_call_filter, &string_hash_ops);
-                if (r < 0)
-                        return r;
-
-                r = set_put_strdup(info->system_call_filter, name);
+                r = set_put_strdup(&info->system_call_filter, name);
                 if (r < 0)
                         return r;
         }
index 8275360adc3911bde3aa996ae9640cbe9c357ca2..30cb79d50966e389fe7b0933734d310e0e4b259a 100644 (file)
@@ -94,6 +94,7 @@ static int generate_path(char **var, char **filenames) {
 }
 
 static int verify_socket(Unit *u) {
+        Unit *service;
         int r;
 
         assert(u);
@@ -101,26 +102,15 @@ static int verify_socket(Unit *u) {
         if (u->type != UNIT_SOCKET)
                 return 0;
 
-        /* Cannot run this without the service being around */
-
-        /* This makes sure instance is created if necessary. */
-        r = socket_instantiate_service(SOCKET(u));
+        r = socket_load_service_unit(SOCKET(u), -1, &service);
         if (r < 0)
-                return log_unit_error_errno(u, r, "Socket cannot be started, failed to create instance: %m");
-
-        /* This checks both type of sockets */
-        if (UNIT_ISSET(SOCKET(u)->service)) {
-                Service *service;
-
-                service = SERVICE(UNIT_DEREF(SOCKET(u)->service));
-                log_unit_debug(u, "Using %s", UNIT(service)->id);
+                return log_unit_error_errno(u, r, "service unit for the socket cannot be loaded: %m");
 
-                if (UNIT(service)->load_state != UNIT_LOADED) {
-                        log_unit_error(u, "Service %s not loaded, %s cannot be started.", UNIT(service)->id, u->id);
-                        return -ENOENT;
-                }
-        }
+        if (service->load_state != UNIT_LOADED)
+                return log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOENT),
+                                            "service %s not loaded, socket cannot be started.", service->id);
 
+        log_unit_debug(u, "using service unit %s.", service->id);
         return 0;
 }
 
index 3ea9041c188c3403e928a1e105443d0c57b77153..9d64e3c76cddf8829caac1f4b4d8bc4dc60f451e 100644 (file)
@@ -17,8 +17,9 @@
 #include "analyze-verify.h"
 #include "build.h"
 #include "bus-error.h"
+#include "bus-locator.h"
+#include "bus-map-properties.h"
 #include "bus-unit-util.h"
-#include "bus-util.h"
 #include "calendarspec.h"
 #include "conf-files.h"
 #include "copy.h"
@@ -349,14 +350,7 @@ static int acquire_time_data(sd_bus *bus, struct unit_times **out) {
         if (r < 0)
                 return r;
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "ListUnits",
-                        &error, &reply,
-                        NULL);
+        r = bus_call_method(bus, bus_systemd_mgr, "ListUnits", &error, &reply, NULL);
         if (r < 0)
                 return log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r));
 
@@ -633,7 +627,7 @@ static int analyze_plot(int argc, char *argv[], void *userdata) {
 
         r = acquire_bus(&bus, &use_full_bus);
         if (r < 0)
-                return log_error_errno(r, "Failed to create bus connection: %m");
+                return bus_log_connect_error(r);
 
         n = acquire_boot_times(bus, &boot);
         if (n < 0)
@@ -1032,7 +1026,7 @@ static int analyze_critical_chain(int argc, char *argv[], void *userdata) {
 
         r = acquire_bus(&bus, NULL);
         if (r < 0)
-                return log_error_errno(r, "Failed to create bus connection: %m");
+                return bus_log_connect_error(r);
 
         n = acquire_time_data(bus, &times);
         if (n <= 0)
@@ -1075,7 +1069,7 @@ static int analyze_blame(int argc, char *argv[], void *userdata) {
 
         r = acquire_bus(&bus, NULL);
         if (r < 0)
-                return log_error_errno(r, "Failed to create bus connection: %m");
+                return bus_log_connect_error(r);
 
         n = acquire_time_data(bus, &times);
         if (n <= 0)
@@ -1132,7 +1126,7 @@ static int analyze_time(int argc, char *argv[], void *userdata) {
 
         r = acquire_bus(&bus, NULL);
         if (r < 0)
-                return log_error_errno(r, "Failed to create bus connection: %m");
+                return bus_log_connect_error(r);
 
         r = pretty_boot_time(bus, &buf);
         if (r < 0)
@@ -1270,7 +1264,7 @@ static int dot(int argc, char *argv[], void *userdata) {
 
         r = acquire_bus(&bus, NULL);
         if (r < 0)
-                return log_error_errno(r, "Failed to create bus connection: %m");
+                return bus_log_connect_error(r);
 
         r = expand_patterns(bus, strv_skip(argv, 1), &expanded_patterns);
         if (r < 0)
@@ -1284,15 +1278,7 @@ static int dot(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return r;
 
-        r = sd_bus_call_method(
-                        bus,
-                       "org.freedesktop.systemd1",
-                       "/org/freedesktop/systemd1",
-                       "org.freedesktop.systemd1.Manager",
-                       "ListUnits",
-                       &error,
-                       &reply,
-                       "");
+        r = bus_call_method(bus, bus_systemd_mgr, "ListUnits", &error, &reply, "");
         if (r < 0)
                 log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r));
 
@@ -1334,15 +1320,7 @@ static int dump_fallback(sd_bus *bus) {
 
         assert(bus);
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "Dump",
-                        &error,
-                        &reply,
-                        NULL);
+        r = bus_call_method(bus, bus_systemd_mgr, "Dump", &error, &reply, NULL);
         if (r < 0)
                 return log_error_errno(r, "Failed to issue method call Dump: %s", bus_error_message(&error, r));
 
@@ -1363,22 +1341,14 @@ static int dump(int argc, char *argv[], void *userdata) {
 
         r = acquire_bus(&bus, NULL);
         if (r < 0)
-                return log_error_errno(r, "Failed to create bus connection: %m");
+                return bus_log_connect_error(r);
 
         (void) pager_open(arg_pager_flags);
 
         if (!sd_bus_can_send(bus, SD_BUS_TYPE_UNIX_FD))
                 return dump_fallback(bus);
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "DumpByFileDescriptor",
-                        &error,
-                        &reply,
-                        NULL);
+        r = bus_call_method(bus, bus_systemd_mgr, "DumpByFileDescriptor", &error, &reply, NULL);
         if (r < 0) {
                 /* fall back to Dump if DumpByFileDescriptor is not supported */
                 if (!IN_SET(r, -EACCES, -EBADR))
@@ -1442,17 +1412,9 @@ static int set_log_level(int argc, char *argv[], void *userdata) {
 
         r = acquire_bus(&bus, NULL);
         if (r < 0)
-                return log_error_errno(r, "Failed to create bus connection: %m");
+                return bus_log_connect_error(r);
 
-        r = sd_bus_set_property(
-                        bus,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "LogLevel",
-                        &error,
-                        "s",
-                        argv[1]);
+        r = bus_set_property(bus, bus_systemd_mgr, "LogLevel", &error, "s", argv[1]);
         if (r < 0)
                 return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
 
@@ -1467,16 +1429,9 @@ static int get_log_level(int argc, char *argv[], void *userdata) {
 
         r = acquire_bus(&bus, NULL);
         if (r < 0)
-                return log_error_errno(r, "Failed to create bus connection: %m");
+                return bus_log_connect_error(r);
 
-        r = sd_bus_get_property_string(
-                        bus,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "LogLevel",
-                        &error,
-                        &level);
+        r = bus_get_property_string(bus, bus_systemd_mgr, "LogLevel", &error, &level);
         if (r < 0)
                 return log_error_errno(r, "Failed to get log level: %s", bus_error_message(&error, r));
 
@@ -1498,17 +1453,9 @@ static int set_log_target(int argc, char *argv[], void *userdata) {
 
         r = acquire_bus(&bus, NULL);
         if (r < 0)
-                return log_error_errno(r, "Failed to create bus connection: %m");
+                return bus_log_connect_error(r);
 
-        r = sd_bus_set_property(
-                        bus,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "LogTarget",
-                        &error,
-                        "s",
-                        argv[1]);
+        r = bus_set_property(bus, bus_systemd_mgr, "LogTarget", &error, "s", argv[1]);
         if (r < 0)
                 return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
 
@@ -1523,16 +1470,9 @@ static int get_log_target(int argc, char *argv[], void *userdata) {
 
         r = acquire_bus(&bus, NULL);
         if (r < 0)
-                return log_error_errno(r, "Failed to create bus connection: %m");
+                return bus_log_connect_error(r);
 
-        r = sd_bus_get_property_string(
-                        bus,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "LogTarget",
-                        &error,
-                        &target);
+        r = bus_get_property_string(bus, bus_systemd_mgr, "LogTarget", &error, &target);
         if (r < 0)
                 return log_error_errno(r, "Failed to get log target: %s", bus_error_message(&error, r));
 
@@ -1655,7 +1595,7 @@ static int dump_exit_status(int argc, char *argv[], void *userdata) {
 #if HAVE_SECCOMP
 
 static int load_kernel_syscalls(Set **ret) {
-        _cleanup_(set_free_freep) Set *syscalls = NULL;
+        _cleanup_set_free_ Set *syscalls = NULL;
         _cleanup_fclose_ FILE *f = NULL;
         int r;
 
@@ -1691,11 +1631,7 @@ static int load_kernel_syscalls(Set **ret) {
                 if (STR_IN_SET(e, "newuname", "newfstat", "newstat", "newlstat", "sysctl"))
                         continue;
 
-                r = set_ensure_allocated(&syscalls, &string_hash_ops);
-                if (r < 0)
-                        return log_oom();
-
-                r = set_put_strdup(syscalls, e);
+                r = set_put_strdup(&syscalls, e);
                 if (r < 0)
                         return log_error_errno(r, "Failed to add system call to list: %m");
         }
@@ -1735,7 +1671,7 @@ static int dump_syscall_filters(int argc, char *argv[], void *userdata) {
         (void) pager_open(arg_pager_flags);
 
         if (strv_isempty(strv_skip(argv, 1))) {
-                _cleanup_(set_free_freep) Set *kernel = NULL;
+                _cleanup_set_free_ Set *kernel = NULL;
                 int i, k;
 
                 k = load_kernel_syscalls(&kernel);
@@ -2117,19 +2053,11 @@ static int service_watchdogs(int argc, char *argv[], void *userdata) {
 
         r = acquire_bus(&bus, NULL);
         if (r < 0)
-                return log_error_errno(r, "Failed to create bus connection: %m");
+                return bus_log_connect_error(r);
 
         if (argc == 1) {
                 /* get ServiceWatchdogs */
-                r = sd_bus_get_property_trivial(
-                                bus,
-                                "org.freedesktop.systemd1",
-                                "/org/freedesktop/systemd1",
-                                "org.freedesktop.systemd1.Manager",
-                                "ServiceWatchdogs",
-                                &error,
-                                'b',
-                                &b);
+                r = bus_get_property_trivial(bus, bus_systemd_mgr, "ServiceWatchdogs", &error, 'b', &b);
                 if (r < 0)
                         return log_error_errno(r, "Failed to get service-watchdog state: %s", bus_error_message(&error, r));
 
@@ -2141,15 +2069,7 @@ static int service_watchdogs(int argc, char *argv[], void *userdata) {
                 if (b < 0)
                         return log_error_errno(b, "Failed to parse service-watchdogs argument: %m");
 
-                r = sd_bus_set_property(
-                                bus,
-                                "org.freedesktop.systemd1",
-                                "/org/freedesktop/systemd1",
-                                "org.freedesktop.systemd1.Manager",
-                                "ServiceWatchdogs",
-                                &error,
-                                "b",
-                                b);
+                r = bus_set_property(bus, bus_systemd_mgr, "ServiceWatchdogs", &error, "b", b);
                 if (r < 0)
                         return log_error_errno(r, "Failed to set service-watchdog state: %s", bus_error_message(&error, r));
         }
@@ -2171,7 +2091,7 @@ static int do_security(int argc, char *argv[], void *userdata) {
 
         r = acquire_bus(&bus, NULL);
         if (r < 0)
-                return log_error_errno(r, "Failed to create bus connection: %m");
+                return bus_log_connect_error(r);
 
         (void) pager_open(arg_pager_flags);
 
@@ -2457,9 +2377,7 @@ static int run(int argc, char *argv[]) {
         setlocale(LC_ALL, "");
         setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */
 
-        log_show_color(true);
-        log_parse_environment();
-        log_open();
+        log_setup_cli();
 
         r = parse_argv(argc, argv);
         if (r <= 0)
index 54431f5d0fb0ed7d54819e38307e97f4dc0451d9..5f8212685b7781522fec1e7bf82ee2246d0c068c 100644 (file)
@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
+#include <sys/file.h>
 #include <unistd.h>
 
 #include "alloc-util.h"
@@ -187,3 +188,29 @@ int get_block_device_harder(const char *path, dev_t *ret) {
 
         return 1;
 }
+
+int lock_whole_block_device(dev_t devt, int operation) {
+        _cleanup_free_ char *whole_node = NULL;
+        _cleanup_close_ int lock_fd = -1;
+        dev_t whole_devt;
+        int r;
+
+        /* Let's get a BSD file lock on the whole block device, as per: https://systemd.io/BLOCK_DEVICE_LOCKING */
+
+        r = block_get_whole_disk(devt, &whole_devt);
+        if (r < 0)
+                return r;
+
+        r = device_path_make_major_minor(S_IFBLK, whole_devt, &whole_node);
+        if (r < 0)
+                return r;
+
+        lock_fd = open(whole_node, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
+        if (lock_fd < 0)
+                return -errno;
+
+        if (flock(lock_fd, operation) < 0)
+                return -errno;
+
+        return TAKE_FD(lock_fd);
+}
index 6d8a796568edf5dd843e38fbd003fd9a5d54f4ea..1e7588f71cab9f1d840fc7ba7e392da15c0ef333 100644 (file)
@@ -18,3 +18,5 @@ int block_get_originating(dev_t d, dev_t *ret);
 int get_block_device(const char *path, dev_t *dev);
 
 int get_block_device_harder(const char *path, dev_t *dev);
+
+int lock_whole_block_device(dev_t devt, int operation);
index 18a7217757936f3b4bce9893d9b878254f30fc10..71e1bc92eb7bfb4690e8f33f318e7cc1569a1d24 100644 (file)
@@ -160,6 +160,31 @@ int btrfs_subvol_make(const char *path) {
         return btrfs_subvol_make_fd(fd, subvolume);
 }
 
+int btrfs_subvol_make_fallback(const char *path, mode_t mode) {
+        mode_t old, combined;
+        int r;
+
+        assert(path);
+
+        /* Let's work like mkdir(), i.e. take the specified mode, and mask it with the current umask. */
+        old = umask(~mode);
+        combined = old | ~mode;
+        if (combined != ~mode)
+                umask(combined);
+        r = btrfs_subvol_make(path);
+        umask(old);
+
+        if (r >= 0)
+                return 1; /* subvol worked */
+        if (r != -ENOTTY)
+                return r;
+
+        if (mkdir(path, mode) < 0)
+                return -errno;
+
+        return 0; /* plain directory */
+}
+
 int btrfs_subvol_set_read_only_fd(int fd, bool b) {
         uint64_t flags, nflags;
         struct stat st;
@@ -175,11 +200,7 @@ int btrfs_subvol_set_read_only_fd(int fd, bool b) {
         if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0)
                 return -errno;
 
-        if (b)
-                nflags = flags | BTRFS_SUBVOL_RDONLY;
-        else
-                nflags = flags & ~BTRFS_SUBVOL_RDONLY;
-
+        nflags = UPDATE_FLAG(flags, BTRFS_SUBVOL_RDONLY, b);
         if (flags == nflags)
                 return 0;
 
@@ -298,7 +319,7 @@ int btrfs_get_block_device_fd(int fd, dev_t *dev) {
                         return -errno;
 
                 if (!S_ISBLK(st.st_mode))
-                        return -ENODEV;
+                        return -ENOTBLK;
 
                 if (major(st.st_rdev) == 0)
                         return -ENODEV;
@@ -906,9 +927,12 @@ static int qgroup_create_or_destroy(int fd, bool b, uint64_t qgroupid) {
         for (c = 0;; c++) {
                 if (ioctl(fd, BTRFS_IOC_QGROUP_CREATE, &args) < 0) {
 
-                        /* If quota is not enabled, we get EINVAL. Turn this into a recognizable error */
-                        if (errno == EINVAL)
-                                return -ENOPROTOOPT;
+                        /* On old kernels if quota is not enabled, we get EINVAL. On newer kernels we get
+                         * ENOTCONN. Let's always convert this to ENOTCONN to make this recognizable
+                         * everywhere the same way. */
+
+                        if (IN_SET(errno, EINVAL, ENOTCONN))
+                                return -ENOTCONN;
 
                         if (errno == EBUSY && c < 10) {
                                 (void) btrfs_quota_scan_wait(fd);
@@ -1128,7 +1152,6 @@ static int subvol_remove_children(int fd, const char *subvolume, uint64_t subvol
                 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
                         _cleanup_free_ char *p = NULL;
                         const struct btrfs_root_ref *ref;
-                        struct btrfs_ioctl_ino_lookup_args ino_args;
 
                         btrfs_ioctl_search_args_set(&args, sh);
 
@@ -1143,9 +1166,10 @@ static int subvol_remove_children(int fd, const char *subvolume, uint64_t subvol
                         if (!p)
                                 return -ENOMEM;
 
-                        zero(ino_args);
-                        ino_args.treeid = subvol_id;
-                        ino_args.objectid = htole64(ref->dirid);
+                        struct btrfs_ioctl_ino_lookup_args ino_args = {
+                                .treeid = subvol_id,
+                                .objectid = htole64(ref->dirid),
+                        };
 
                         if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0)
                                 return -errno;
@@ -1483,7 +1507,6 @@ static int subvol_snapshot_children(
 
                 FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) {
                         _cleanup_free_ char *p = NULL, *c = NULL, *np = NULL;
-                        struct btrfs_ioctl_ino_lookup_args ino_args;
                         const struct btrfs_root_ref *ref;
                         _cleanup_close_ int old_child_fd = -1, new_child_fd = -1;
 
@@ -1507,9 +1530,10 @@ static int subvol_snapshot_children(
                         if (!p)
                                 return -ENOMEM;
 
-                        zero(ino_args);
-                        ino_args.treeid = old_subvol_id;
-                        ino_args.objectid = htole64(ref->dirid);
+                        struct btrfs_ioctl_ino_lookup_args ino_args = {
+                                .treeid = old_subvol_id,
+                                .objectid = htole64(ref->dirid),
+                        };
 
                         if (ioctl(old_fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0)
                                 return -errno;
index b15667bf2f8336246fef994b29611019d3fc81d5..c1bbb42ca1bdb98765d7321cb0bdac797d8ad76a 100644 (file)
@@ -66,6 +66,8 @@ int btrfs_quota_scan_ongoing(int fd);
 int btrfs_subvol_make(const char *path);
 int btrfs_subvol_make_fd(int fd, const char *subvolume);
 
+int btrfs_subvol_make_fallback(const char *path, mode_t);
+
 int btrfs_subvol_snapshot_fd_full(int old_fd, const char *new_path, BtrfsSnapshotFlags flags, copy_progress_path_t progress_path, copy_progress_bytes_t progress_bytes, void *userdata);
 static inline int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags) {
         return btrfs_subvol_snapshot_fd_full(old_fd, new_path, flags, NULL, NULL, NULL);
index c47e912eb0ac4617de7bc2b03c148cfcda143224..d160af5bc7e6d7e6101c73d54853eb3c37270877 100644 (file)
 #define _LZ4_FEATURE_ "-LZ4"
 #endif
 
+#if HAVE_ZSTD
+#define _ZSTD_FEATURE_ "+ZSTD"
+#else
+#define _ZSTD_FEATURE_ "-ZSTD"
+#endif
+
 #if HAVE_SECCOMP
 #define _SECCOMP_FEATURE_ "+SECCOMP"
 #else
         _ACL_FEATURE_ " "                                               \
         _XZ_FEATURE_ " "                                                \
         _LZ4_FEATURE_ " "                                               \
+        _ZSTD_FEATURE_ " "                                              \
         _SECCOMP_FEATURE_ " "                                           \
         _BLKID_FEATURE_ " "                                             \
         _ELFUTILS_FEATURE_ " "                                          \
index af27d84ba4725a48f575b95bcce045e2767acf25..b483833947518e968b1b1b23ca5f34396a34fc1c 100644 (file)
@@ -18,7 +18,6 @@ static const struct capability_name* lookup_capability(register const char *str,
 #include "cap-to-name.h"
 
 const char *capability_to_name(int id) {
-
         if (id < 0)
                 return NULL;
 
@@ -57,12 +56,11 @@ int capability_list_length(void) {
 
 int capability_set_to_string_alloc(uint64_t set, char **s) {
         _cleanup_free_ char *str = NULL;
-        unsigned long i;
         size_t allocated = 0, n = 0;
 
         assert(s);
 
-        for (i = 0; i <= cap_last_cap(); i++)
+        for (unsigned i = 0; i <= cap_last_cap(); i++)
                 if (set & (UINT64_C(1) << i)) {
                         const char *p;
                         char buf[2 + 16 + 1];
@@ -70,7 +68,7 @@ int capability_set_to_string_alloc(uint64_t set, char **s) {
 
                         p = capability_to_name(i);
                         if (!p) {
-                                xsprintf(buf, "0x%lx", i);
+                                xsprintf(buf, "0x%x", i);
                                 p = buf;
                         }
 
@@ -95,11 +93,10 @@ int capability_set_to_string_alloc(uint64_t set, char **s) {
 
 int capability_set_from_string(const char *s, uint64_t *set) {
         uint64_t val = 0;
-        const char *p;
 
         assert(set);
 
-        for (p = s;;) {
+        for (const char *p = s;;) {
                 _cleanup_free_ char *word = NULL;
                 int r;
 
index ac96eabc032ba41b7f61abf748fc6143b97319d3..5a4d020f5225efbc1ad474d3dcddd22ed8063a6d 100644 (file)
@@ -31,8 +31,8 @@ int have_effective_cap(int value) {
         return fv == CAP_SET;
 }
 
-unsigned long cap_last_cap(void) {
-        static thread_local unsigned long saved;
+unsigned cap_last_cap(void) {
+        static thread_local unsigned saved;
         static thread_local bool valid = false;
         _cleanup_free_ char *content = NULL;
         unsigned long p = 0;
@@ -65,7 +65,7 @@ unsigned long cap_last_cap(void) {
         if (prctl(PR_CAPBSET_READ, p) < 0) {
 
                 /* Hmm, look downwards, until we find one that works */
-                for (p--; p > 0; p --)
+                for (p--; p > 0; p--)
                         if (prctl(PR_CAPBSET_READ, p) >= 0)
                                 break;
 
@@ -84,12 +84,10 @@ unsigned long cap_last_cap(void) {
 }
 
 int capability_update_inherited_set(cap_t caps, uint64_t set) {
-        unsigned long i;
-
         /* Add capabilities in the set to the inherited caps, drops capabilities not in the set.
          * Do not apply them yet. */
 
-        for (i = 0; i <= cap_last_cap(); i++) {
+        for (unsigned i = 0; i <= cap_last_cap(); i++) {
                 cap_flag_value_t flag = set & (UINT64_C(1) << i) ? CAP_SET : CAP_CLEAR;
                 cap_value_t v;
 
@@ -104,11 +102,10 @@ int capability_update_inherited_set(cap_t caps, uint64_t set) {
 
 int capability_ambient_set_apply(uint64_t set, bool also_inherit) {
         _cleanup_cap_free_ cap_t caps = NULL;
-        unsigned long i;
         int r;
 
         /* Remove capabilities requested in ambient set, but not in the bounding set */
-        for (i = 0; i <= cap_last_cap(); i++) {
+        for (unsigned i = 0; i <= cap_last_cap(); i++) {
                 if (set == 0)
                         break;
 
@@ -140,7 +137,7 @@ int capability_ambient_set_apply(uint64_t set, bool also_inherit) {
                         return -errno;
         }
 
-        for (i = 0; i <= cap_last_cap(); i++) {
+        for (unsigned i = 0; i <= cap_last_cap(); i++) {
 
                 if (set & (UINT64_C(1) << i)) {
 
@@ -167,7 +164,6 @@ int capability_ambient_set_apply(uint64_t set, bool also_inherit) {
 int capability_bounding_set_drop(uint64_t keep, bool right_now) {
         _cleanup_cap_free_ cap_t before_cap = NULL, after_cap = NULL;
         cap_flag_value_t fv;
-        unsigned long i;
         int r;
 
         /* If we are run as PID 1 we will lack CAP_SETPCAP by default
@@ -204,7 +200,7 @@ int capability_bounding_set_drop(uint64_t keep, bool right_now) {
         if (!after_cap)
                 return -errno;
 
-        for (i = 0; i <= cap_last_cap(); i++) {
+        for (unsigned i = 0; i <= cap_last_cap(); i++) {
                 cap_value_t v;
 
                 if ((keep & (UINT64_C(1) << i)))
@@ -390,7 +386,6 @@ bool ambient_capabilities_supported(void) {
 }
 
 bool capability_quintet_mangle(CapabilityQuintet *q) {
-        unsigned long i;
         uint64_t combined, drop = 0;
         bool ambient_supported;
 
@@ -402,7 +397,7 @@ bool capability_quintet_mangle(CapabilityQuintet *q) {
         if (ambient_supported)
                 combined |= q->ambient;
 
-        for (i = 0; i <= cap_last_cap(); i++) {
+        for (unsigned i = 0; i <= cap_last_cap(); i++) {
                 unsigned long bit = UINT64_C(1) << i;
                 if (!FLAGS_SET(combined, bit))
                         continue;
@@ -431,16 +426,15 @@ int capability_quintet_enforce(const CapabilityQuintet *q) {
         int r;
 
         if (q->ambient != (uint64_t) -1) {
-                unsigned long i;
                 bool changed = false;
 
                 c = cap_get_proc();
                 if (!c)
                         return -errno;
 
-                /* In order to raise the ambient caps set we first need to raise the matching inheritable + permitted
-                 * cap */
-                for (i = 0; i <= cap_last_cap(); i++) {
+                /* In order to raise the ambient caps set we first need to raise the matching
+                 * inheritable + permitted cap */
+                for (unsigned i = 0; i <= cap_last_cap(); i++) {
                         uint64_t m = UINT64_C(1) << i;
                         cap_value_t cv = (cap_value_t) i;
                         cap_flag_value_t old_value_inheritable, old_value_permitted;
@@ -475,7 +469,6 @@ int capability_quintet_enforce(const CapabilityQuintet *q) {
 
         if (q->inheritable != (uint64_t) -1 || q->permitted != (uint64_t) -1 || q->effective != (uint64_t) -1) {
                 bool changed = false;
-                unsigned long i;
 
                 if (!c) {
                         c = cap_get_proc();
@@ -483,7 +476,7 @@ int capability_quintet_enforce(const CapabilityQuintet *q) {
                                 return -errno;
                 }
 
-                for (i = 0; i <= cap_last_cap(); i++) {
+                for (unsigned i = 0; i <= cap_last_cap(); i++) {
                         uint64_t m = UINT64_C(1) << i;
                         cap_value_t cv = (cap_value_t) i;
 
index b5bce29ab53841d672a7d0871ff95fd917c5df4f..fcc59daedc0ab3a5c5809689e447aecc76953b1a 100644 (file)
@@ -12,7 +12,7 @@
 
 #define CAP_ALL (uint64_t) -1
 
-unsigned long cap_last_cap(void);
+unsigned cap_last_cap(void);
 int have_effective_cap(int value);
 int capability_bounding_set_drop(uint64_t keep, bool right_now);
 int capability_bounding_set_drop_usermode(uint64_t keep);
index 752133a8d9d1a71c28932bb3eacf4274b56d4b18..e94fcfad022c6fa9a56afe0709dc1137e900bb59 100644 (file)
@@ -37,6 +37,7 @@
 #include "strv.h"
 #include "unit-name.h"
 #include "user-util.h"
+#include "xattr-util.h"
 
 static int cg_enumerate_items(const char *controller, const char *path, FILE **_f, const char *item) {
         _cleanup_free_ char *fs = NULL;
@@ -148,6 +149,17 @@ bool cg_ns_supported(void) {
         return enabled;
 }
 
+bool cg_freezer_supported(void) {
+        static thread_local int supported = -1;
+
+        if (supported >= 0)
+                return supported;
+
+        supported = cg_all_unified() > 0 && access("/sys/fs/cgroup/init.scope/cgroup.freeze", F_OK) == 0;
+
+        return supported;
+}
+
 int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d) {
         _cleanup_free_ char *fs = NULL;
         int r;
@@ -605,6 +617,24 @@ int cg_get_xattr(const char *controller, const char *path, const char *name, voi
         return (int) n;
 }
 
+int cg_get_xattr_malloc(const char *controller, const char *path, const char *name, char **ret) {
+        _cleanup_free_ char *fs = NULL;
+        int r;
+
+        assert(path);
+        assert(name);
+
+        r = cg_get_path(controller, path, NULL, &fs);
+        if (r < 0)
+                return r;
+
+        r = getxattr_malloc(fs, name, ret, false);
+        if (r < 0)
+                return r;
+
+        return r;
+}
+
 int cg_remove_xattr(const char *controller, const char *path, const char *name) {
         _cleanup_free_ char *fs = NULL;
         int r;
@@ -878,9 +908,8 @@ int cg_is_empty_recursive(const char *controller, const char *path) {
         }
 }
 
-int cg_split_spec(const char *spec, char **controller, char **path) {
-        char *t = NULL, *u = NULL;
-        const char *e;
+int cg_split_spec(const char *spec, char **ret_controller, char **ret_path) {
+        _cleanup_free_ char *controller = NULL, *path = NULL;
 
         assert(spec);
 
@@ -888,76 +917,53 @@ int cg_split_spec(const char *spec, char **controller, char **path) {
                 if (!path_is_normalized(spec))
                         return -EINVAL;
 
-                if (path) {
-                        t = strdup(spec);
-                        if (!t)
+                if (ret_path) {
+                        path = strdup(spec);
+                        if (!path)
                                 return -ENOMEM;
 
-                        *path = path_simplify(t, false);
+                        path_simplify(path, false);
                 }
 
-                if (controller)
-                        *controller = NULL;
-
-                return 0;
-        }
-
-        e = strchr(spec, ':');
-        if (!e) {
-                if (!cg_controller_is_valid(spec))
-                        return -EINVAL;
+        } else {
+                const char *e;
 
-                if (controller) {
-                        t = strdup(spec);
-                        if (!t)
+                e = strchr(spec, ':');
+                if (e) {
+                        controller = strndup(spec, e-spec);
+                        if (!controller)
                                 return -ENOMEM;
+                        if (!cg_controller_is_valid(controller))
+                                return -EINVAL;
 
-                        *controller = t;
-                }
-
-                if (path)
-                        *path = NULL;
+                        if (!isempty(e + 1)) {
+                                path = strdup(e+1);
+                                if (!path)
+                                        return -ENOMEM;
 
-                return 0;
-        }
+                                if (!path_is_normalized(path) ||
+                                    !path_is_absolute(path))
+                                        return -EINVAL;
 
-        t = strndup(spec, e-spec);
-        if (!t)
-                return -ENOMEM;
-        if (!cg_controller_is_valid(t)) {
-                free(t);
-                return -EINVAL;
-        }
+                                path_simplify(path, false);
+                        }
 
-        if (isempty(e+1))
-                u = NULL;
-        else {
-                u = strdup(e+1);
-                if (!u) {
-                        free(t);
-                        return -ENOMEM;
-                }
+                } else {
+                        if (!cg_controller_is_valid(spec))
+                                return -EINVAL;
 
-                if (!path_is_normalized(u) ||
-                    !path_is_absolute(u)) {
-                        free(t);
-                        free(u);
-                        return -EINVAL;
+                        if (ret_controller) {
+                                controller = strdup(spec);
+                                if (!controller)
+                                        return -ENOMEM;
+                        }
                 }
-
-                path_simplify(u, false);
         }
 
-        if (controller)
-                *controller = t;
-        else
-                free(t);
-
-        if (path)
-                *path = u;
-        else
-                free(u);
-
+        if (ret_controller)
+                *ret_controller = TAKE_PTR(controller);
+        if (ret_path)
+                *ret_path = TAKE_PTR(path);
         return 0;
 }
 
@@ -1663,12 +1669,39 @@ int cg_get_attribute(const char *controller, const char *path, const char *attri
         return read_one_line_file(p, ret);
 }
 
-int cg_get_keyed_attribute(
+int cg_get_attribute_as_uint64(const char *controller, const char *path, const char *attribute, uint64_t *ret) {
+        _cleanup_free_ char *value = NULL;
+        uint64_t v;
+        int r;
+
+        assert(ret);
+
+        r = cg_get_attribute(controller, path, attribute, &value);
+        if (r == -ENOENT)
+                return -ENODATA;
+        if (r < 0)
+                return r;
+
+        if (streq(value, "max")) {
+                *ret = CGROUP_LIMIT_MAX;
+                return 0;
+        }
+
+        r = safe_atou64(value, &v);
+        if (r < 0)
+                return r;
+
+        *ret = v;
+        return 0;
+}
+
+int cg_get_keyed_attribute_full(
                 const char *controller,
                 const char *path,
                 const char *attribute,
                 char **keys,
-                char **ret_values) {
+                char **ret_values,
+                CGroupKeyMode mode) {
 
         _cleanup_free_ char *filename = NULL, *contents = NULL;
         const char *p;
@@ -1680,7 +1713,8 @@ int cg_get_keyed_attribute(
          * all keys to retrieve. The 'ret_values' parameter should be passed as string size with the same number of
          * entries as 'keys'. On success each entry will be set to the value of the matching key.
          *
-         * If the attribute file doesn't exist at all returns ENOENT, if any key is not found returns ENXIO. */
+         * If the attribute file doesn't exist at all returns ENOENT, if any key is not found returns ENXIO. If mode
+         * is set to GG_KEY_MODE_GRACEFUL we ignore missing keys and return those that were parsed successfully. */
 
         r = cg_get_path(controller, path, attribute, &filename);
         if (r < 0)
@@ -1728,6 +1762,9 @@ int cg_get_keyed_attribute(
                 p += strspn(p, NEWLINE);
         }
 
+        if (mode & CG_KEY_MODE_GRACEFUL)
+                goto done;
+
         r = -ENXIO;
 
 fail:
@@ -1738,6 +1775,9 @@ fail:
 
 done:
         memcpy(ret_values, v, sizeof(char*) * n);
+        if (mode & CG_KEY_MODE_GRACEFUL)
+                return n_done;
+
         return 0;
 }
 
index ad166190636e26a638331817a0258882211c9f08..2b88571bc1c7767d57cc9de9d0a417fe2e0cbb82 100644 (file)
@@ -170,7 +170,7 @@ typedef int (*cg_kill_log_func_t)(pid_t pid, int sig, void *userdata);
 int cg_kill(const char *controller, const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata);
 int cg_kill_recursive(const char *controller, const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata);
 
-int cg_split_spec(const char *spec, char **controller, char **path);
+int cg_split_spec(const char *spec, char **ret_controller, char **ret_path);
 int cg_mangle_path(const char *path, char **result);
 
 int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs);
@@ -180,14 +180,39 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path);
 
 int cg_rmdir(const char *controller, const char *path);
 
+typedef enum  {
+        CG_KEY_MODE_GRACEFUL = 1 << 0,
+} CGroupKeyMode;
+
 int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value);
 int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret);
-int cg_get_keyed_attribute(const char *controller, const char *path, const char *attribute, char **keys, char **values);
+int cg_get_keyed_attribute_full(const char *controller, const char *path, const char *attribute, char **keys, char **values, CGroupKeyMode mode);
+
+static inline int cg_get_keyed_attribute(
+                const char *controller,
+                const char *path,
+                const char *attribute,
+                char **keys,
+                char **ret_values) {
+        return cg_get_keyed_attribute_full(controller, path, attribute, keys, ret_values, 0);
+}
+
+static inline int cg_get_keyed_attribute_graceful(
+                const char *controller,
+                const char *path,
+                const char *attribute,
+                char **keys,
+                char **ret_values) {
+        return cg_get_keyed_attribute_full(controller, path, attribute, keys, ret_values, CG_KEY_MODE_GRACEFUL);
+}
+
+int cg_get_attribute_as_uint64(const char *controller, const char *path, const char *attribute, uint64_t *ret);
 
 int cg_set_access(const char *controller, const char *path, uid_t uid, gid_t gid);
 
 int cg_set_xattr(const char *controller, const char *path, const char *name, const void *value, size_t size, int flags);
 int cg_get_xattr(const char *controller, const char *path, const char *name, void *value, size_t size);
+int cg_get_xattr_malloc(const char *controller, const char *path, const char *name, char **ret);
 int cg_remove_xattr(const char *controller, const char *path, const char *name);
 
 int cg_install_release_agent(const char *controller, const char *agent);
@@ -235,6 +260,7 @@ int cg_mask_to_string(CGroupMask mask, char **ret);
 int cg_kernel_controllers(Set **controllers);
 
 bool cg_ns_supported(void);
+bool cg_freezer_supported(void);
 
 int cg_all_unified(void);
 int cg_hybrid_unified(void);
index 58eb62fb7a55d3592c788f59ffa128755ec567f0..eb19516c2acc2446243c8356ea9a6f2638f3b25a 100644 (file)
@@ -77,8 +77,10 @@ static int files_add(
                 /* Is this a masking entry? */
                 if ((flags & CONF_FILES_FILTER_MASKED))
                         if (null_or_empty(&st)) {
+                                assert(masked);
+
                                 /* Mark this one as masked */
-                                r = set_put_strdup(masked, de->d_name);
+                                r = set_put_strdup(&masked, de->d_name);
                                 if (r < 0)
                                         return r;
 
index 97d566c5b936b250c0bc7693f58d567de982c95d..b384010ae321d1a74ef09ae368977ac595a1e4d6 100644 (file)
@@ -15,6 +15,7 @@
 #include "copy.h"
 #include "dirent-util.h"
 #include "fd-util.h"
+#include "fileio.h"
 #include "fs-util.h"
 #include "io-util.h"
 #include "macro.h"
@@ -569,10 +570,9 @@ static int fd_copy_directory(
         if (fdf < 0)
                 return -errno;
 
-        d = fdopendir(fdf);
+        d = take_fdopendir(&fdf);
         if (!d)
                 return -errno;
-        fdf = -1;
 
         exists = false;
         if (copy_flags & COPY_MERGE_EMPTY) {
index 5ebe5b24832c0d58d806ffed3a6db7ca819dc63c..888ef236642e8a4ca696b19f18ad2898a0087fec 100644 (file)
@@ -7,7 +7,7 @@
 #include "device-nodes.h"
 #include "utf8.h"
 
-int whitelisted_char_for_devnode(char c, const char *white) {
+int allow_listed_char_for_devnode(char c, const char *white) {
 
         if ((c >= '0' && c <= '9') ||
             (c >= 'A' && c <= 'Z') ||
@@ -38,7 +38,7 @@ int encode_devnode_name(const char *str, char *str_enc, size_t len) {
                         j += seqlen;
                         i += (seqlen-1);
 
-                } else if (str[i] == '\\' || !whitelisted_char_for_devnode(str[i], NULL)) {
+                } else if (str[i] == '\\' || !allow_listed_char_for_devnode(str[i], NULL)) {
 
                         if (len-j < 4)
                                 return -EINVAL;
index 3840e6d307a78d5d0eff7aac06620ec01bee9a67..0dad8c9c681cd46c4096e750febe336b39251387 100644 (file)
@@ -8,7 +8,7 @@
 #include "stdio-util.h"
 
 int encode_devnode_name(const char *str, char *str_enc, size_t len);
-int whitelisted_char_for_devnode(char c, const char *additional);
+int allow_listed_char_for_devnode(char c, const char *additional);
 
 #define DEV_NUM_PATH_MAX                                                \
         (STRLEN("/dev/block/") + DECIMAL_STR_MAX(dev_t) + 1 + DECIMAL_STR_MAX(dev_t))
diff --git a/src/basic/dlfcn-util.h b/src/basic/dlfcn-util.h
new file mode 100644 (file)
index 0000000..d254afb
--- /dev/null
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <dlfcn.h>
+
+#include "macro.h"
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(void*, dlclose);
index e7edd17d0b0bd7482270e5174580664c2dbc30d3..007137cf01277aa5eb4ddbaad04b41e8e7eb869b 100644 (file)
@@ -210,9 +210,9 @@ int efi_set_variable(
         if (!p)
                 return -ENOMEM;
 
-        /* Newer efivarfs protects variables that are not in a whitelist with FS_IMMUTABLE_FL by default, to protect
-         * them for accidental removal and modification. We are not changing these variables accidentally however,
-         * hence let's unset the bit first. */
+        /* Newer efivarfs protects variables that are not in an allow list with FS_IMMUTABLE_FL by default,
+         * to protect them for accidental removal and modification. We are not changing these variables
+         * accidentally however, hence let's unset the bit first. */
 
         r = chattr_path(p, 0, FS_IMMUTABLE_FL, &saved_flags);
         if (r < 0 && r != -ENOENT)
index 65a6384eeb0457013ff6bb2306d51b49da4eec4a..0ca650f48f6d957291e9c0694c495d481f00ff52 100644 (file)
@@ -87,12 +87,16 @@ static inline bool ERRNO_IS_RESOURCE(int r) {
                       ENOMEM);
 }
 
-/* Three different errors for "operation/system call/ioctl not supported" */
+/* Seven different errors for "operation/system call/ioctl/socket feature not supported" */
 static inline bool ERRNO_IS_NOT_SUPPORTED(int r) {
         return IN_SET(abs(r),
                       EOPNOTSUPP,
                       ENOTTY,
-                      ENOSYS);
+                      ENOSYS,
+                      EAFNOSUPPORT,
+                      EPFNOSUPPORT,
+                      EPROTONOSUPPORT,
+                      ESOCKTNOSUPPORT);
 }
 
 /* Two different errors for access problems */
index c5c44d2e7d8e940b087edf28e10e7c69031745e8..116efa4119d44f61ffef2111f27cf611c023acf5 100644 (file)
@@ -518,22 +518,28 @@ char* shell_maybe_quote(const char *s, EscapeStyle style) {
                 return NULL;
 
         t = r;
-        if (style == ESCAPE_BACKSLASH)
+        switch (style) {
+        case ESCAPE_BACKSLASH:
+        case ESCAPE_BACKSLASH_ONELINE:
                 *(t++) = '"';
-        else if (style == ESCAPE_POSIX) {
+                break;
+        case ESCAPE_POSIX:
                 *(t++) = '$';
                 *(t++) = '\'';
-        } else
+                break;
+        default:
                 assert_not_reached("Bad EscapeStyle");
+        }
 
         t = mempcpy(t, s, p - s);
 
-        if (style == ESCAPE_BACKSLASH)
-                t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE, false);
+        if (IN_SET(style, ESCAPE_BACKSLASH, ESCAPE_BACKSLASH_ONELINE))
+                t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE,
+                                             style == ESCAPE_BACKSLASH_ONELINE);
         else
                 t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE_POSIX, true);
 
-        if (style == ESCAPE_BACKSLASH)
+        if (IN_SET(style, ESCAPE_BACKSLASH, ESCAPE_BACKSLASH_ONELINE))
                 *(t++) = '"';
         else
                 *(t++) = '\'';
index b8eb137c3d398bf4e6adf48c33d3f7721eaac7d4..0b00b116edc82a5b6105bd881cbba94fb26cd3da 100644 (file)
@@ -34,8 +34,13 @@ typedef enum UnescapeFlags {
 } UnescapeFlags;
 
 typedef enum EscapeStyle {
-        ESCAPE_BACKSLASH = 1,
-        ESCAPE_POSIX     = 2,
+        ESCAPE_BACKSLASH         = 1,  /* Add shell quotes ("") so the shell will consider this a single
+                                          argument, possibly multiline. Tabs and newlines are not escaped. */
+        ESCAPE_BACKSLASH_ONELINE = 2,  /* Similar to ESCAPE_BACKSLASH, but always produces a single-line
+                                          string instead. Shell escape sequences are produced for tabs and
+                                          newlines. */
+        ESCAPE_POSIX             = 3,  /* Similar to ESCAPE_BACKSLASH_ONELINE, but uses POSIX shell escape
+                                        * syntax (a string enclosed in $'') instead of plain quotes. */
 } EscapeStyle;
 
 char *cescape(const char *s);
index de3f238d33556948dbf3bd4f6bb94c136e12a2d7..75a6282ed08d9e746b8612875031a89090d57f85 100644 (file)
 #include "path-util.h"
 #include "process-util.h"
 #include "socket-util.h"
+#include "stat-util.h"
 #include "stdio-util.h"
-#include "util.h"
 #include "tmpfile-util.h"
+#include "util.h"
 
 /* The maximum number of iterations in the loop to close descriptors in the fallback case
  * when /proc/self/fd/ is inaccessible. */
@@ -147,11 +148,7 @@ int fd_nonblock(int fd, bool nonblock) {
         if (flags < 0)
                 return -errno;
 
-        if (nonblock)
-                nflags = flags | O_NONBLOCK;
-        else
-                nflags = flags & ~O_NONBLOCK;
-
+        nflags = UPDATE_FLAG(flags, O_NONBLOCK, nonblock);
         if (nflags == flags)
                 return 0;
 
@@ -170,11 +167,7 @@ int fd_cloexec(int fd, bool cloexec) {
         if (flags < 0)
                 return -errno;
 
-        if (cloexec)
-                nflags = flags | FD_CLOEXEC;
-        else
-                nflags = flags & ~FD_CLOEXEC;
-
+        nflags = UPDATE_FLAG(flags, FD_CLOEXEC, cloexec);
         if (nflags == flags)
                 return 0;
 
@@ -950,8 +943,15 @@ int fd_reopen(int fd, int flags) {
 
         xsprintf(procfs_path, "/proc/self/fd/%i", fd);
         new_fd = open(procfs_path, flags);
-        if (new_fd < 0)
-                return -errno;
+        if (new_fd < 0) {
+                if (errno != ENOENT)
+                        return -errno;
+
+                if (proc_mounted() == 0)
+                        return -ENOSYS; /* if we have no /proc/, the concept is not implementable */
+
+                return -ENOENT;
+        }
 
         return new_fd;
 }
index de5bd78b5944c7c66ddac68294788011b033aa7f..c3d55d209ac7ede12ceaa70b943023fdb73ddaa0 100644 (file)
@@ -22,6 +22,7 @@
 #include "mkdir.h"
 #include "parse-util.h"
 #include "path-util.h"
+#include "socket-util.h"
 #include "stdio-util.h"
 #include "string-util.h"
 #include "tmpfile-util.h"
@@ -54,6 +55,44 @@ int fdopen_unlocked(int fd, const char *options, FILE **ret) {
         return 0;
 }
 
+int take_fdopen_unlocked(int *fd, const char *options, FILE **ret) {
+        int     r;
+
+        assert(fd);
+
+        r = fdopen_unlocked(*fd, options, ret);
+        if (r < 0)
+                return r;
+
+        *fd = -1;
+
+        return 0;
+}
+
+FILE* take_fdopen(int *fd, const char *options) {
+        assert(fd);
+
+        FILE *f = fdopen(*fd, options);
+        if (!f)
+                return NULL;
+
+        *fd = -1;
+
+        return f;
+}
+
+DIR* take_fdopendir(int *dfd) {
+        assert(dfd);
+
+        DIR *d = fdopendir(*dfd);
+        if (!d)
+                return NULL;
+
+        *dfd = -1;
+
+        return d;
+}
+
 FILE* open_memstream_unlocked(char **ptr, size_t *sizeloc) {
         FILE *f = open_memstream(ptr, sizeloc);
         if (!f)
@@ -164,6 +203,13 @@ static int write_string_file_atomic(
                 goto fail;
         }
 
+        if (FLAGS_SET(flags, WRITE_STRING_FILE_SYNC)) {
+                /* Sync the rename, too */
+                r = fsync_directory_of_file(fileno(f));
+                if (r < 0)
+                        return r;
+        }
+
         return 0;
 
 fail:
@@ -437,13 +483,12 @@ int read_full_stream_full(
         assert(f);
         assert(ret_contents);
         assert(!FLAGS_SET(flags, READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_UNHEX));
-        assert(!(flags & (READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_UNHEX)) || ret_size);
 
         n_next = LINE_MAX; /* Start size */
 
         fd = fileno(f);
-        if (fd >= 0) { /* If the FILE* object is backed by an fd (as opposed to memory or such, see fmemopen(), let's
-                        * optimize our buffering) */
+        if (fd >= 0) { /* If the FILE* object is backed by an fd (as opposed to memory or such, see fmemopen()), let's
+                        * optimize our buffering */
 
                 if (fstat(fd, &st) < 0)
                         return -errno;
@@ -460,7 +505,7 @@ int read_full_stream_full(
                         if (st.st_size > 0)
                                 n_next = st.st_size + 1;
 
-                        if (flags & READ_FULL_FILE_SECURE)
+                        if (flags & READ_FULL_FILE_WARN_WORLD_READABLE)
                                 (void) warn_file_is_world_accessible(filename, &st, NULL, 0);
                 }
         }
@@ -490,21 +535,18 @@ int read_full_stream_full(
 
                 errno = 0;
                 k = fread(buf + l, 1, n - l, f);
-                if (k > 0)
-                        l += k;
+
+                assert(k <= n - l);
+                l += k;
 
                 if (ferror(f)) {
                         r = errno_or_else(EIO);
                         goto finalize;
                 }
-
                 if (feof(f))
                         break;
 
-                /* We aren't expecting fread() to return a short read outside
-                 * of (error && eof), assert buffer is full and enlarge buffer.
-                 */
-                assert(l == n);
+                assert(k > 0); /* we can't have read zero bytes because that would have been EOF */
 
                 /* Safety check */
                 if (n >= READ_FULL_BYTES_MAX) {
@@ -516,12 +558,21 @@ int read_full_stream_full(
         }
 
         if (flags & (READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_UNHEX)) {
+                _cleanup_free_ void *decoded = NULL;
+                size_t decoded_size;
+
                 buf[l++] = 0;
                 if (flags & READ_FULL_FILE_UNBASE64)
-                        r = unbase64mem_full(buf, l, flags & READ_FULL_FILE_SECURE, (void **) ret_contents, ret_size);
+                        r = unbase64mem_full(buf, l, flags & READ_FULL_FILE_SECURE, &decoded, &decoded_size);
                 else
-                        r = unhexmem_full(buf, l, flags & READ_FULL_FILE_SECURE, (void **) ret_contents, ret_size);
-                goto finalize;
+                        r = unhexmem_full(buf, l, flags & READ_FULL_FILE_SECURE, &decoded, &decoded_size);
+                if (r < 0)
+                        goto finalize;
+
+                if (flags & READ_FULL_FILE_SECURE)
+                        explicit_bzero_safe(buf, n);
+                free_and_replace(buf, decoded);
+                n = l = decoded_size;
         }
 
         if (!ret_size) {
@@ -558,8 +609,54 @@ int read_full_file_full(int dir_fd, const char *filename, ReadFullFileFlags flag
         assert(contents);
 
         r = xfopenat(dir_fd, filename, "re", 0, &f);
-        if (r < 0)
-                return r;
+        if (r < 0) {
+                _cleanup_close_ int dfd = -1, sk = -1;
+                union sockaddr_union sa;
+
+                /* ENXIO is what Linux returns if we open a node that is an AF_UNIX socket */
+                if (r != -ENXIO)
+                        return r;
+
+                /* If this is enabled, let's try to connect to it */
+                if (!FLAGS_SET(flags, READ_FULL_FILE_CONNECT_SOCKET))
+                        return -ENXIO;
+
+                if (dir_fd == AT_FDCWD)
+                        r = sockaddr_un_set_path(&sa.un, filename);
+                else {
+                        char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
+
+                        /* If we shall operate relative to some directory, then let's use O_PATH first to
+                         * open the socket inode, and then connect to it via /proc/self/fd/. We have to do
+                         * this since there's not connectat() that takes a directory fd as first arg. */
+
+                        dfd = openat(dir_fd, filename, O_PATH|O_CLOEXEC);
+                        if (dfd < 0)
+                                return -errno;
+
+                        xsprintf(procfs_path, "/proc/self/fd/%i", dfd);
+                        r = sockaddr_un_set_path(&sa.un, procfs_path);
+                }
+                if (r < 0)
+                        return r;
+
+                sk = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
+                if (sk < 0)
+                        return -errno;
+
+                if (connect(sk, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0)
+                        return errno == ENOTSOCK ? -ENXIO : -errno; /* propagate original error if this is
+                                                                     * not a socket after all */
+
+                if (shutdown(sk, SHUT_WR) < 0)
+                        return -errno;
+
+                f = fdopen(sk, "r");
+                if (!f)
+                        return -errno;
+
+                TAKE_FD(sk);
+        }
 
         (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
 
@@ -1142,3 +1239,25 @@ int warn_file_is_world_accessible(const char *filename, struct stat *st, const c
                             filename, st->st_mode & 07777);
         return 0;
 }
+
+int sync_rights(int from, int to) {
+        struct stat st;
+
+        if (fstat(from, &st) < 0)
+                return -errno;
+
+        return fchmod_and_chown(to, st.st_mode & 07777, st.st_uid, st.st_gid);
+}
+
+int rename_and_apply_smack_floor_label(const char *from, const char *to) {
+        int r = 0;
+        if (rename(from, to) < 0)
+                return -errno;
+
+#ifdef SMACK_RUN_LABEL
+        r = mac_smack_apply(to, SMACK_ATTR_ACCESS, SMACK_FLOOR_LABEL);
+        if (r < 0)
+                return r;
+#endif
+        return r;
+}
index d140bfd99ea6d5cf422acef09da8f953c675a6ae..7d58fa7cfc24d51033edb97a3f7a4fec6d6aa5f1 100644 (file)
@@ -32,13 +32,18 @@ typedef enum {
 } WriteStringFileFlags;
 
 typedef enum {
-        READ_FULL_FILE_SECURE   = 1 << 0,
-        READ_FULL_FILE_UNBASE64 = 1 << 1,
-        READ_FULL_FILE_UNHEX    = 1 << 2,
+        READ_FULL_FILE_SECURE              = 1 << 0, /* erase any buffers we employ internally, after use */
+        READ_FULL_FILE_UNBASE64            = 1 << 1, /* base64 decode what we read */
+        READ_FULL_FILE_UNHEX               = 1 << 2, /* hex decode what we read */
+        READ_FULL_FILE_WARN_WORLD_READABLE = 1 << 3, /* if regular file, log at LOG_WARNING level if access mode above 0700 */
+        READ_FULL_FILE_CONNECT_SOCKET      = 1 << 4, /* if socket inode, connect to it and read off it */
 } ReadFullFileFlags;
 
 int fopen_unlocked(const char *path, const char *options, FILE **ret);
 int fdopen_unlocked(int fd, const char *options, FILE **ret);
+int take_fdopen_unlocked(int *fd, const char *options, FILE **ret);
+FILE* take_fdopen(int *fd, const char *options);
+DIR* take_fdopendir(int *dfd);
 FILE* open_memstream_unlocked(char **ptr, size_t *sizeloc);
 FILE* fmemopen_unlocked(void *buf, size_t size, const char *mode);
 
@@ -103,3 +108,7 @@ static inline int read_nul_string(FILE *f, size_t limit, char **ret) {
 int safe_fgetc(FILE *f, char *ret);
 
 int warn_file_is_world_accessible(const char *filename, struct stat *st, const char *unit, unsigned line);
+
+int sync_rights(int from, int to);
+
+int rename_and_apply_smack_floor_label(const char *temp_path, const char *dest_path);
index ef3b5a51842f31d307b315585ce1cbd27ce50d6f..34a2260783bb1c94dff6b65710d9673b1084f114 100644 (file)
@@ -8,8 +8,10 @@
 #include <unistd.h>
 
 #include "alloc-util.h"
+#include "blockdev-util.h"
 #include "dirent-util.h"
 #include "fd-util.h"
+#include "fileio.h"
 #include "fs-util.h"
 #include "locale-util.h"
 #include "log.h"
@@ -21,6 +23,7 @@
 #include "parse-util.h"
 #include "path-util.h"
 #include "process-util.h"
+#include "random-util.h"
 #include "stat-util.h"
 #include "stdio-util.h"
 #include "string-util.h"
@@ -337,34 +340,51 @@ int fchmod_opath(int fd, mode_t m) {
          * fchownat() does. */
 
         xsprintf(procfs_path, "/proc/self/fd/%i", fd);
-        if (chmod(procfs_path, m) < 0)
-                return -errno;
+        if (chmod(procfs_path, m) < 0) {
+                if (errno != ENOENT)
+                        return -errno;
+
+                if (proc_mounted() == 0)
+                        return -ENOSYS; /* if we have no /proc/, the concept is not implementable */
+
+                return -ENOENT;
+        }
 
         return 0;
 }
 
-int fd_warn_permissions(const char *path, int fd) {
-        struct stat st;
-
-        if (fstat(fd, &st) < 0)
-                return -errno;
+int stat_warn_permissions(const char *path, const struct stat *st) {
+        assert(path);
+        assert(st);
 
         /* Don't complain if we are reading something that is not a file, for example /dev/null */
-        if (!S_ISREG(st.st_mode))
+        if (!S_ISREG(st->st_mode))
                 return 0;
 
-        if (st.st_mode & 0111)
+        if (st->st_mode & 0111)
                 log_warning("Configuration file %s is marked executable. Please remove executable permission bits. Proceeding anyway.", path);
 
-        if (st.st_mode & 0002)
+        if (st->st_mode & 0002)
                 log_warning("Configuration file %s is marked world-writable. Please remove world writability permission bits. Proceeding anyway.", path);
 
-        if (getpid_cached() == 1 && (st.st_mode & 0044) != 0044)
+        if (getpid_cached() == 1 && (st->st_mode & 0044) != 0044)
                 log_warning("Configuration file %s is marked world-inaccessible. This has no effect as configuration data is accessible via APIs without restrictions. Proceeding anyway.", path);
 
         return 0;
 }
 
+int fd_warn_permissions(const char *path, int fd) {
+        struct stat st;
+
+        assert(path);
+        assert(fd >= 0);
+
+        if (fstat(fd, &st) < 0)
+                return -errno;
+
+        return stat_warn_permissions(path, &st);
+}
+
 int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) {
         char fdpath[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
         _cleanup_close_ int fd = -1;
@@ -1294,11 +1314,13 @@ void unlink_tempfilep(char (*p)[]) {
                 (void) unlink_noerrno(*p);
 }
 
-int unlinkat_deallocate(int fd, const char *name, int flags) {
+int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags) {
         _cleanup_close_ int truncate_fd = -1;
         struct stat st;
         off_t l, bs;
 
+        assert((flags & ~(UNLINK_REMOVEDIR|UNLINK_ERASE)) == 0);
+
         /* Operates like unlinkat() but also deallocates the file contents if it is a regular file and there's no other
          * link to it. This is useful to ensure that other processes that might have the file open for reading won't be
          * able to keep the data pinned on disk forever. This call is particular useful whenever we execute clean-up
@@ -1315,7 +1337,7 @@ int unlinkat_deallocate(int fd, const char *name, int flags) {
          * Note that we attempt deallocation, but failure to succeed with that is not considered fatal, as long as the
          * primary job – to delete the file – is accomplished. */
 
-        if ((flags & AT_REMOVEDIR) == 0) {
+        if (!FLAGS_SET(flags, UNLINK_REMOVEDIR)) {
                 truncate_fd = openat(fd, name, O_WRONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW|O_NONBLOCK);
                 if (truncate_fd < 0) {
 
@@ -1331,7 +1353,7 @@ int unlinkat_deallocate(int fd, const char *name, int flags) {
                 }
         }
 
-        if (unlinkat(fd, name, flags) < 0)
+        if (unlinkat(fd, name, FLAGS_SET(flags, UNLINK_REMOVEDIR) ? AT_REMOVEDIR : 0) < 0)
                 return -errno;
 
         if (truncate_fd < 0) /* Don't have a file handle, can't do more ☹️ */
@@ -1342,7 +1364,45 @@ int unlinkat_deallocate(int fd, const char *name, int flags) {
                 return 0;
         }
 
-        if (!S_ISREG(st.st_mode) || st.st_blocks == 0 || st.st_nlink > 0)
+        if (!S_ISREG(st.st_mode))
+                return 0;
+
+        if (FLAGS_SET(flags, UNLINK_ERASE) && st.st_size > 0 && st.st_nlink == 0) {
+                uint64_t left = st.st_size;
+                char buffer[64 * 1024];
+
+                /* If erasing is requested, let's overwrite the file with random data once before deleting
+                 * it. This isn't going to give you shred(1) semantics, but hopefully should be good enough
+                 * for stuff backed by tmpfs at least.
+                 *
+                 * Note that we only erase like this if the link count of the file is zero. If it is higher it
+                 * is still linked by someone else and we'll leave it to them to remove it securely
+                 * eventually! */
+
+                random_bytes(buffer, sizeof(buffer));
+
+                while (left > 0) {
+                        ssize_t n;
+
+                        n = write(truncate_fd, buffer, MIN(sizeof(buffer), left));
+                        if (n < 0) {
+                                log_debug_errno(errno, "Failed to erase data in file '%s', ignoring.", name);
+                                break;
+                        }
+
+                        assert(left >= (size_t) n);
+                        left -= n;
+                }
+
+                /* Let's refresh metadata */
+                if (fstat(truncate_fd, &st) < 0) {
+                        log_debug_errno(errno, "Failed to stat file '%s' for deallocation, ignoring: %m", name);
+                        return 0;
+                }
+        }
+
+        /* Don't dallocate if there's nothing to deallocate or if the file is linked elsewhere */
+        if (st.st_blocks == 0 || st.st_nlink > 0)
                 return 0;
 
         /* If this is a regular file, it actually took up space on disk and there are no other links it's time to
@@ -1481,3 +1541,88 @@ int open_parent(const char *path, int flags, mode_t mode) {
 
         return fd;
 }
+
+static int blockdev_is_encrypted(const char *sysfs_path, unsigned depth_left) {
+        _cleanup_free_ char *p = NULL, *uuids = NULL;
+        _cleanup_closedir_ DIR *d = NULL;
+        int r, found_encrypted = false;
+
+        assert(sysfs_path);
+
+        if (depth_left == 0)
+                return -EINVAL;
+
+        p = path_join(sysfs_path, "dm/uuid");
+        if (!p)
+                return -ENOMEM;
+
+        r = read_one_line_file(p, &uuids);
+        if (r != -ENOENT) {
+                if (r < 0)
+                        return r;
+
+                /* The DM device's uuid attribute is prefixed with "CRYPT-" if this is a dm-crypt device. */
+                if (startswith(uuids, "CRYPT-"))
+                        return true;
+        }
+
+        /* Not a dm-crypt device itself. But maybe it is on top of one? Follow the links in the "slaves/"
+         * subdir. */
+
+        p = mfree(p);
+        p = path_join(sysfs_path, "slaves");
+        if (!p)
+                return -ENOMEM;
+
+        d = opendir(p);
+        if (!d) {
+                if (errno == ENOENT) /* Doesn't have underlying devices */
+                        return false;
+
+                return -errno;
+        }
+
+        for (;;) {
+                _cleanup_free_ char *q = NULL;
+                struct dirent *de;
+
+                errno = 0;
+                de = readdir_no_dot(d);
+                if (!de) {
+                        if (errno != 0)
+                                return -errno;
+
+                        break; /* No more underlying devices */
+                }
+
+                q = path_join(p, de->d_name);
+                if (!q)
+                        return -ENOMEM;
+
+                r = blockdev_is_encrypted(q, depth_left - 1);
+                if (r < 0)
+                        return r;
+                if (r == 0) /* we found one that is not encrypted? then propagate that immediately */
+                        return false;
+
+                found_encrypted = true;
+        }
+
+        return found_encrypted;
+}
+
+int path_is_encrypted(const char *path) {
+        char p[SYS_BLOCK_PATH_MAX(NULL)];
+        dev_t devt;
+        int r;
+
+        r = get_block_device(path, &devt);
+        if (r < 0)
+                return r;
+        if (r == 0) /* doesn't have a block device */
+                return false;
+
+        xsprintf_sys_block_path(p, NULL, devt);
+
+        return blockdev_is_encrypted(p, 10 /* safety net: maximum recursion depth */);
+}
index 6b9ade2ec15e3c21e90511ccf86458b8b7473df6..b184570f9f4adb5bc0aa9f3aec31320145cdb992 100644 (file)
@@ -40,6 +40,7 @@ int fchmod_umask(int fd, mode_t mode);
 int fchmod_opath(int fd, mode_t m);
 
 int fd_warn_permissions(const char *path, int fd);
+int stat_warn_permissions(const char *path, const struct stat *st);
 
 #define laccess(path, mode) faccessat(AT_FDCWD, (path), (mode), AT_SYMLINK_NOFOLLOW)
 
@@ -82,7 +83,7 @@ enum {
         CHASE_SAFE        = 1 << 3, /* Return EPERM if we ever traverse from unprivileged to privileged files or directories */
         CHASE_TRAIL_SLASH = 1 << 4, /* Any trailing slash will be preserved */
         CHASE_STEP        = 1 << 5, /* Just execute a single step of the normalization */
-        CHASE_NOFOLLOW    = 1 << 6, /* Do not follow the path's right-most compontent. With ret_fd, when the path's
+        CHASE_NOFOLLOW    = 1 << 6, /* Do not follow the path's right-most component. With ret_fd, when the path's
                                      * right-most component refers to symlink, return O_PATH fd of the symlink. */
         CHASE_WARN        = 1 << 7, /* Emit an appropriate warning when an error is encountered */
 };
@@ -113,7 +114,13 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(char*, unlink_and_free);
 int access_fd(int fd, int mode);
 
 void unlink_tempfilep(char (*p)[]);
-int unlinkat_deallocate(int fd, const char *name, int flags);
+
+typedef enum UnlinkDeallocateFlags {
+        UNLINK_REMOVEDIR = 1 << 0,
+        UNLINK_ERASE     = 1 << 1,
+} UnlinkDeallocateFlags;
+
+int unlinkat_deallocate(int fd, const char *name, UnlinkDeallocateFlags flags);
 
 int fsync_directory_of_file(int fd);
 int fsync_full(int fd);
@@ -122,3 +129,5 @@ int fsync_path_at(int at_fd, const char *path);
 int syncfs_path(int atfd, const char *path);
 
 int open_parent(const char *path, int flags, mode_t mode);
+
+int path_is_encrypted(const char *path);
index fce339512c6abe547d1133aaeb0658a7c25e14ae..cf279e5cbefc5673d66ca7369084d4682a12ff64 100644 (file)
@@ -10,6 +10,8 @@ void string_hash_func(const char *p, struct siphash *state) {
 }
 
 DEFINE_HASH_OPS(string_hash_ops, char, string_hash_func, string_compare_func);
+DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(string_hash_ops_free,
+                                    char, string_hash_func, string_compare_func, free);
 DEFINE_HASH_OPS_FULL(string_hash_ops_free_free,
                      char, string_hash_func, string_compare_func, free,
                      char, free);
@@ -53,6 +55,8 @@ void path_hash_func(const char *q, struct siphash *state) {
 }
 
 DEFINE_HASH_OPS(path_hash_ops, char, path_hash_func, path_compare);
+DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(path_hash_ops_free,
+                                    char, path_hash_func, path_compare, free);
 
 void trivial_hash_func(const void *p, struct siphash *state) {
         siphash24_compress(&p, sizeof(p), state);
index 7bb5d1cd0217ff6c82730c1d9269d710f239be70..005d1b21d21ea0806ccda6f66ccaf104edc2369d 100644 (file)
@@ -76,10 +76,12 @@ struct hash_ops {
 void string_hash_func(const char *p, struct siphash *state);
 #define string_compare_func strcmp
 extern const struct hash_ops string_hash_ops;
+extern const struct hash_ops string_hash_ops_free;
 extern const struct hash_ops string_hash_ops_free_free;
 
 void path_hash_func(const char *p, struct siphash *state);
 extern const struct hash_ops path_hash_ops;
+extern const struct hash_ops path_hash_ops_free;
 
 /* This will compare the passed pointers directly, and will not dereference them. This is hence not useful for strings
  * or suchlike. */
index 4853514c96a6e66a608d03b3ad76271c456d1ac9..67c439123078d154afd49e2e7b34e926b349c20e 100644 (file)
@@ -145,12 +145,7 @@ struct hashmap_debug_info {
 /* Tracks all existing hashmaps. Get at it from gdb. See sd_dump_hashmaps.py */
 static LIST_HEAD(struct hashmap_debug_info, hashmap_debug_list);
 static pthread_mutex_t hashmap_debug_list_mutex = PTHREAD_MUTEX_INITIALIZER;
-
-#define HASHMAP_DEBUG_FIELDS struct hashmap_debug_info debug;
-
-#else /* !ENABLE_DEBUG_HASHMAP */
-#define HASHMAP_DEBUG_FIELDS
-#endif /* ENABLE_DEBUG_HASHMAP */
+#endif
 
 enum HashmapType {
         HASHMAP_TYPE_PLAIN,
@@ -212,7 +207,10 @@ struct HashmapBase {
         bool from_pool:1;            /* whether was allocated from mempool */
         bool dirty:1;                /* whether dirtied since last iterated_cache_get() */
         bool cached:1;               /* whether this hashmap is being cached */
-        HASHMAP_DEBUG_FIELDS         /* optional hashmap_debug_info */
+
+#if ENABLE_DEBUG_HASHMAP
+        struct hashmap_debug_info debug;
+#endif
 };
 
 /* Specific hash types
@@ -254,7 +252,7 @@ struct hashmap_type_info {
         unsigned n_direct_buckets;
 };
 
-static const struct hashmap_type_info hashmap_type_info[_HASHMAP_TYPE_MAX] = {
+static _used_ const struct hashmap_type_info hashmap_type_info[_HASHMAP_TYPE_MAX] = {
         [HASHMAP_TYPE_PLAIN] = {
                 .head_size        = sizeof(Hashmap),
                 .entry_size       = sizeof(struct plain_hashmap_entry),
@@ -707,7 +705,7 @@ static unsigned hashmap_iterate_entry(HashmapBase *h, Iterator *i) {
                                                : hashmap_iterate_in_internal_order(h, i);
 }
 
-bool internal_hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key) {
+bool _hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key) {
         struct hashmap_base_entry *e;
         void *data;
         unsigned idx;
@@ -733,7 +731,7 @@ bool internal_hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const v
 }
 
 bool set_iterate(const Set *s, Iterator *i, void **value) {
-        return internal_hashmap_iterate(HASHMAP_BASE((Set*) s), i, value, NULL);
+        return _hashmap_iterate(HASHMAP_BASE((Set*) s), i, value, NULL);
 }
 
 #define HASHMAP_FOREACH_IDX(idx, h, i) \
@@ -741,7 +739,7 @@ bool set_iterate(const Set *s, Iterator *i, void **value) {
              (idx != IDX_NIL); \
              (idx) = hashmap_iterate_entry((h), &(i)))
 
-IteratedCache *internal_hashmap_iterated_cache_new(HashmapBase *h) {
+IteratedCache *_hashmap_iterated_cache_new(HashmapBase *h) {
         IteratedCache *cache;
 
         assert(h);
@@ -770,7 +768,7 @@ static void reset_direct_storage(HashmapBase *h) {
         memset(p, DIB_RAW_INIT, sizeof(dib_raw_t) * hi->n_direct_buckets);
 }
 
-static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enum HashmapType type HASHMAP_DEBUG_PARAMS) {
+static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enum HashmapType type  HASHMAP_DEBUG_PARAMS) {
         HashmapBase *h;
         const struct hashmap_type_info *hi = &hashmap_type_info[type];
         bool up;
@@ -809,20 +807,20 @@ static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enu
         return h;
 }
 
-Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS) {
-        return (Hashmap*)        hashmap_base_new(hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS);
+Hashmap *_hashmap_new(const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS) {
+        return (Hashmap*)        hashmap_base_new(hash_ops, HASHMAP_TYPE_PLAIN  HASHMAP_DEBUG_PASS_ARGS);
 }
 
-OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS) {
-        return (OrderedHashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS);
+OrderedHashmap *_ordered_hashmap_new(const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS) {
+        return (OrderedHashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_ORDERED  HASHMAP_DEBUG_PASS_ARGS);
 }
 
-Set *internal_set_new(const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS) {
-        return (Set*)            hashmap_base_new(hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS);
+Set *_set_new(const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS) {
+        return (Set*)            hashmap_base_new(hash_ops, HASHMAP_TYPE_SET  HASHMAP_DEBUG_PASS_ARGS);
 }
 
 static int hashmap_base_ensure_allocated(HashmapBase **h, const struct hash_ops *hash_ops,
-                                         enum HashmapType type HASHMAP_DEBUG_PARAMS) {
+                                         enum HashmapType type  HASHMAP_DEBUG_PARAMS) {
         HashmapBase *q;
 
         assert(h);
@@ -830,24 +828,24 @@ static int hashmap_base_ensure_allocated(HashmapBase **h, const struct hash_ops
         if (*h)
                 return 0;
 
-        q = hashmap_base_new(hash_ops, type HASHMAP_DEBUG_PASS_ARGS);
+        q = hashmap_base_new(hash_ops, type  HASHMAP_DEBUG_PASS_ARGS);
         if (!q)
                 return -ENOMEM;
 
         *h = q;
-        return 0;
+        return 1;
 }
 
-int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS) {
-        return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS);
+int _hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS) {
+        return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_PLAIN  HASHMAP_DEBUG_PASS_ARGS);
 }
 
-int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS) {
-        return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS);
+int _ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS) {
+        return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_ORDERED  HASHMAP_DEBUG_PASS_ARGS);
 }
 
-int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS) {
-        return hashmap_base_ensure_allocated((HashmapBase**)s, hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS);
+int _set_ensure_allocated(Set **s, const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS) {
+        return hashmap_base_ensure_allocated((HashmapBase**)s, hash_ops, HASHMAP_TYPE_SET  HASHMAP_DEBUG_PASS_ARGS);
 }
 
 static void hashmap_free_no_clear(HashmapBase *h) {
@@ -868,16 +866,16 @@ static void hashmap_free_no_clear(HashmapBase *h) {
                 free(h);
 }
 
-HashmapBase *internal_hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) {
+HashmapBase *_hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) {
         if (h) {
-                internal_hashmap_clear(h, default_free_key, default_free_value);
+                _hashmap_clear(h, default_free_key, default_free_value);
                 hashmap_free_no_clear(h);
         }
 
         return NULL;
 }
 
-void internal_hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) {
+void _hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value) {
         free_func_t free_key, free_value;
         if (!h)
                 return;
@@ -891,11 +889,11 @@ void internal_hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_f
                  * hash table, and only then call the destructor functions. If these destructors then try to unregister
                  * themselves from our hash table a second time, the entry is already gone. */
 
-                while (internal_hashmap_size(h) > 0) {
+                while (_hashmap_size(h) > 0) {
                         void *k = NULL;
                         void *v;
 
-                        v = internal_hashmap_first_key_and_value(h, true, &k);
+                        v = _hashmap_first_key_and_value(h, true, &k);
 
                         if (free_key)
                                 free_key(k);
@@ -1249,6 +1247,30 @@ int set_put(Set *s, const void *key) {
         return hashmap_put_boldly(s, hash, &swap, true);
 }
 
+int _set_ensure_put(Set **s, const struct hash_ops *hash_ops, const void *key  HASHMAP_DEBUG_PARAMS) {
+        int r;
+
+        r = _set_ensure_allocated(s, hash_ops  HASHMAP_DEBUG_PASS_ARGS);
+        if (r < 0)
+                return r;
+
+        return set_put(*s, key);
+}
+
+int _set_ensure_consume(Set **s, const struct hash_ops *hash_ops, void *key  HASHMAP_DEBUG_PARAMS) {
+        int r;
+
+        r = _set_ensure_put(s, hash_ops, key  HASHMAP_DEBUG_PASS_ARGS);
+        if (r <= 0) {
+                if (hash_ops && hash_ops->free_key)
+                        hash_ops->free_key(key);
+                else
+                        free(key);
+        }
+
+        return r;
+}
+
 int hashmap_replace(Hashmap *h, const void *key, void *value) {
         struct swap_entries swap;
         struct plain_hashmap_entry *e;
@@ -1301,7 +1323,7 @@ int hashmap_update(Hashmap *h, const void *key, void *value) {
         return 0;
 }
 
-void *internal_hashmap_get(HashmapBase *h, const void *key) {
+void *_hashmap_get(HashmapBase *h, const void *key) {
         struct hashmap_base_entry *e;
         unsigned hash, idx;
 
@@ -1336,7 +1358,7 @@ void *hashmap_get2(Hashmap *h, const void *key, void **key2) {
         return e->value;
 }
 
-bool internal_hashmap_contains(HashmapBase *h, const void *key) {
+bool _hashmap_contains(HashmapBase *h, const void *key) {
         unsigned hash;
 
         if (!h)
@@ -1346,7 +1368,7 @@ bool internal_hashmap_contains(HashmapBase *h, const void *key) {
         return bucket_scan(h, hash, key) != IDX_NIL;
 }
 
-void *internal_hashmap_remove(HashmapBase *h, const void *key) {
+void *_hashmap_remove(HashmapBase *h, const void *key) {
         struct hashmap_base_entry *e;
         unsigned hash, idx;
         void *data;
@@ -1484,7 +1506,7 @@ int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_
         return 0;
 }
 
-void *internal_hashmap_remove_value(HashmapBase *h, const void *key, void *value) {
+void *_hashmap_remove_value(HashmapBase *h, const void *key, void *value) {
         struct hashmap_base_entry *e;
         unsigned hash, idx;
 
@@ -1514,7 +1536,7 @@ static unsigned find_first_entry(HashmapBase *h) {
         return hashmap_iterate_entry(h, &i);
 }
 
-void *internal_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key) {
+void *_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key) {
         struct hashmap_base_entry *e;
         void *key, *data;
         unsigned idx;
@@ -1539,21 +1561,21 @@ void *internal_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **r
         return data;
 }
 
-unsigned internal_hashmap_size(HashmapBase *h) {
+unsigned _hashmap_size(HashmapBase *h) {
         if (!h)
                 return 0;
 
         return n_entries(h);
 }
 
-unsigned internal_hashmap_buckets(HashmapBase *h) {
+unsigned _hashmap_buckets(HashmapBase *h) {
         if (!h)
                 return 0;
 
         return n_buckets(h);
 }
 
-int internal_hashmap_merge(Hashmap *h, Hashmap *other) {
+int _hashmap_merge(Hashmap *h, Hashmap *other) {
         Iterator i;
         unsigned idx;
 
@@ -1589,7 +1611,7 @@ int set_merge(Set *s, Set *other) {
         return 0;
 }
 
-int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add) {
+int _hashmap_reserve(HashmapBase *h, unsigned entries_add) {
         int r;
 
         assert(h);
@@ -1607,7 +1629,7 @@ int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add) {
  * Returns: 0 on success.
  *          -ENOMEM on alloc failure, in which case no move has been done.
  */
-int internal_hashmap_move(HashmapBase *h, HashmapBase *other) {
+int _hashmap_move(HashmapBase *h, HashmapBase *other) {
         struct swap_entries swap;
         struct hashmap_base_entry *e, *n;
         Iterator i;
@@ -1652,7 +1674,7 @@ int internal_hashmap_move(HashmapBase *h, HashmapBase *other) {
         return 0;
 }
 
-int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key) {
+int _hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key) {
         struct swap_entries swap;
         unsigned h_hash, other_hash, idx;
         struct hashmap_base_entry *e, *n;
@@ -1689,13 +1711,13 @@ int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *ke
         return 0;
 }
 
-HashmapBase *internal_hashmap_copy(HashmapBase *h) {
+HashmapBase *_hashmap_copy(HashmapBase *h  HASHMAP_DEBUG_PARAMS) {
         HashmapBase *copy;
         int r;
 
         assert(h);
 
-        copy = hashmap_base_new(h->hash_ops, h->type  HASHMAP_DEBUG_SRC_ARGS);
+        copy = hashmap_base_new(h->hash_ops, h->type  HASHMAP_DEBUG_PASS_ARGS);
         if (!copy)
                 return NULL;
 
@@ -1711,15 +1733,13 @@ HashmapBase *internal_hashmap_copy(HashmapBase *h) {
                 assert_not_reached("Unknown hashmap type");
         }
 
-        if (r < 0) {
-                internal_hashmap_free(copy, false, false);
-                return NULL;
-        }
+        if (r < 0)
+                return _hashmap_free(copy, false, false);
 
         return copy;
 }
 
-char **internal_hashmap_get_strv(HashmapBase *h) {
+char **_hashmap_get_strv(HashmapBase *h) {
         char **sv;
         Iterator i;
         unsigned idx, n;
@@ -1767,56 +1787,69 @@ int set_consume(Set *s, void *value) {
         return r;
 }
 
-int hashmap_put_strdup(Hashmap **h, const char *k, const char *v) {
+int _hashmap_put_strdup(Hashmap **h, const char *k, const char *v  HASHMAP_DEBUG_PARAMS) {
         int r;
 
-        r = hashmap_ensure_allocated(h, &string_hash_ops_free_free);
+        r = _hashmap_ensure_allocated(h, &string_hash_ops_free_free  HASHMAP_DEBUG_PASS_ARGS);
         if (r < 0)
                 return r;
 
         _cleanup_free_ char *kdup = NULL, *vdup = NULL;
+
         kdup = strdup(k);
-        vdup = strdup(v);
-        if (!kdup || !vdup)
+        if (!kdup)
                 return -ENOMEM;
 
+        if (v) {
+                vdup = strdup(v);
+                if (!vdup)
+                        return -ENOMEM;
+        }
+
         r = hashmap_put(*h, kdup, vdup);
         if (r < 0) {
-                if (r == -EEXIST && streq(v, hashmap_get(*h, kdup)))
+                if (r == -EEXIST && streq_ptr(v, hashmap_get(*h, kdup)))
                         return 0;
                 return r;
         }
 
-        assert(r > 0); /* 0 would mean vdup is already in the hashmap, which cannot be */
-        kdup = vdup = NULL;
+        /* 0 with non-null vdup would mean vdup is already in the hashmap, which cannot be */
+        assert(vdup == NULL || r > 0);
+        if (r > 0)
+                kdup = vdup = NULL;
 
-        return 0;
+        return r;
 }
 
-int set_put_strdup(Set *s, const char *p) {
+int _set_put_strdup(Set **s, const char *p  HASHMAP_DEBUG_PARAMS) {
         char *c;
+        int r;
 
         assert(s);
         assert(p);
 
-        if (set_contains(s, (char*) p))
+        r = _set_ensure_allocated(s, &string_hash_ops_free  HASHMAP_DEBUG_PASS_ARGS);
+        if (r < 0)
+                return r;
+
+        if (set_contains(*s, (char*) p))
                 return 0;
 
         c = strdup(p);
         if (!c)
                 return -ENOMEM;
 
-        return set_consume(s, c);
+        return set_consume(*s, c);
 }
 
-int set_put_strdupv(Set *s, char **l) {
+int _set_put_strdupv(Set **s, char **l  HASHMAP_DEBUG_PARAMS) {
         int n = 0, r;
         char **i;
 
         assert(s);
 
         STRV_FOREACH(i, l) {
-                r = set_put_strdup(s, *i);
+                r = _set_put_strdup(s, *i  HASHMAP_DEBUG_PASS_ARGS);
                 if (r < 0)
                         return r;
 
index 65adc92513d737854607a406ad15e0a13e0ef111..6009441621596c310afbabd8466631f0744a8c56 100644 (file)
@@ -14,7 +14,7 @@
  * will be treated as empty hashmap for all read operations. That way it is not
  * necessary to instantiate an object for each Hashmap use.
  *
- * If ENABLE_DEBUG_HASHMAP is defined (by configuring with --enable-debug=hashmap),
+ * If ENABLE_DEBUG_HASHMAP is defined (by configuring with -Ddebug-extra=hashmap),
  * the implementation will:
  * - store extra data for debugging and statistics (see tools/gdb-sd_dump_hashmaps.py)
  * - perform extra checks for invalid use of iterators
 
 typedef void* (*hashmap_destroy_t)(void *p);
 
-/* The base type for all hashmap and set types. Many functions in the
- * implementation take (HashmapBase*) parameters and are run-time polymorphic,
- * though the API is not meant to be polymorphic (do not call functions
- * internal_*() directly). */
+/* The base type for all hashmap and set types. Many functions in the implementation take (HashmapBase*)
+ * parameters and are run-time polymorphic, though the API is not meant to be polymorphic (do not call
+ * underscore-prefixed functions directly). */
 typedef struct HashmapBase HashmapBase;
 
 /* Specific hashmap/set types */
@@ -84,62 +83,66 @@ typedef struct {
 # define HASHMAP_DEBUG_PASS_ARGS
 #endif
 
-Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS);
-OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS);
-#define hashmap_new(ops) internal_hashmap_new(ops  HASHMAP_DEBUG_SRC_ARGS)
-#define ordered_hashmap_new(ops) internal_ordered_hashmap_new(ops  HASHMAP_DEBUG_SRC_ARGS)
+Hashmap *_hashmap_new(const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS);
+OrderedHashmap *_ordered_hashmap_new(const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS);
+#define hashmap_new(ops) _hashmap_new(ops  HASHMAP_DEBUG_SRC_ARGS)
+#define ordered_hashmap_new(ops) _ordered_hashmap_new(ops  HASHMAP_DEBUG_SRC_ARGS)
+
+#define hashmap_free_and_replace(a, b)          \
+        ({                                      \
+                hashmap_free(a);                \
+                (a) = (b);                      \
+                (b) = NULL;                     \
+                0;                              \
+        })
 
-HashmapBase *internal_hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value);
+HashmapBase *_hashmap_free(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value);
 static inline Hashmap *hashmap_free(Hashmap *h) {
-        return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, NULL);
+        return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, NULL);
 }
 static inline OrderedHashmap *ordered_hashmap_free(OrderedHashmap *h) {
-        return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, NULL);
+        return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, NULL);
 }
 
 static inline Hashmap *hashmap_free_free(Hashmap *h) {
-        return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, free);
+        return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, free);
 }
 static inline OrderedHashmap *ordered_hashmap_free_free(OrderedHashmap *h) {
-        return (void*) internal_hashmap_free(HASHMAP_BASE(h), NULL, free);
+        return (void*) _hashmap_free(HASHMAP_BASE(h), NULL, free);
 }
 
 static inline Hashmap *hashmap_free_free_key(Hashmap *h) {
-        return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, NULL);
+        return (void*) _hashmap_free(HASHMAP_BASE(h), free, NULL);
 }
 static inline OrderedHashmap *ordered_hashmap_free_free_key(OrderedHashmap *h) {
-        return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, NULL);
+        return (void*) _hashmap_free(HASHMAP_BASE(h), free, NULL);
 }
 
 static inline Hashmap *hashmap_free_free_free(Hashmap *h) {
-        return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, free);
+        return (void*) _hashmap_free(HASHMAP_BASE(h), free, free);
 }
 static inline OrderedHashmap *ordered_hashmap_free_free_free(OrderedHashmap *h) {
-        return (void*) internal_hashmap_free(HASHMAP_BASE(h), free, free);
+        return (void*) _hashmap_free(HASHMAP_BASE(h), free, free);
 }
 
 IteratedCache *iterated_cache_free(IteratedCache *cache);
 int iterated_cache_get(IteratedCache *cache, const void ***res_keys, const void ***res_values, unsigned *res_n_entries);
 
-HashmapBase *internal_hashmap_copy(HashmapBase *h);
-static inline Hashmap *hashmap_copy(Hashmap *h) {
-        return (Hashmap*) internal_hashmap_copy(HASHMAP_BASE(h));
-}
-static inline OrderedHashmap *ordered_hashmap_copy(OrderedHashmap *h) {
-        return (OrderedHashmap*) internal_hashmap_copy(HASHMAP_BASE(h));
-}
+HashmapBase *_hashmap_copy(HashmapBase *h  HASHMAP_DEBUG_PARAMS);
+#define hashmap_copy(h) ((Hashmap*) _hashmap_copy(HASHMAP_BASE(h)  HASHMAP_DEBUG_SRC_ARGS))
+#define ordered_hashmap_copy(h) ((OrderedHashmap*) _hashmap_copy(HASHMAP_BASE(h)  HASHMAP_DEBUG_SRC_ARGS))
 
-int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS);
-int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS);
-#define hashmap_ensure_allocated(h, ops) internal_hashmap_ensure_allocated(h, ops  HASHMAP_DEBUG_SRC_ARGS)
-#define ordered_hashmap_ensure_allocated(h, ops) internal_ordered_hashmap_ensure_allocated(h, ops  HASHMAP_DEBUG_SRC_ARGS)
+int _hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS);
+int _ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops  HASHMAP_DEBUG_PARAMS);
+#define hashmap_ensure_allocated(h, ops) _hashmap_ensure_allocated(h, ops  HASHMAP_DEBUG_SRC_ARGS)
+#define ordered_hashmap_ensure_allocated(h, ops) _ordered_hashmap_ensure_allocated(h, ops  HASHMAP_DEBUG_SRC_ARGS)
 
-IteratedCache *internal_hashmap_iterated_cache_new(HashmapBase *h);
+IteratedCache *_hashmap_iterated_cache_new(HashmapBase *h);
 static inline IteratedCache *hashmap_iterated_cache_new(Hashmap *h) {
-        return (IteratedCache*) internal_hashmap_iterated_cache_new(HASHMAP_BASE(h));
+        return (IteratedCache*) _hashmap_iterated_cache_new(HASHMAP_BASE(h));
 }
 static inline IteratedCache *ordered_hashmap_iterated_cache_new(OrderedHashmap *h) {
-        return (IteratedCache*) internal_hashmap_iterated_cache_new(HASHMAP_BASE(h));
+        return (IteratedCache*) _hashmap_iterated_cache_new(HASHMAP_BASE(h));
 }
 
 int hashmap_put(Hashmap *h, const void *key, void *value);
@@ -147,7 +150,8 @@ static inline int ordered_hashmap_put(OrderedHashmap *h, const void *key, void *
         return hashmap_put(PLAIN_HASHMAP(h), key, value);
 }
 
-int hashmap_put_strdup(Hashmap **h, const char *k, const char *v);
+int _hashmap_put_strdup(Hashmap **h, const char *k, const char *v  HASHMAP_DEBUG_PARAMS);
+#define hashmap_put_strdup(h, k, v) _hashmap_put_strdup(h, k, v  HASHMAP_DEBUG_SRC_ARGS)
 
 int hashmap_update(Hashmap *h, const void *key, void *value);
 static inline int ordered_hashmap_update(OrderedHashmap *h, const void *key, void *value) {
@@ -159,12 +163,12 @@ static inline int ordered_hashmap_replace(OrderedHashmap *h, const void *key, vo
         return hashmap_replace(PLAIN_HASHMAP(h), key, value);
 }
 
-void *internal_hashmap_get(HashmapBase *h, const void *key);
+void *_hashmap_get(HashmapBase *h, const void *key);
 static inline void *hashmap_get(Hashmap *h, const void *key) {
-        return internal_hashmap_get(HASHMAP_BASE(h), key);
+        return _hashmap_get(HASHMAP_BASE(h), key);
 }
 static inline void *ordered_hashmap_get(OrderedHashmap *h, const void *key) {
-        return internal_hashmap_get(HASHMAP_BASE(h), key);
+        return _hashmap_get(HASHMAP_BASE(h), key);
 }
 
 void *hashmap_get2(Hashmap *h, const void *key, void **rkey);
@@ -172,20 +176,20 @@ static inline void *ordered_hashmap_get2(OrderedHashmap *h, const void *key, voi
         return hashmap_get2(PLAIN_HASHMAP(h), key, rkey);
 }
 
-bool internal_hashmap_contains(HashmapBase *h, const void *key);
+bool _hashmap_contains(HashmapBase *h, const void *key);
 static inline bool hashmap_contains(Hashmap *h, const void *key) {
-        return internal_hashmap_contains(HASHMAP_BASE(h), key);
+        return _hashmap_contains(HASHMAP_BASE(h), key);
 }
 static inline bool ordered_hashmap_contains(OrderedHashmap *h, const void *key) {
-        return internal_hashmap_contains(HASHMAP_BASE(h), key);
+        return _hashmap_contains(HASHMAP_BASE(h), key);
 }
 
-void *internal_hashmap_remove(HashmapBase *h, const void *key);
+void *_hashmap_remove(HashmapBase *h, const void *key);
 static inline void *hashmap_remove(Hashmap *h, const void *key) {
-        return internal_hashmap_remove(HASHMAP_BASE(h), key);
+        return _hashmap_remove(HASHMAP_BASE(h), key);
 }
 static inline void *ordered_hashmap_remove(OrderedHashmap *h, const void *key) {
-        return internal_hashmap_remove(HASHMAP_BASE(h), key);
+        return _hashmap_remove(HASHMAP_BASE(h), key);
 }
 
 void *hashmap_remove2(Hashmap *h, const void *key, void **rkey);
@@ -193,9 +197,9 @@ static inline void *ordered_hashmap_remove2(OrderedHashmap *h, const void *key,
         return hashmap_remove2(PLAIN_HASHMAP(h), key, rkey);
 }
 
-void *internal_hashmap_remove_value(HashmapBase *h, const void *key, void *value);
+void *_hashmap_remove_value(HashmapBase *h, const void *key, void *value);
 static inline void *hashmap_remove_value(Hashmap *h, const void *key, void *value) {
-        return internal_hashmap_remove_value(HASHMAP_BASE(h), key, value);
+        return _hashmap_remove_value(HASHMAP_BASE(h), key, value);
 }
 
 static inline void *ordered_hashmap_remove_value(OrderedHashmap *h, const void *key, void *value) {
@@ -214,41 +218,41 @@ static inline int ordered_hashmap_remove_and_replace(OrderedHashmap *h, const vo
 
 /* Since merging data from a OrderedHashmap into a Hashmap or vice-versa
  * should just work, allow this by having looser type-checking here. */
-int internal_hashmap_merge(Hashmap *h, Hashmap *other);
-#define hashmap_merge(h, other) internal_hashmap_merge(PLAIN_HASHMAP(h), PLAIN_HASHMAP(other))
+int _hashmap_merge(Hashmap *h, Hashmap *other);
+#define hashmap_merge(h, other) _hashmap_merge(PLAIN_HASHMAP(h), PLAIN_HASHMAP(other))
 #define ordered_hashmap_merge(h, other) hashmap_merge(h, other)
 
-int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add);
+int _hashmap_reserve(HashmapBase *h, unsigned entries_add);
 static inline int hashmap_reserve(Hashmap *h, unsigned entries_add) {
-        return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add);
+        return _hashmap_reserve(HASHMAP_BASE(h), entries_add);
 }
 static inline int ordered_hashmap_reserve(OrderedHashmap *h, unsigned entries_add) {
-        return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add);
+        return _hashmap_reserve(HASHMAP_BASE(h), entries_add);
 }
 
-int internal_hashmap_move(HashmapBase *h, HashmapBase *other);
+int _hashmap_move(HashmapBase *h, HashmapBase *other);
 /* Unlike hashmap_merge, hashmap_move does not allow mixing the types. */
 static inline int hashmap_move(Hashmap *h, Hashmap *other) {
-        return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other));
+        return _hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other));
 }
 static inline int ordered_hashmap_move(OrderedHashmap *h, OrderedHashmap *other) {
-        return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other));
+        return _hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other));
 }
 
-int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key);
+int _hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key);
 static inline int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key) {
-        return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key);
+        return _hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key);
 }
 static inline int ordered_hashmap_move_one(OrderedHashmap *h, OrderedHashmap *other, const void *key) {
-        return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key);
+        return _hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key);
 }
 
-unsigned internal_hashmap_size(HashmapBase *h) _pure_;
+unsigned _hashmap_size(HashmapBase *h) _pure_;
 static inline unsigned hashmap_size(Hashmap *h) {
-        return internal_hashmap_size(HASHMAP_BASE(h));
+        return _hashmap_size(HASHMAP_BASE(h));
 }
 static inline unsigned ordered_hashmap_size(OrderedHashmap *h) {
-        return internal_hashmap_size(HASHMAP_BASE(h));
+        return _hashmap_size(HASHMAP_BASE(h));
 }
 
 static inline bool hashmap_isempty(Hashmap *h) {
@@ -258,49 +262,49 @@ static inline bool ordered_hashmap_isempty(OrderedHashmap *h) {
         return ordered_hashmap_size(h) == 0;
 }
 
-unsigned internal_hashmap_buckets(HashmapBase *h) _pure_;
+unsigned _hashmap_buckets(HashmapBase *h) _pure_;
 static inline unsigned hashmap_buckets(Hashmap *h) {
-        return internal_hashmap_buckets(HASHMAP_BASE(h));
+        return _hashmap_buckets(HASHMAP_BASE(h));
 }
 static inline unsigned ordered_hashmap_buckets(OrderedHashmap *h) {
-        return internal_hashmap_buckets(HASHMAP_BASE(h));
+        return _hashmap_buckets(HASHMAP_BASE(h));
 }
 
-bool internal_hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key);
+bool _hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key);
 static inline bool hashmap_iterate(Hashmap *h, Iterator *i, void **value, const void **key) {
-        return internal_hashmap_iterate(HASHMAP_BASE(h), i, value, key);
+        return _hashmap_iterate(HASHMAP_BASE(h), i, value, key);
 }
 static inline bool ordered_hashmap_iterate(OrderedHashmap *h, Iterator *i, void **value, const void **key) {
-        return internal_hashmap_iterate(HASHMAP_BASE(h), i, value, key);
+        return _hashmap_iterate(HASHMAP_BASE(h), i, value, key);
 }
 
-void internal_hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value);
+void _hashmap_clear(HashmapBase *h, free_func_t default_free_key, free_func_t default_free_value);
 static inline void hashmap_clear(Hashmap *h) {
-        internal_hashmap_clear(HASHMAP_BASE(h), NULL, NULL);
+        _hashmap_clear(HASHMAP_BASE(h), NULL, NULL);
 }
 static inline void ordered_hashmap_clear(OrderedHashmap *h) {
-        internal_hashmap_clear(HASHMAP_BASE(h), NULL, NULL);
+        _hashmap_clear(HASHMAP_BASE(h), NULL, NULL);
 }
 
 static inline void hashmap_clear_free(Hashmap *h) {
-        internal_hashmap_clear(HASHMAP_BASE(h), NULL, free);
+        _hashmap_clear(HASHMAP_BASE(h), NULL, free);
 }
 static inline void ordered_hashmap_clear_free(OrderedHashmap *h) {
-        internal_hashmap_clear(HASHMAP_BASE(h), NULL, free);
+        _hashmap_clear(HASHMAP_BASE(h), NULL, free);
 }
 
 static inline void hashmap_clear_free_key(Hashmap *h) {
-        internal_hashmap_clear(HASHMAP_BASE(h), free, NULL);
+        _hashmap_clear(HASHMAP_BASE(h), free, NULL);
 }
 static inline void ordered_hashmap_clear_free_key(OrderedHashmap *h) {
-        internal_hashmap_clear(HASHMAP_BASE(h), free, NULL);
+        _hashmap_clear(HASHMAP_BASE(h), free, NULL);
 }
 
 static inline void hashmap_clear_free_free(Hashmap *h) {
-        internal_hashmap_clear(HASHMAP_BASE(h), free, free);
+        _hashmap_clear(HASHMAP_BASE(h), free, free);
 }
 static inline void ordered_hashmap_clear_free_free(OrderedHashmap *h) {
-        internal_hashmap_clear(HASHMAP_BASE(h), free, free);
+        _hashmap_clear(HASHMAP_BASE(h), free, free);
 }
 
 /*
@@ -314,50 +318,50 @@ static inline void ordered_hashmap_clear_free_free(OrderedHashmap *h) {
  * the first entry is O(1).
  */
 
-void *internal_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key);
+void *_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **ret_key);
 static inline void *hashmap_steal_first_key_and_value(Hashmap *h, void **ret) {
-        return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, ret);
+        return _hashmap_first_key_and_value(HASHMAP_BASE(h), true, ret);
 }
 static inline void *ordered_hashmap_steal_first_key_and_value(OrderedHashmap *h, void **ret) {
-        return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, ret);
+        return _hashmap_first_key_and_value(HASHMAP_BASE(h), true, ret);
 }
 static inline void *hashmap_first_key_and_value(Hashmap *h, void **ret) {
-        return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, ret);
+        return _hashmap_first_key_and_value(HASHMAP_BASE(h), false, ret);
 }
 static inline void *ordered_hashmap_first_key_and_value(OrderedHashmap *h, void **ret) {
-        return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, ret);
+        return _hashmap_first_key_and_value(HASHMAP_BASE(h), false, ret);
 }
 
 static inline void *hashmap_steal_first(Hashmap *h) {
-        return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, NULL);
+        return _hashmap_first_key_and_value(HASHMAP_BASE(h), true, NULL);
 }
 static inline void *ordered_hashmap_steal_first(OrderedHashmap *h) {
-        return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), true, NULL);
+        return _hashmap_first_key_and_value(HASHMAP_BASE(h), true, NULL);
 }
 static inline void *hashmap_first(Hashmap *h) {
-        return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, NULL);
+        return _hashmap_first_key_and_value(HASHMAP_BASE(h), false, NULL);
 }
 static inline void *ordered_hashmap_first(OrderedHashmap *h) {
-        return internal_hashmap_first_key_and_value(HASHMAP_BASE(h), false, NULL);
+        return _hashmap_first_key_and_value(HASHMAP_BASE(h), false, NULL);
 }
 
-static inline void *internal_hashmap_first_key(HashmapBase *h, bool remove) {
+static inline void *_hashmap_first_key(HashmapBase *h, bool remove) {
         void *key = NULL;
 
-        (void) internal_hashmap_first_key_and_value(HASHMAP_BASE(h), remove, &key);
+        (void) _hashmap_first_key_and_value(HASHMAP_BASE(h), remove, &key);
         return key;
 }
 static inline void *hashmap_steal_first_key(Hashmap *h) {
-        return internal_hashmap_first_key(HASHMAP_BASE(h), true);
+        return _hashmap_first_key(HASHMAP_BASE(h), true);
 }
 static inline void *ordered_hashmap_steal_first_key(OrderedHashmap *h) {
-        return internal_hashmap_first_key(HASHMAP_BASE(h), true);
+        return _hashmap_first_key(HASHMAP_BASE(h), true);
 }
 static inline void *hashmap_first_key(Hashmap *h) {
-        return internal_hashmap_first_key(HASHMAP_BASE(h), false);
+        return _hashmap_first_key(HASHMAP_BASE(h), false);
 }
 static inline void *ordered_hashmap_first_key(OrderedHashmap *h) {
-        return internal_hashmap_first_key(HASHMAP_BASE(h), false);
+        return _hashmap_first_key(HASHMAP_BASE(h), false);
 }
 
 #define hashmap_clear_with_destructor(_s, _f)                   \
@@ -386,12 +390,12 @@ static inline void *ordered_hashmap_first_key(OrderedHashmap *h) {
 /* no hashmap_next */
 void *ordered_hashmap_next(OrderedHashmap *h, const void *key);
 
-char **internal_hashmap_get_strv(HashmapBase *h);
+char **_hashmap_get_strv(HashmapBase *h);
 static inline char **hashmap_get_strv(Hashmap *h) {
-        return internal_hashmap_get_strv(HASHMAP_BASE(h));
+        return _hashmap_get_strv(HASHMAP_BASE(h));
 }
 static inline char **ordered_hashmap_get_strv(OrderedHashmap *h) {
-        return internal_hashmap_get_strv(HASHMAP_BASE(h));
+        return _hashmap_get_strv(HASHMAP_BASE(h));
 }
 
 /*
index 7bc2e3f370fb650f5f2f0093f58afb8e1f46f54a..90a3dfc86471d01d93c53d96678151593c796305 100644 (file)
@@ -12,6 +12,7 @@
 #include "hostname-util.h"
 #include "macro.h"
 #include "string-util.h"
+#include "strv.h"
 
 bool hostname_is_set(void) {
         struct utsname u;
@@ -21,7 +22,7 @@ bool hostname_is_set(void) {
         if (isempty(u.nodename))
                 return false;
 
-        /* This is the built-in kernel default host name */
+        /* This is the built-in kernel default hostname */
         if (streq(u.nodename, "(none)"))
                 return false;
 
@@ -30,6 +31,7 @@ bool hostname_is_set(void) {
 
 char* gethostname_malloc(void) {
         struct utsname u;
+        const char *s;
 
         /* This call tries to return something useful, either the actual hostname
          * or it makes something up. The only reason it might fail is OOM.
@@ -37,10 +39,28 @@ char* gethostname_malloc(void) {
 
         assert_se(uname(&u) >= 0);
 
-        if (isempty(u.nodename) || streq(u.nodename, "(none)"))
-                return strdup(FALLBACK_HOSTNAME);
+        s = u.nodename;
+        if (isempty(s) || streq(s, "(none)"))
+                s = FALLBACK_HOSTNAME;
 
-        return strdup(u.nodename);
+        return strdup(s);
+}
+
+char* gethostname_short_malloc(void) {
+        struct utsname u;
+        const char *s;
+
+        /* Like above, but kills the FQDN part if present. */
+
+        assert_se(uname(&u) >= 0);
+
+        s = u.nodename;
+        if (isempty(s) || streq(s, "(none)") || s[0] == '.') {
+                s = FALLBACK_HOSTNAME;
+                assert(s[0] != '.');
+        }
+
+        return strndup(s, strcspn(s, "."));
 }
 
 int gethostname_strict(char **ret) {
@@ -77,7 +97,7 @@ bool valid_ldh_char(char c) {
 }
 
 /**
- * Check if s looks like a valid host name or FQDN. This does not do
+ * Check if s looks like a valid hostname or FQDN. This does not do
  * full DNS validation, but only checks if the name is composed of
  * allowed characters and the length is not above the maximum allowed
  * by Linux (c.f. dns_name_is_valid()). Trailing dot is allowed if
@@ -180,14 +200,16 @@ bool is_localhost(const char *hostname) {
         /* This tries to identify local host and domain names
          * described in RFC6761 plus the redhatism of localdomain */
 
-        return strcaseeq(hostname, "localhost") ||
-               strcaseeq(hostname, "localhost.") ||
-               strcaseeq(hostname, "localhost.localdomain") ||
-               strcaseeq(hostname, "localhost.localdomain.") ||
-               endswith_no_case(hostname, ".localhost") ||
-               endswith_no_case(hostname, ".localhost.") ||
-               endswith_no_case(hostname, ".localhost.localdomain") ||
-               endswith_no_case(hostname, ".localhost.localdomain.");
+        return STRCASE_IN_SET(
+                        hostname,
+                        "localhost",
+                        "localhost.",
+                        "localhost.localdomain",
+                        "localhost.localdomain.") ||
+                endswith_no_case(hostname, ".localhost") ||
+                endswith_no_case(hostname, ".localhost.") ||
+                endswith_no_case(hostname, ".localhost.localdomain") ||
+                endswith_no_case(hostname, ".localhost.localdomain.");
 }
 
 bool is_gateway_hostname(const char *hostname) {
index 7ba386a0fd99c5162cd32b22e63b8ce24e344532..cafd6f020bff96eb6b9a1f2bf735774fb67351b5 100644 (file)
@@ -9,6 +9,7 @@
 bool hostname_is_set(void);
 
 char* gethostname_malloc(void);
+char* gethostname_short_malloc(void);
 int gethostname_strict(char **ret);
 
 bool valid_ldh_char(char c) _const_;
index bfe855fb4b9ca9ed0bd7d59508beb2c9eec7868d..ea50e26197f48a7253dcb4cfcbaeb1f2de4bbd63 100644 (file)
@@ -14,6 +14,7 @@
 #include "macro.h"
 #include "parse-util.h"
 #include "random-util.h"
+#include "string-util.h"
 #include "strxcpyx.h"
 #include "util.h"
 
@@ -107,11 +108,7 @@ int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_
                 return in4_addr_equal(&a->in, &b->in);
 
         if (family == AF_INET6)
-                return
-                        a->in6.s6_addr32[0] == b->in6.s6_addr32[0] &&
-                        a->in6.s6_addr32[1] == b->in6.s6_addr32[1] &&
-                        a->in6.s6_addr32[2] == b->in6.s6_addr32[2] &&
-                        a->in6.s6_addr32[3] == b->in6.s6_addr32[3];
+                return IN6_ARE_ADDR_EQUAL(&a->in6, &b->in6);
 
         return -EAFNOSUPPORT;
 }
@@ -177,47 +174,89 @@ int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen)
         assert(u);
 
         /* Increases the network part of an address by one. Returns
-         * positive it that succeeds, or 0 if this overflows. */
+         * positive if that succeeds, or -ERANGE if this overflows. */
+
+        return in_addr_prefix_nth(family, u, prefixlen, 1);
+}
+
+/*
+ * Calculates the nth prefix of size prefixlen starting from the address denoted by u.
+ *
+ * On success 1 will be returned and the calculated prefix will be available in
+ * u. In the case nth == 0 the input will be left unchanged and 1 will be returned.
+ * In case the calculation cannot be performed (invalid prefix length,
+ * overflows would occur) -ERANGE is returned. If the address family given isn't
+ * supported -EAFNOSUPPORT will be returned.
+ *
+ *
+ * Examples:
+ *   - in_addr_prefix_nth(AF_INET, 192.168.0.0, 24, 2), returns 1, writes 192.168.2.0 to u
+ *   - in_addr_prefix_nth(AF_INET, 192.168.0.0, 24, 0), returns 1, no data written
+ *   - in_addr_prefix_nth(AF_INET, 255.255.255.0, 24, 1), returns -ERANGE, no data written
+ *   - in_addr_prefix_nth(AF_INET, 255.255.255.0, 0, 1), returns -ERANGE, no data written
+ *   - in_addr_prefix_nth(AF_INET6, 2001:db8, 64, 0xff00) returns 1, writes 2001:0db8:0000:ff00:: to u
+ */
+int in_addr_prefix_nth(int family, union in_addr_union *u, unsigned prefixlen, uint64_t nth) {
+        assert(u);
 
         if (prefixlen <= 0)
-                return 0;
+                return -ERANGE;
 
-        if (family == AF_INET) {
-                uint32_t c, n;
+        if (nth == 0)
+                return 1;
 
+        if (family == AF_INET) {
+                uint32_t c, n, t;
                 if (prefixlen > 32)
                         prefixlen = 32;
 
                 c = be32toh(u->in.s_addr);
-                n = c + (1UL << (32 - prefixlen));
-                if (n < c)
-                        return 0;
-                n &= 0xFFFFFFFFUL << (32 - prefixlen);
 
+                t = nth << (32 - prefixlen);
+
+                /* Check for wrap */
+                if (c > UINT32_MAX - t)
+                        return -ERANGE;
+
+                n = c + t;
+
+                n &= UINT32_C(0xFFFFFFFF) << (32 - prefixlen);
                 u->in.s_addr = htobe32(n);
                 return 1;
         }
 
         if (family == AF_INET6) {
-                struct in6_addr add = {}, result;
+                struct in6_addr result = {};
                 uint8_t overflow = 0;
-                unsigned i;
+                uint64_t delta;  /* this assumes that we only ever have to up to 1<<64 subnets */
+                unsigned start_byte = (prefixlen - 1) / 8;
 
                 if (prefixlen > 128)
                         prefixlen = 128;
 
                 /* First calculate what we have to add */
-                add.s6_addr[(prefixlen-1) / 8] = 1 << (7 - (prefixlen-1) % 8);
+                delta = nth << ((128 - prefixlen) % 8);
 
-                for (i = 16; i > 0; i--) {
+                for (unsigned i = 16; i > 0; i--) {
                         unsigned j = i - 1;
+                        unsigned d = 0;
+
+                        if (j <= start_byte) {
+                                int16_t t;
 
-                        result.s6_addr[j] = u->in6.s6_addr[j] + add.s6_addr[j] + overflow;
-                        overflow = (result.s6_addr[j] < u->in6.s6_addr[j]);
+                                d = delta & 0xFF;
+                                delta >>= 8;
+
+                                t = u->in6.s6_addr[j] + d + overflow;
+                                overflow = t > UINT8_MAX ? t - UINT8_MAX : 0;
+
+                                result.s6_addr[j] = (uint8_t)t;
+                        } else
+                                result.s6_addr[j] = u->in6.s6_addr[j];
                 }
 
-                if (overflow)
-                        return 0;
+                if (overflow || delta != 0)
+                        return -ERANGE;
 
                 u->in6 = result;
                 return 1;
@@ -403,6 +442,61 @@ fallback:
         return in_addr_to_string(family, u, ret);
 }
 
+int in_addr_port_ifindex_name_to_string(int family, const union in_addr_union *u, uint16_t port, int ifindex, const char *server_name, char **ret) {
+        _cleanup_free_ char *ip_str = NULL, *x = NULL;
+        int r;
+
+        assert(IN_SET(family, AF_INET, AF_INET6));
+        assert(u);
+        assert(ret);
+
+        /* Much like in_addr_to_string(), but optionally appends the zone interface index to the address, to properly
+         * handle IPv6 link-local addresses. */
+
+        r = in_addr_to_string(family, u, &ip_str);
+        if (r < 0)
+                return r;
+
+        if (family == AF_INET6) {
+                r = in_addr_is_link_local(family, u);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        ifindex = 0;
+        } else
+                ifindex = 0; /* For IPv4 address, ifindex is always ignored. */
+
+        if (port == 0 && ifindex == 0 && isempty(server_name)) {
+                *ret = TAKE_PTR(ip_str);
+                return 0;
+        }
+
+        const char *separator = isempty(server_name) ? "" : "#";
+        server_name = strempty(server_name);
+
+        if (port > 0) {
+                if (family == AF_INET6) {
+                        if (ifindex > 0)
+                                r = asprintf(&x, "[%s]:%"PRIu16"%%%i%s%s", ip_str, port, ifindex, separator, server_name);
+                        else
+                                r = asprintf(&x, "[%s]:%"PRIu16"%s%s", ip_str, port, separator, server_name);
+                } else
+                        r = asprintf(&x, "%s:%"PRIu16"%s%s", ip_str, port, separator, server_name);
+        } else {
+                if (ifindex > 0)
+                        r = asprintf(&x, "%s%%%i%s%s", ip_str, ifindex, separator, server_name);
+                else {
+                        x = strjoin(ip_str, separator, server_name);
+                        r = x ? 0 : -ENOMEM;
+                }
+        }
+        if (r < 0)
+                return -ENOMEM;
+
+        *ret = TAKE_PTR(x);
+        return 0;
+}
+
 int in_addr_from_string(int family, const char *s, union in_addr_union *ret) {
         union in_addr_union buffer;
         assert(s);
index ae2dad0bb1e92280cb6efe34b8e9e94dbd6e8949..dc3f575bc977dfd0195a38f43722c5f0b22b56d5 100644 (file)
@@ -36,10 +36,12 @@ bool in4_addr_equal(const struct in_addr *a, const struct in_addr *b);
 int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b);
 int in_addr_prefix_intersect(int family, const union in_addr_union *a, unsigned aprefixlen, const union in_addr_union *b, unsigned bprefixlen);
 int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen);
+int in_addr_prefix_nth(int family, union in_addr_union *u, unsigned prefixlen, uint64_t nth);
 int in_addr_random_prefix(int family, union in_addr_union *u, unsigned prefixlen_fixed_part, unsigned prefixlen);
 int in_addr_to_string(int family, const union in_addr_union *u, char **ret);
 int in_addr_prefix_to_string(int family, const union in_addr_union *u, unsigned prefixlen, char **ret);
 int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret);
+int in_addr_port_ifindex_name_to_string(int family, const union in_addr_union *u, uint16_t port, int ifindex, const char *server_name, char **ret);
 int in_addr_from_string(int family, const char *s, union in_addr_union *ret);
 int in_addr_from_string_auto(const char *s, int *ret_family, union in_addr_union *ret);
 
index c906fc074171921fa3ff7ad6db0e05aab3db2595..18baadb1fed767ddb014c3d8d108a1436bf000e9 100644 (file)
 #include "time-util.h"
 
 int flush_fd(int fd) {
-        struct pollfd pollfd = {
-                .fd = fd,
-                .events = POLLIN,
-        };
         int count = 0;
 
         /* Read from the specified file descriptor, until POLLIN is not set anymore, throwing away everything
@@ -27,19 +23,18 @@ int flush_fd(int fd) {
                 ssize_t l;
                 int r;
 
-                r = poll(&pollfd, 1, 0);
+                r = fd_wait_for_event(fd, POLLIN, 0);
                 if (r < 0) {
-                        if (errno == EINTR)
+                        if (r == -EINTR)
                                 continue;
 
-                        return -errno;
-
-                } else if (r == 0)
+                        return r;
+                }
+                if (r == 0)
                         return count;
 
                 l = read(fd, buf, sizeof(buf));
                 if (l < 0) {
-
                         if (errno == EINTR)
                                 continue;
 
@@ -155,21 +150,15 @@ int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
 }
 
 int pipe_eof(int fd) {
-        struct pollfd pollfd = {
-                .fd = fd,
-                .events = POLLIN|POLLHUP,
-        };
-
         int r;
 
-        r = poll(&pollfd, 1, 0);
+        r = fd_wait_for_event(fd, POLLIN, 0);
         if (r < 0)
-                return -errno;
-
+                return r;
         if (r == 0)
                 return 0;
 
-        return pollfd.revents & POLLHUP;
+        return !!(r & POLLHUP);
 }
 
 int fd_wait_for_event(int fd, int event, usec_t t) {
@@ -188,6 +177,9 @@ int fd_wait_for_event(int fd, int event, usec_t t) {
         if (r == 0)
                 return 0;
 
+        if (pollfd.revents & POLLNVAL)
+                return -EBADF;
+
         return pollfd.revents;
 }
 
index 12a7fb0945e21c326c612bd68c10a5a24bdc6582..741c43c2b9af1f4f37a06cbe21e9ab18b4574970 100644 (file)
 #include "selinux-util.h"
 #include "smack-util.h"
 
-int label_fix(const char *path, LabelFixFlags flags) {
+int label_fix_container(const char *path, const char *inside_path, LabelFixFlags flags) {
         int r, q;
 
-        r = mac_selinux_fix(path, flags);
-        q = mac_smack_fix(path, flags);
+        r = mac_selinux_fix_container(path, inside_path, flags);
+        q = mac_smack_fix_container(path, inside_path, flags);
 
         if (r < 0)
                 return r;
@@ -45,6 +45,26 @@ int symlink_label(const char *old_path, const char *new_path) {
         return mac_smack_fix(new_path, 0);
 }
 
+int mknod_label(const char *pathname, mode_t mode, dev_t dev) {
+        int r;
+
+        assert(pathname);
+
+        r = mac_selinux_create_file_prepare(pathname, mode);
+        if (r < 0)
+                return r;
+
+        if (mknod(pathname, mode, dev) < 0)
+                r = -errno;
+
+        mac_selinux_create_file_clear();
+
+        if (r < 0)
+                return r;
+
+        return mac_smack_fix(pathname, 0);
+}
+
 int btrfs_subvol_make_label(const char *path) {
         int r;
 
index 594fd65974c1e3981270c874dcfcffea7c4c21f6..6dc0f710efcb5e904f5a29d64fff694571d40e02 100644 (file)
@@ -9,10 +9,14 @@ typedef enum LabelFixFlags {
         LABEL_IGNORE_EROFS  = 1 << 1,
 } LabelFixFlags;
 
-int label_fix(const char *path, LabelFixFlags flags);
+int label_fix_container(const char *path, const char *inside_path, LabelFixFlags flags);
+static inline int label_fix(const char *path, LabelFixFlags flags) {
+        return label_fix_container(path, path, flags);
+}
 
 int mkdir_label(const char *path, mode_t mode);
 int mkdirat_label(int dirfd, const char *path, mode_t mode);
 int symlink_label(const char *old_path, const char *new_path);
+int mknod_label(const char *pathname, mode_t mode, dev_t dev);
 
 int btrfs_subvol_make_label(const char *path);
index 3242e9c7fbf16db4ed8ebd17d0b7cc01e7f1e202..9da767a5706f11135cb042d8be6fe3f70cf07278 100644 (file)
@@ -125,16 +125,9 @@ uint64_t system_tasks_max(void) {
         if (r < 0)
                 log_debug_errno(r, "Failed to determine cgroup root path, ignoring: %m");
         else {
-                _cleanup_free_ char *value = NULL;
-
-                r = cg_get_attribute("pids", root, "pids.max", &value);
+                r = cg_get_attribute_as_uint64("pids", root, "pids.max", &b);
                 if (r < 0)
                         log_debug_errno(r, "Failed to read pids.max attribute of cgroup root, ignoring: %m");
-                else if (!streq(value, "max")) {
-                        r = safe_atou64(value, &b);
-                        if (r < 0)
-                                log_debug_errno(r, "Failed to parse pids.max attribute of cgroup root, ignoring: %m");
-                }
         }
 
         return MIN3(TASKS_MAX,
diff --git a/src/basic/linux/can/netlink.h b/src/basic/linux/can/netlink.h
new file mode 100644 (file)
index 0000000..6f598b7
--- /dev/null
@@ -0,0 +1,144 @@
+/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
+/*
+ * linux/can/netlink.h
+ *
+ * Definitions for the CAN netlink interface
+ *
+ * Copyright (c) 2009 Wolfgang Grandegger <wg@grandegger.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _UAPI_CAN_NETLINK_H
+#define _UAPI_CAN_NETLINK_H
+
+#include <linux/types.h>
+
+/*
+ * CAN bit-timing parameters
+ *
+ * For further information, please read chapter "8 BIT TIMING
+ * REQUIREMENTS" of the "Bosch CAN Specification version 2.0"
+ * at http://www.semiconductors.bosch.de/pdf/can2spec.pdf.
+ */
+struct can_bittiming {
+       __u32 bitrate;          /* Bit-rate in bits/second */
+       __u32 sample_point;     /* Sample point in one-tenth of a percent */
+       __u32 tq;               /* Time quanta (TQ) in nanoseconds */
+       __u32 prop_seg;         /* Propagation segment in TQs */
+       __u32 phase_seg1;       /* Phase buffer segment 1 in TQs */
+       __u32 phase_seg2;       /* Phase buffer segment 2 in TQs */
+       __u32 sjw;              /* Synchronisation jump width in TQs */
+       __u32 brp;              /* Bit-rate prescaler */
+};
+
+/*
+ * CAN hardware-dependent bit-timing constant
+ *
+ * Used for calculating and checking bit-timing parameters
+ */
+struct can_bittiming_const {
+       char name[16];          /* Name of the CAN controller hardware */
+       __u32 tseg1_min;        /* Time segment 1 = prop_seg + phase_seg1 */
+       __u32 tseg1_max;
+       __u32 tseg2_min;        /* Time segment 2 = phase_seg2 */
+       __u32 tseg2_max;
+       __u32 sjw_max;          /* Synchronisation jump width */
+       __u32 brp_min;          /* Bit-rate prescaler */
+       __u32 brp_max;
+       __u32 brp_inc;
+};
+
+/*
+ * CAN clock parameters
+ */
+struct can_clock {
+       __u32 freq;             /* CAN system clock frequency in Hz */
+};
+
+/*
+ * CAN operational and error states
+ */
+enum can_state {
+       CAN_STATE_ERROR_ACTIVE = 0,     /* RX/TX error count < 96 */
+       CAN_STATE_ERROR_WARNING,        /* RX/TX error count < 128 */
+       CAN_STATE_ERROR_PASSIVE,        /* RX/TX error count < 256 */
+       CAN_STATE_BUS_OFF,              /* RX/TX error count >= 256 */
+       CAN_STATE_STOPPED,              /* Device is stopped */
+       CAN_STATE_SLEEPING,             /* Device is sleeping */
+       CAN_STATE_MAX
+};
+
+/*
+ * CAN bus error counters
+ */
+struct can_berr_counter {
+       __u16 txerr;
+       __u16 rxerr;
+};
+
+/*
+ * CAN controller mode
+ */
+struct can_ctrlmode {
+       __u32 mask;
+       __u32 flags;
+};
+
+#define CAN_CTRLMODE_LOOPBACK          0x01    /* Loopback mode */
+#define CAN_CTRLMODE_LISTENONLY                0x02    /* Listen-only mode */
+#define CAN_CTRLMODE_3_SAMPLES         0x04    /* Triple sampling mode */
+#define CAN_CTRLMODE_ONE_SHOT          0x08    /* One-Shot mode */
+#define CAN_CTRLMODE_BERR_REPORTING    0x10    /* Bus-error reporting */
+#define CAN_CTRLMODE_FD                        0x20    /* CAN FD mode */
+#define CAN_CTRLMODE_PRESUME_ACK       0x40    /* Ignore missing CAN ACKs */
+#define CAN_CTRLMODE_FD_NON_ISO                0x80    /* CAN FD in non-ISO mode */
+
+/*
+ * CAN device statistics
+ */
+struct can_device_stats {
+       __u32 bus_error;        /* Bus errors */
+       __u32 error_warning;    /* Changes to error warning state */
+       __u32 error_passive;    /* Changes to error passive state */
+       __u32 bus_off;          /* Changes to bus off state */
+       __u32 arbitration_lost; /* Arbitration lost errors */
+       __u32 restarts;         /* CAN controller re-starts */
+};
+
+/*
+ * CAN netlink interface
+ */
+enum {
+       IFLA_CAN_UNSPEC,
+       IFLA_CAN_BITTIMING,
+       IFLA_CAN_BITTIMING_CONST,
+       IFLA_CAN_CLOCK,
+       IFLA_CAN_STATE,
+       IFLA_CAN_CTRLMODE,
+       IFLA_CAN_RESTART_MS,
+       IFLA_CAN_RESTART,
+       IFLA_CAN_BERR_COUNTER,
+       IFLA_CAN_DATA_BITTIMING,
+       IFLA_CAN_DATA_BITTIMING_CONST,
+       IFLA_CAN_TERMINATION,
+       IFLA_CAN_TERMINATION_CONST,
+       IFLA_CAN_BITRATE_CONST,
+       IFLA_CAN_DATA_BITRATE_CONST,
+       IFLA_CAN_BITRATE_MAX,
+       __IFLA_CAN_MAX
+};
+
+#define IFLA_CAN_MAX   (__IFLA_CAN_MAX - 1)
+
+/* u16 termination range: 1..65535 Ohms */
+#define CAN_TERMINATION_DISABLED 0
+
+#endif /* !_UAPI_CAN_NETLINK_H */
diff --git a/src/basic/linux/hdlc/ioctl.h b/src/basic/linux/hdlc/ioctl.h
new file mode 100644 (file)
index 0000000..b06341a
--- /dev/null
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef __HDLC_IOCTL_H__
+#define __HDLC_IOCTL_H__
+
+
+#define GENERIC_HDLC_VERSION 4 /* For synchronization with sethdlc utility */
+
+#define CLOCK_DEFAULT   0      /* Default setting */
+#define CLOCK_EXT      1       /* External TX and RX clock - DTE */
+#define CLOCK_INT      2       /* Internal TX and RX clock - DCE */
+#define CLOCK_TXINT    3       /* Internal TX and external RX clock */
+#define CLOCK_TXFROMRX 4       /* TX clock derived from external RX clock */
+
+
+#define ENCODING_DEFAULT       0 /* Default setting */
+#define ENCODING_NRZ           1
+#define ENCODING_NRZI          2
+#define ENCODING_FM_MARK       3
+#define ENCODING_FM_SPACE      4
+#define ENCODING_MANCHESTER    5
+
+
+#define PARITY_DEFAULT         0 /* Default setting */
+#define PARITY_NONE            1 /* No parity */
+#define PARITY_CRC16_PR0       2 /* CRC16, initial value 0x0000 */
+#define PARITY_CRC16_PR1       3 /* CRC16, initial value 0xFFFF */
+#define PARITY_CRC16_PR0_CCITT 4 /* CRC16, initial 0x0000, ITU-T version */
+#define PARITY_CRC16_PR1_CCITT 5 /* CRC16, initial 0xFFFF, ITU-T version */
+#define PARITY_CRC32_PR0_CCITT 6 /* CRC32, initial value 0x00000000 */
+#define PARITY_CRC32_PR1_CCITT 7 /* CRC32, initial value 0xFFFFFFFF */
+
+#define LMI_DEFAULT            0 /* Default setting */
+#define LMI_NONE               1 /* No LMI, all PVCs are static */
+#define LMI_ANSI               2 /* ANSI Annex D */
+#define LMI_CCITT              3 /* ITU-T Annex A */
+#define LMI_CISCO              4 /* The "original" LMI, aka Gang of Four */
+
+#ifndef __ASSEMBLY__
+
+typedef struct {
+       unsigned int clock_rate; /* bits per second */
+       unsigned int clock_type; /* internal, external, TX-internal etc. */
+       unsigned short loopback;
+} sync_serial_settings;          /* V.35, V.24, X.21 */
+
+typedef struct {
+       unsigned int clock_rate; /* bits per second */
+       unsigned int clock_type; /* internal, external, TX-internal etc. */
+       unsigned short loopback;
+       unsigned int slot_map;
+} te1_settings;                  /* T1, E1 */
+
+typedef struct {
+       unsigned short encoding;
+       unsigned short parity;
+} raw_hdlc_proto;
+
+typedef struct {
+       unsigned int t391;
+       unsigned int t392;
+       unsigned int n391;
+       unsigned int n392;
+       unsigned int n393;
+       unsigned short lmi;
+       unsigned short dce; /* 1 for DCE (network side) operation */
+} fr_proto;
+
+typedef struct {
+       unsigned int dlci;
+} fr_proto_pvc;          /* for creating/deleting FR PVCs */
+
+typedef struct {
+       unsigned int dlci;
+       char master[IFNAMSIZ];  /* Name of master FRAD device */
+}fr_proto_pvc_info;            /* for returning PVC information only */
+
+typedef struct {
+    unsigned int interval;
+    unsigned int timeout;
+} cisco_proto;
+
+typedef struct {
+       unsigned short dce; /* 1 for DCE (network side) operation */
+       unsigned int modulo; /* modulo (8 = basic / 128 = extended) */
+       unsigned int window; /* frame window size */
+       unsigned int t1; /* timeout t1 */
+       unsigned int t2; /* timeout t2 */
+       unsigned int n2; /* frame retry counter */
+} x25_hdlc_proto;
+
+/* PPP doesn't need any info now - supply length = 0 to ioctl */
+
+#endif /* __ASSEMBLY__ */
+#endif /* __HDLC_IOCTL_H__ */
index a5ae898ff4c249e64efa03c34ff4585822e3fb73..59948c2f73a9f06adc7ed33b8284a7b9a468bad6 100644 (file)
@@ -212,6 +212,7 @@ struct if_settings {
                fr_proto                *fr;
                fr_proto_pvc            *fr_pvc;
                fr_proto_pvc_info       *fr_pvc_info;
+               x25_hdlc_proto          *x25;
 
                /* interface settings */
                sync_serial_settings    *sync;
index 790585f0e61b5b252a03bd558694dc4c8921246b..45f3750aa861b5bfbaedf9c0b643b3d6ac5888ae 100644 (file)
 #define BOND_XMIT_POLICY_ENCAP23       3 /* encapsulated layer 2+3 */
 #define BOND_XMIT_POLICY_ENCAP34       4 /* encapsulated layer 3+4 */
 
+/* 802.3ad port state definitions (43.4.2.2 in the 802.3ad standard) */
+#define LACP_STATE_LACP_ACTIVITY   0x1
+#define LACP_STATE_LACP_TIMEOUT    0x2
+#define LACP_STATE_AGGREGATION     0x4
+#define LACP_STATE_SYNCHRONIZATION 0x8
+#define LACP_STATE_COLLECTING      0x10
+#define LACP_STATE_DISTRIBUTING    0x20
+#define LACP_STATE_DEFAULTED       0x40
+#define LACP_STATE_EXPIRED         0x80
+
 typedef struct ifbond {
        __s32 bond_mode;
        __s32 num_slaves;
index 1b3c2b643a02e611a8e45dadc46db1ef688a5dd9..42f7ca38ad80e2d93c4143945c9ab386aaa7a1a4 100644 (file)
@@ -130,6 +130,7 @@ enum {
 #define BRIDGE_VLAN_INFO_RANGE_BEGIN   (1<<3) /* VLAN is start of vlan range */
 #define BRIDGE_VLAN_INFO_RANGE_END     (1<<4) /* VLAN is end of vlan range */
 #define BRIDGE_VLAN_INFO_BRENTRY       (1<<5) /* Global bridge VLAN entry */
+#define BRIDGE_VLAN_INFO_ONLY_OPTS     (1<<6) /* Skip create/delete/flags */
 
 struct bridge_vlan_info {
        __u16 flags;
@@ -156,6 +157,45 @@ struct bridge_vlan_xstats {
        __u32 pad2;
 };
 
+struct bridge_stp_xstats {
+       __u64 transition_blk;
+       __u64 transition_fwd;
+       __u64 rx_bpdu;
+       __u64 tx_bpdu;
+       __u64 rx_tcn;
+       __u64 tx_tcn;
+};
+
+/* Bridge vlan RTM header */
+struct br_vlan_msg {
+       __u8 family;
+       __u8 reserved1;
+       __u16 reserved2;
+       __u32 ifindex;
+};
+
+/* Bridge vlan RTM attributes
+ * [BRIDGE_VLANDB_ENTRY] = {
+ *     [BRIDGE_VLANDB_ENTRY_INFO]
+ *     ...
+ * }
+ */
+enum {
+       BRIDGE_VLANDB_UNSPEC,
+       BRIDGE_VLANDB_ENTRY,
+       __BRIDGE_VLANDB_MAX,
+};
+#define BRIDGE_VLANDB_MAX (__BRIDGE_VLANDB_MAX - 1)
+
+enum {
+       BRIDGE_VLANDB_ENTRY_UNSPEC,
+       BRIDGE_VLANDB_ENTRY_INFO,
+       BRIDGE_VLANDB_ENTRY_RANGE,
+       BRIDGE_VLANDB_ENTRY_STATE,
+       __BRIDGE_VLANDB_ENTRY_MAX,
+};
+#define BRIDGE_VLANDB_ENTRY_MAX (__BRIDGE_VLANDB_ENTRY_MAX - 1)
+
 /* Bridge multicast database attributes
  * [MDBA_MDB] = {
  *     [MDBA_MDB_ENTRY] = {
@@ -262,6 +302,7 @@ enum {
        BRIDGE_XSTATS_VLAN,
        BRIDGE_XSTATS_MCAST,
        BRIDGE_XSTATS_PAD,
+       BRIDGE_XSTATS_STP,
        __BRIDGE_XSTATS_MAX
 };
 #define BRIDGE_XSTATS_MAX (__BRIDGE_XSTATS_MAX - 1)
index 8aec8769d9442d3eadbeb441cb987d35897c4882..024af2d1d0af4059cba0c40211b1095ecd55387e 100644 (file)
@@ -169,6 +169,7 @@ enum {
        IFLA_MAX_MTU,
        IFLA_PROP_LIST,
        IFLA_ALT_IFNAME, /* Alternative ifname */
+       IFLA_PERM_ADDRESS,
        __IFLA_MAX
 };
 
@@ -485,6 +486,13 @@ enum macsec_validation_type {
        MACSEC_VALIDATE_MAX = __MACSEC_VALIDATE_END - 1,
 };
 
+enum macsec_offload {
+       MACSEC_OFFLOAD_OFF = 0,
+       MACSEC_OFFLOAD_PHY = 1,
+       __MACSEC_OFFLOAD_END,
+       MACSEC_OFFLOAD_MAX = __MACSEC_OFFLOAD_END - 1,
+};
+
 /* IPVLAN section */
 enum {
        IFLA_IPVLAN_UNSPEC,
index 98e4d5d7c45ca7686b8506927294998980315bb7..1d63c43c38ccaf91067e18c9d6f0bd98c9f9ba1e 100644 (file)
@@ -45,6 +45,7 @@ enum macsec_attrs {
        MACSEC_ATTR_RXSC_LIST,   /* dump, nested, macsec_rxsc_attrs for each RXSC */
        MACSEC_ATTR_TXSC_STATS,  /* dump, nested, macsec_txsc_stats_attr */
        MACSEC_ATTR_SECY_STATS,  /* dump, nested, macsec_secy_stats_attr */
+       MACSEC_ATTR_OFFLOAD,     /* config, nested, macsec_offload_attrs */
        __MACSEC_ATTR_END,
        NUM_MACSEC_ATTR = __MACSEC_ATTR_END,
        MACSEC_ATTR_MAX = __MACSEC_ATTR_END - 1,
@@ -97,6 +98,15 @@ enum macsec_sa_attrs {
        MACSEC_SA_ATTR_MAX = __MACSEC_SA_ATTR_END - 1,
 };
 
+enum macsec_offload_attrs {
+       MACSEC_OFFLOAD_ATTR_UNSPEC,
+       MACSEC_OFFLOAD_ATTR_TYPE, /* config/dump, u8 0..2 */
+       MACSEC_OFFLOAD_ATTR_PAD,
+       __MACSEC_OFFLOAD_ATTR_END,
+       NUM_MACSEC_OFFLOAD_ATTR = __MACSEC_OFFLOAD_ATTR_END,
+       MACSEC_OFFLOAD_ATTR_MAX = __MACSEC_OFFLOAD_ATTR_END - 1,
+};
+
 enum macsec_nl_commands {
        MACSEC_CMD_GET_TXSC,
        MACSEC_CMD_ADD_RXSC,
@@ -108,6 +118,7 @@ enum macsec_nl_commands {
        MACSEC_CMD_ADD_RXSA,
        MACSEC_CMD_DEL_RXSA,
        MACSEC_CMD_UPD_RXSA,
+       MACSEC_CMD_UPD_OFFLOAD,
 };
 
 /* u64 per-RXSC stats */
index e7ad9d350a283d81e89696e1bc42438030accc90..1521073b634800f5ee1c01e08ff86742dab0d2f5 100644 (file)
@@ -76,6 +76,8 @@ enum {
 #define IPPROTO_MPLS           IPPROTO_MPLS
   IPPROTO_RAW = 255,           /* Raw IP packets                       */
 #define IPPROTO_RAW            IPPROTO_RAW
+  IPPROTO_MPTCP = 262,         /* Multipath TCP connection             */
+#define IPPROTO_MPTCP          IPPROTO_MPTCP
   IPPROTO_MAX
 };
 #endif
index 9f1a728762124c59f366aa80c3e69ce78049aaf4..bbe791b241682fafddfac2d40aaad2bc789eebe8 100644 (file)
@@ -971,6 +971,37 @@ struct tc_pie_xstats {
        __u32 ecn_mark;                 /* packets marked with ecn*/
 };
 
+/* FQ PIE */
+enum {
+       TCA_FQ_PIE_UNSPEC,
+       TCA_FQ_PIE_LIMIT,
+       TCA_FQ_PIE_FLOWS,
+       TCA_FQ_PIE_TARGET,
+       TCA_FQ_PIE_TUPDATE,
+       TCA_FQ_PIE_ALPHA,
+       TCA_FQ_PIE_BETA,
+       TCA_FQ_PIE_QUANTUM,
+       TCA_FQ_PIE_MEMORY_LIMIT,
+       TCA_FQ_PIE_ECN_PROB,
+       TCA_FQ_PIE_ECN,
+       TCA_FQ_PIE_BYTEMODE,
+       TCA_FQ_PIE_DQ_RATE_ESTIMATOR,
+       __TCA_FQ_PIE_MAX
+};
+#define TCA_FQ_PIE_MAX   (__TCA_FQ_PIE_MAX - 1)
+
+struct tc_fq_pie_xstats {
+       __u32 packets_in;       /* total number of packets enqueued */
+       __u32 dropped;          /* packets dropped due to fq_pie_action */
+       __u32 overlimit;        /* dropped due to lack of space in queue */
+       __u32 overmemory;       /* dropped due to lack of memory in queue */
+       __u32 ecn_mark;         /* packets marked with ecn */
+       __u32 new_flow_count;   /* count of new flows created by packets */
+       __u32 new_flows_len;    /* count of flows in new list */
+       __u32 old_flows_len;    /* count of flows in old list */
+       __u32 memory_usage;     /* total memory across all queues */
+};
+
 /* CBS */
 struct tc_cbs_qopt {
        __u8 offload;
@@ -1187,4 +1218,21 @@ enum {
 
 #define TCA_TAPRIO_ATTR_MAX (__TCA_TAPRIO_ATTR_MAX - 1)
 
+/* ETS */
+
+#define TCQ_ETS_MAX_BANDS 16
+
+enum {
+       TCA_ETS_UNSPEC,
+       TCA_ETS_NBANDS,         /* u8 */
+       TCA_ETS_NSTRICT,        /* u8 */
+       TCA_ETS_QUANTA,         /* nested TCA_ETS_QUANTA_BAND */
+       TCA_ETS_QUANTA_BAND,    /* u32 */
+       TCA_ETS_PRIOMAP,        /* nested TCA_ETS_PRIOMAP_BAND */
+       TCA_ETS_PRIOMAP_BAND,   /* u8 */
+       __TCA_ETS_MAX,
+};
+
+#define TCA_ETS_MAX (__TCA_ETS_MAX - 1)
+
 #endif
index 1418a8362bb7548a8cd0a25c77ae78e8d2d6a389..4a8c5b7451570d9e5cce44830e208334675583de 100644 (file)
@@ -171,6 +171,13 @@ enum {
        RTM_GETLINKPROP,
 #define RTM_GETLINKPROP        RTM_GETLINKPROP
 
+       RTM_NEWVLAN = 112,
+#define RTM_NEWNVLAN   RTM_NEWVLAN
+       RTM_DELVLAN,
+#define RTM_DELVLAN    RTM_DELVLAN
+       RTM_GETVLAN,
+#define RTM_GETVLAN    RTM_GETVLAN
+
        __RTM_MAX,
 #define RTM_MAX                (((__RTM_MAX + 3) & ~3) - 1)
 };
@@ -309,6 +316,8 @@ enum rt_scope_t {
 #define RTM_F_PREFIX           0x800   /* Prefix addresses             */
 #define RTM_F_LOOKUP_TABLE     0x1000  /* set rtm_table to FIB lookup result */
 #define RTM_F_FIB_MATCH                0x2000  /* return full fib lookup match */
+#define RTM_F_OFFLOAD          0x4000  /* route is offloaded */
+#define RTM_F_TRAP             0x8000  /* route is trapping packets */
 
 /* Reserved table identifiers */
 
@@ -721,6 +730,8 @@ enum rtnetlink_groups {
 #define RTNLGRP_IPV6_MROUTE_R  RTNLGRP_IPV6_MROUTE_R
        RTNLGRP_NEXTHOP,
 #define RTNLGRP_NEXTHOP                RTNLGRP_NEXTHOP
+       RTNLGRP_BRVLAN,
+#define RTNLGRP_BRVLAN         RTNLGRP_BRVLAN
        __RTNLGRP_MAX
 };
 #define RTNLGRP_MAX    (__RTNLGRP_MAX - 1)
index 96151ffbf8cc5c13058f0e0149cb8e60a9946f93..8e6a12b602fc3f4a363da9fe73df07862b40e14d 100644 (file)
@@ -254,6 +254,21 @@ bool locale_is_valid(const char *name) {
         return true;
 }
 
+int locale_is_installed(const char *name) {
+        if (!locale_is_valid(name))
+                return false;
+
+        if (STR_IN_SET(name, "C", "POSIX")) /* These ones are always OK */
+                return true;
+
+        _cleanup_(freelocalep) locale_t loc =
+                newlocale(LC_ALL_MASK, name, 0);
+        if (loc == (locale_t) 0)
+                return errno == ENOMEM ? -ENOMEM : false;
+
+        return true;
+}
+
 void init_gettext(void) {
         setlocale(LC_ALL, "");
         textdomain(GETTEXT_PACKAGE);
@@ -305,7 +320,7 @@ out:
         return (bool) cached_answer;
 }
 
-static bool emoji_enabled(void) {
+bool emoji_enabled(void) {
         static int cached_emoji_enabled = -1;
 
         if (cached_emoji_enabled < 0) {
@@ -350,6 +365,7 @@ const char *special_glyph(SpecialGlyph code) {
                         [SPECIAL_GLYPH_SIGMA]                   = "S",
                         [SPECIAL_GLYPH_ARROW]                   = "->",
                         [SPECIAL_GLYPH_ELLIPSIS]                = "...",
+                        [SPECIAL_GLYPH_EXTERNAL_LINK]           = "[LNK]",
                         [SPECIAL_GLYPH_ECSTATIC_SMILEY]         = ":-]",
                         [SPECIAL_GLYPH_HAPPY_SMILEY]            = ":-}",
                         [SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY]   = ":-)",
@@ -357,6 +373,8 @@ const char *special_glyph(SpecialGlyph code) {
                         [SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY] = ":-(",
                         [SPECIAL_GLYPH_UNHAPPY_SMILEY]          = ":-{",
                         [SPECIAL_GLYPH_DEPRESSED_SMILEY]        = ":-[",
+                        [SPECIAL_GLYPH_LOCK_AND_KEY]            = "o-,",
+                        [SPECIAL_GLYPH_TOUCH]                   = "O=",    /* Yeah, not very convincing, can you do it better? */
                 },
 
                 /* UTF-8 */
@@ -384,6 +402,9 @@ const char *special_glyph(SpecialGlyph code) {
                         /* Single glyph in Unicode, three in ASCII */
                         [SPECIAL_GLYPH_ELLIPSIS]                = "\342\200\246",             /* … (actually called: HORIZONTAL ELLIPSIS) */
 
+                        /* Three glyphs in Unicode, five in ASCII */
+                        [SPECIAL_GLYPH_EXTERNAL_LINK]           = "[\360\237\241\225]",       /* 🡕 (actually called: NORTH EAST SANS-SERIF ARROW, enclosed in []) */
+
                         /* These smileys are a single glyph in Unicode, and three in ASCII */
                         [SPECIAL_GLYPH_ECSTATIC_SMILEY]         = "\360\237\230\207",         /* 😇 (actually called: SMILING FACE WITH HALO) */
                         [SPECIAL_GLYPH_HAPPY_SMILEY]            = "\360\237\230\200",         /* 😀 (actually called: GRINNING FACE) */
@@ -392,12 +413,18 @@ const char *special_glyph(SpecialGlyph code) {
                         [SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY] = "\360\237\231\201",         /* 🙁 (actually called: SLIGHTLY FROWNING FACE) */
                         [SPECIAL_GLYPH_UNHAPPY_SMILEY]          = "\360\237\230\250",         /* 😨 (actually called: FEARFUL FACE) */
                         [SPECIAL_GLYPH_DEPRESSED_SMILEY]        = "\360\237\244\242",         /* 🤢 (actually called: NAUSEATED FACE) */
+
+                        /* This emoji is a single character cell glyph in Unicode, and three in ASCII */
+                        [SPECIAL_GLYPH_LOCK_AND_KEY]            = "\360\237\224\220",         /* 🔐 (actually called: CLOSED LOCK WITH KEY) */
+
+                        /* This emoji is a single character cell glyph in Unicode, and two in ASCII */
+                        [SPECIAL_GLYPH_TOUCH]                   = "\360\237\221\206",         /* 👆 (actually called: BACKHAND INDEX POINTING UP */
                 },
         };
 
         assert(code < _SPECIAL_GLYPH_MAX);
 
-        return draw_table[code >= _SPECIAL_GLYPH_FIRST_SMILEY ? emoji_enabled() : is_locale_utf8()][code];
+        return draw_table[code >= _SPECIAL_GLYPH_FIRST_EMOJI ? emoji_enabled() : is_locale_utf8()][code];
 }
 
 void locale_variables_free(char *l[_VARIABLE_LC_MAX]) {
index cefc4e7f0ed0ea1493b8f6ed881b8b427efe71e2..aa25e17f154596ce6803efbd6d1e4f0bc6bbb762 100644 (file)
@@ -31,6 +31,7 @@ typedef enum LocaleVariable {
 
 int get_locales(char ***l);
 bool locale_is_valid(const char *name);
+int locale_is_installed(const char *name);
 
 #define _(String) gettext(String)
 #define N_(String) String
@@ -54,19 +55,24 @@ typedef enum {
         SPECIAL_GLYPH_LIGHT_SHADE,
         SPECIAL_GLYPH_DARK_SHADE,
         SPECIAL_GLYPH_SIGMA,
-        _SPECIAL_GLYPH_FIRST_SMILEY,
-        SPECIAL_GLYPH_ECSTATIC_SMILEY = _SPECIAL_GLYPH_FIRST_SMILEY,
+        SPECIAL_GLYPH_EXTERNAL_LINK,
+        _SPECIAL_GLYPH_FIRST_EMOJI,
+        SPECIAL_GLYPH_ECSTATIC_SMILEY = _SPECIAL_GLYPH_FIRST_EMOJI,
         SPECIAL_GLYPH_HAPPY_SMILEY,
         SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY,
         SPECIAL_GLYPH_NEUTRAL_SMILEY,
         SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY,
         SPECIAL_GLYPH_UNHAPPY_SMILEY,
         SPECIAL_GLYPH_DEPRESSED_SMILEY,
-        _SPECIAL_GLYPH_MAX
+        SPECIAL_GLYPH_LOCK_AND_KEY,
+        SPECIAL_GLYPH_TOUCH,
+        _SPECIAL_GLYPH_MAX,
 } SpecialGlyph;
 
 const char *special_glyph(SpecialGlyph code) _const_;
 
+bool emoji_enabled(void);
+
 const char* locale_variable_to_string(LocaleVariable i) _const_;
 LocaleVariable locale_variable_from_string(const char *s) _pure_;
 
index 17557e1844b2a0e9fecf94d9b8ebd06257b4d843..c6fe20380830b9e6ff78ac869b392c213798565e 100644 (file)
@@ -7,6 +7,7 @@
 #include <stdarg.h>
 #include <stddef.h>
 #include <sys/signalfd.h>
+#include <sys/stat.h>
 #include <sys/time.h>
 #include <sys/uio.h>
 #include <sys/un.h>
@@ -51,6 +52,7 @@ static bool syslog_is_stream = false;
 
 static bool show_color = false;
 static bool show_location = false;
+static bool show_time = false;
 
 static bool upgrade_syslog_to_journal = false;
 static bool always_reopen_console = false;
@@ -218,6 +220,32 @@ fail:
         return r;
 }
 
+static bool stderr_is_journal(void) {
+        _cleanup_free_ char *w = NULL;
+        const char *e;
+        uint64_t dev, ino;
+        struct stat st;
+
+        e = getenv("JOURNAL_STREAM");
+        if (!e)
+                return false;
+
+        if (extract_first_word(&e, &w, ":", EXTRACT_DONT_COALESCE_SEPARATORS) <= 0)
+                return false;
+        if (!e)
+                return false;
+
+        if (safe_atou64(w, &dev) < 0)
+                return false;
+        if (safe_atou64(e, &ino) < 0)
+                return false;
+
+        if (fstat(STDERR_FILENO, &st) < 0)
+                return false;
+
+        return st.st_dev == dev && st.st_ino == ino;
+}
+
 int log_open(void) {
         int r;
 
@@ -237,9 +265,7 @@ int log_open(void) {
                 return 0;
         }
 
-        if (log_target != LOG_TARGET_AUTO ||
-            getpid_cached() == 1 ||
-            isatty(STDERR_FILENO) <= 0) {
+        if (log_target != LOG_TARGET_AUTO || getpid_cached() == 1 || stderr_is_journal()) {
 
                 if (!prohibit_ipc &&
                     IN_SET(log_target, LOG_TARGET_AUTO,
@@ -332,8 +358,10 @@ static int write_to_console(
                 const char *func,
                 const char *buffer) {
 
-        char location[256], prefix[1 + DECIMAL_STR_MAX(int) + 2];
-        struct iovec iovec[6] = {};
+        char location[256],
+             header_time[FORMAT_TIMESTAMP_MAX],
+             prefix[1 + DECIMAL_STR_MAX(int) + 2];
+        struct iovec iovec[8] = {};
         const char *on = NULL, *off = NULL;
         size_t n = 0;
 
@@ -345,6 +373,13 @@ static int write_to_console(
                 iovec[n++] = IOVEC_MAKE_STRING(prefix);
         }
 
+        if (show_time) {
+                if (format_timestamp(header_time, sizeof(header_time), now(CLOCK_REALTIME))) {
+                        iovec[n++] = IOVEC_MAKE_STRING(header_time);
+                        iovec[n++] = IOVEC_MAKE_STRING(" ");
+                }
+        }
+
         if (show_color)
                 get_log_colors(LOG_PRI(level), &on, &off, NULL);
 
@@ -1099,22 +1134,32 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
 
                 if (log_show_location_from_string(value ?: "1") < 0)
                         log_warning("Failed to parse log location setting '%s'. Ignoring.", value);
+
+        } else if (proc_cmdline_key_streq(key, "systemd.log_time")) {
+
+                if (log_show_time_from_string(value ?: "1") < 0)
+                        log_warning("Failed to parse log time setting '%s'. Ignoring.", value);
+
         }
 
         return 0;
 }
 
 void log_parse_environment_realm(LogRealm realm) {
-        /* Do not call from library code. */
-
-        const char *e;
-
         if (getpid_cached() == 1 || get_ctty_devnr(0, NULL) < 0)
                 /* Only try to read the command line in daemons. We assume that anything that has a
                  * controlling tty is user stuff. For PID1 we do a special check in case it hasn't
                  * closed the console yet. */
                 (void) proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
 
+        log_parse_environment_cli_realm(realm);
+}
+
+void log_parse_environment_cli_realm(LogRealm realm) {
+        /* Do not call from library code. */
+
+        const char *e;
+
         e = getenv("SYSTEMD_LOG_TARGET");
         if (e && log_set_target_from_string(e) < 0)
                 log_warning("Failed to parse log target '%s'. Ignoring.", e);
@@ -1130,6 +1175,10 @@ void log_parse_environment_realm(LogRealm realm) {
         e = getenv("SYSTEMD_LOG_LOCATION");
         if (e && log_show_location_from_string(e) < 0)
                 log_warning("Failed to parse log location '%s'. Ignoring.", e);
+
+        e = getenv("SYSTEMD_LOG_TIME");
+        if (e && log_show_time_from_string(e) < 0)
+                log_warning("Failed to parse log time '%s'. Ignoring.", e);
 }
 
 LogTarget log_get_target(void) {
@@ -1156,6 +1205,14 @@ bool log_get_show_location(void) {
         return show_location;
 }
 
+void log_show_time(bool b) {
+        show_time = b;
+}
+
+bool log_get_show_time(void) {
+        return show_time;
+}
+
 int log_show_color_from_string(const char *e) {
         int t;
 
@@ -1178,6 +1235,17 @@ int log_show_location_from_string(const char *e) {
         return 0;
 }
 
+int log_show_time_from_string(const char *e) {
+        int t;
+
+        t = parse_boolean(e);
+        if (t < 0)
+                return t;
+
+        log_show_time(t);
+        return 0;
+}
+
 bool log_on_console(void) {
         if (IN_SET(log_target, LOG_TARGET_CONSOLE,
                                LOG_TARGET_CONSOLE_PREFIXED))
@@ -1366,3 +1434,11 @@ void log_setup_service(void) {
         log_parse_environment();
         (void) log_open();
 }
+
+void log_setup_cli(void) {
+        /* Sets up logging the way it is most appropriate for running a program as a CLI utility. */
+
+        log_show_color(true);
+        log_parse_environment_cli();
+        (void) log_open();
+}
index 740fdbf6171e7a761c50c9adc1a23e5accc9e6bb..15807d3029f95e76e20da8be0526223094e9b4bf 100644 (file)
@@ -59,9 +59,12 @@ void log_show_color(bool b);
 bool log_get_show_color(void) _pure_;
 void log_show_location(bool b);
 bool log_get_show_location(void) _pure_;
+void log_show_time(bool b);
+bool log_get_show_time(void) _pure_;
 
 int log_show_color_from_string(const char *e);
 int log_show_location_from_string(const char *e);
+int log_show_time_from_string(const char *e);
 
 LogTarget log_get_target(void) _pure_;
 int log_get_max_level_realm(LogRealm realm) _pure_;
@@ -81,8 +84,11 @@ void log_close(void);
 void log_forget_fds(void);
 
 void log_parse_environment_realm(LogRealm realm);
+void log_parse_environment_cli_realm(LogRealm realm);
 #define log_parse_environment() \
         log_parse_environment_realm(LOG_REALM)
+#define log_parse_environment_cli() \
+        log_parse_environment_cli_realm(LOG_REALM)
 
 int log_dispatch_internal(
                 int level,
@@ -338,3 +344,4 @@ int log_syntax_invalid_utf8_internal(
 #define DEBUG_LOGGING _unlikely_(log_get_max_level() >= LOG_DEBUG)
 
 void log_setup_service(void);
+void log_setup_cli(void);
index 5aa7f59c0ba1fc5c0f8391dc71d64d0dbb7d4c63..ceea8176f5b85321e7966b4e747c386f8ed79980 100644 (file)
 #define _variable_no_sanitize_address_
 #endif
 
+/* Apparently there's no has_feature() call defined to check for ubsan, hence let's define this
+ * unconditionally on llvm */
+#if defined(__clang__)
+#define _function_no_sanitize_float_cast_overflow_ __attribute__((no_sanitize("float-cast-overflow")))
+#else
+#define _function_no_sanitize_float_cast_overflow_
+#endif
+
 /* Temporarily disable some warnings */
 #define DISABLE_WARNING_FORMAT_NONLITERAL                               \
         _Pragma("GCC diagnostic push");                                 \
         _Pragma("GCC diagnostic push")
 #endif
 
+#define DISABLE_WARNING_FLOAT_EQUAL \
+        _Pragma("GCC diagnostic push");                                 \
+        _Pragma("GCC diagnostic ignored \"-Wfloat-equal\"")
+
+#define DISABLE_WARNING_TYPE_LIMITS \
+        _Pragma("GCC diagnostic push");                                 \
+        _Pragma("GCC diagnostic ignored \"-Wtype-limits\"")
+
 #define REENABLE_WARNING                                                \
         _Pragma("GCC diagnostic pop")
 
@@ -270,6 +286,15 @@ static inline size_t GREEDY_ALLOC_ROUND_UP(size_t l) {
                 UNIQ_T(A, aq) < UNIQ_T(B, bq) ? UNIQ_T(A, aq) : UNIQ_T(B, bq); \
         })
 
+/* evaluates to (void) if _A or _B are not constant or of different types */
+#define CONST_MIN(_A, _B) \
+        (__builtin_choose_expr(                                         \
+                __builtin_constant_p(_A) &&                             \
+                __builtin_constant_p(_B) &&                             \
+                __builtin_types_compatible_p(typeof(_A), typeof(_B)),   \
+                ((_A) < (_B)) ? (_A) : (_B),                            \
+                VOID_0))
+
 #define MIN3(x, y, z)                                   \
         ({                                              \
                 const typeof(x) _c = MIN(x, y);         \
@@ -432,6 +457,8 @@ static inline int __coverity_check_and_return__(int condition) {
 
 #define char_array_0(x) x[sizeof(x)-1] = 0;
 
+#define sizeof_field(struct_type, member) sizeof(((struct_type *) 0)->member)
+
 /* Returns the number of chars needed to format variables of the
  * specified type as a decimal string. Adds in extra space for a
  * negative '-' prefix (hence works correctly on signed
@@ -451,8 +478,10 @@ static inline int __coverity_check_and_return__(int condition) {
                 ans;                                    \
         })
 
+#define UPDATE_FLAG(orig, flag, b)                      \
+        ((b) ? ((orig) | (flag)) : ((orig) & ~(flag)))
 #define SET_FLAG(v, flag, b) \
-        (v) = (b) ? ((v) | (flag)) : ((v) & ~(flag))
+        (v) = UPDATE_FLAG(v, flag, b)
 #define FLAGS_SET(v, flags) \
         ((~(v) & (flags)) == 0)
 
@@ -509,6 +538,12 @@ static inline int __coverity_check_and_return__(int condition) {
                 (y) = (_t);                        \
         } while (false)
 
+/* Iterates through a specified list of pointers. Accepts NULL pointers, but uses (void*) -1 as internal marker for EOL. */
+#define FOREACH_POINTER(p, x, ...)                                                      \
+        for (typeof(p) *_l = (typeof(p)[]) { ({ p = x; }), ##__VA_ARGS__, (void*) -1 }; \
+             p != (typeof(p)) (void*) -1;                                               \
+             p = *(++_l))
+
 /* Define C11 thread_local attribute even on older gcc compiler
  * version */
 #ifndef thread_local
@@ -583,4 +618,17 @@ static inline int __coverity_check_and_return__(int condition) {
         DEFINE_PUBLIC_TRIVIAL_REF_FUNC(type, name);                    \
         DEFINE_PUBLIC_TRIVIAL_UNREF_FUNC(type, name, free_func);
 
+/* A macro to force copying of a variable from memory. This is useful whenever we want to read something from
+ * memory and want to make sure the compiler won't optimize away the destination variable for us. It's not
+ * supposed to be a full CPU memory barrier, i.e. CPU is still allowed to reorder the reads, but it is not
+ * allowed to remove our local copies of the variables. We want this to work for unaligned memory, hence
+ * memcpy() is great for our purposes. */
+#define READ_NOW(x)                                                     \
+        ({                                                              \
+                typeof(x) _copy;                                        \
+                memcpy(&_copy, &(x), sizeof(_copy));                    \
+                asm volatile ("" : : : "memory");                       \
+                _copy;                                                  \
+        })
+
 #include "log.h"
index b7e2e67e84c287f63a87d66ebe1dc4d39ff5c6d7..4f596cffb75bc2d9fd28c94df8b5d9020bcbddd3 100644 (file)
@@ -12,7 +12,8 @@
 
 size_t page_size(void) _pure_;
 #define PAGE_ALIGN(l) ALIGN_TO((l), page_size())
-#define PAGE_ALIGN_DOWN(l) (l & ~(page_size() - 1))
+#define PAGE_ALIGN_DOWN(l) ((l) & ~(page_size() - 1))
+#define PAGE_OFFSET(l) ((l) & (page_size() - 1))
 
 /* Normal memcpy requires src to be nonnull. We do nothing if n is 0. */
 static inline void memcpy_safe(void *dst, const void *src, size_t n) {
index ccb22e159505a1cfb4fd5dddf13057055f6cc312..90924d6cb895525e1becade481fff0eb5a6600b6 100644 (file)
@@ -39,6 +39,7 @@ basic_sources = files('''
         device-nodes.h
         dirent-util.c
         dirent-util.h
+        dlfcn-util.h
         efivars.c
         efivars.h
         env-file.c
@@ -159,6 +160,8 @@ basic_sources = files('''
         ordered-set.h
         parse-util.c
         parse-util.h
+        path-lookup.c
+        path-lookup.h
         path-util.c
         path-util.h
         prioq.c
@@ -169,6 +172,7 @@ basic_sources = files('''
         process-util.h
         procfs-util.c
         procfs-util.h
+        pthread-util.h
         quota-util.c
         quota-util.h
         random-util.c
@@ -325,7 +329,8 @@ libbasic = static_library(
                         threads,
                         libcap,
                         libselinux,
-                        libm],
+                        libm,
+                        libdl],
         c_args : ['-fvisibility=default'],
         install : false)
 
index 2e76031b325c5e3904413a953349ed75e9a61f22..17af87a3aefe9b77f88866552d1f0cb2fadcdf74 100644 (file)
@@ -14,3 +14,7 @@
 #ifndef GRND_RANDOM
 #define GRND_RANDOM 0x0002
 #endif
+
+#ifndef GRND_INSECURE
+#define GRND_INSECURE 0x0004
+#endif
index 276be366c3d742ddbee34a15a53f141410a7e061..8c3fec1e350a53bf40d0eb67e41e41ed89f29422 100644 (file)
@@ -62,3 +62,8 @@ struct sockaddr_vm {
 #ifndef IP_TRANSPARENT
 #define IP_TRANSPARENT 19
 #endif
+
+/* linux/sockios.h */
+#ifndef SIOCGSKNS
+#define SIOCGSKNS 0x894C
+#endif
index 0eba7fc514b2cea8ff7eedacd6e4fe126bee826c..e844a598060d3e6485ecd126e1e9378dee3d5166 100644 (file)
@@ -10,6 +10,7 @@
 #include "mkdir.h"
 #include "selinux-util.h"
 #include "smack-util.h"
+#include "user-util.h"
 
 int mkdir_label(const char *path, mode_t mode) {
         int r;
@@ -50,9 +51,9 @@ int mkdir_safe_label(const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirF
 }
 
 int mkdir_parents_label(const char *path, mode_t mode) {
-        return mkdir_parents_internal(NULL, path, mode, mkdir_label);
+        return mkdir_parents_internal(NULL, path, mode, UID_INVALID, UID_INVALID, 0, mkdir_label);
 }
 
 int mkdir_p_label(const char *path, mode_t mode) {
-        return mkdir_p_internal(NULL, path, mode, mkdir_label);
+        return mkdir_p_internal(NULL, path, mode, UID_INVALID, UID_INVALID, 0, mkdir_label);
 }
index fa682d4c438e0abf3040d86ec28d319c8ccf3813..6ebc2b95fd18859556b14cdfb153362bddbbc020 100644 (file)
 #include "stdio-util.h"
 #include "user-util.h"
 
-int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags, mkdir_func_t _mkdir) {
+int mkdir_safe_internal(
+                const char *path,
+                mode_t mode,
+                uid_t uid, gid_t gid,
+                MkdirFlags flags,
+                mkdir_func_t _mkdir) {
+
         struct stat st;
         int r;
 
-        assert(_mkdir != mkdir);
+        assert(path);
+        assert(_mkdir && _mkdir != mkdir);
 
         if (_mkdir(path, mode) >= 0) {
                 r = chmod_and_chown(path, mode, uid, gid);
@@ -44,19 +51,16 @@ int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, Mkd
                         return -errno;
         }
 
-        if (!S_ISDIR(st.st_mode)) {
-                log_full(flags & MKDIR_WARN_MODE ? LOG_WARNING : LOG_DEBUG,
-                         "Path \"%s\" already exists and is not a directory, refusing.", path);
-                return -ENOTDIR;
-        }
+        if (!S_ISDIR(st.st_mode))
+                return log_full_errno(flags & MKDIR_WARN_MODE ? LOG_WARNING : LOG_DEBUG, SYNTHETIC_ERRNO(ENOTDIR),
+                                      "Path \"%s\" already exists and is not a directory, refusing.", path);
         if ((st.st_mode & 0007) > (mode & 0007) ||
             (st.st_mode & 0070) > (mode & 0070) ||
-            (st.st_mode & 0700) > (mode & 0700)) {
-                log_full(flags & MKDIR_WARN_MODE ? LOG_WARNING : LOG_DEBUG,
-                         "Directory \"%s\" already exists, but has mode %04o that is too permissive (%04o was requested), refusing.",
-                         path, st.st_mode & 0777, mode);
-                return -EEXIST;
-        }
+            (st.st_mode & 0700) > (mode & 0700))
+                return log_full_errno(flags & MKDIR_WARN_MODE ? LOG_WARNING : LOG_DEBUG, SYNTHETIC_ERRNO(EEXIST),
+                                      "Directory \"%s\" already exists, but has mode %04o that is too permissive (%04o was requested), refusing.",
+                                      path, st.st_mode & 0777, mode);
+
         if ((uid != UID_INVALID && st.st_uid != uid) ||
             (gid != GID_INVALID && st.st_gid != gid)) {
                 char u[DECIMAL_STR_MAX(uid_t)] = "-", g[DECIMAL_STR_MAX(gid_t)] = "-";
@@ -65,10 +69,9 @@ int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, Mkd
                         xsprintf(u, UID_FMT, uid);
                 if (gid != UID_INVALID)
                         xsprintf(g, GID_FMT, gid);
-                log_full(flags & MKDIR_WARN_MODE ? LOG_WARNING : LOG_DEBUG,
-                         "Directory \"%s\" already exists, but is owned by "UID_FMT":"GID_FMT" (%s:%s was requested), refusing.",
-                         path, st.st_uid, st.st_gid, u, g);
-                return -EEXIST;
+                return log_full_errno(flags & MKDIR_WARN_MODE ? LOG_WARNING : LOG_DEBUG, SYNTHETIC_ERRNO(EEXIST),
+                                      "Directory \"%s\" already exists, but is owned by "UID_FMT":"GID_FMT" (%s:%s was requested), refusing.",
+                                      path, st.st_uid, st.st_gid, u, g);
         }
 
         return 0;
@@ -90,7 +93,7 @@ int mkdir_safe(const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags f
         return mkdir_safe_internal(path, mode, uid, gid, flags, mkdir_errno_wrapper);
 }
 
-int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir) {
+int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags, mkdir_func_t _mkdir) {
         const char *p, *e;
         int r;
 
@@ -133,34 +136,54 @@ int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, mk
                 if (prefix && path_startswith(prefix, t))
                         continue;
 
-                r = _mkdir(t, mode);
-                if (r < 0 && r != -EEXIST)
-                        return r;
+                if (!uid_is_valid(uid) && !gid_is_valid(gid) && flags == 0) {
+                        r = _mkdir(t, mode);
+                        if (r < 0 && r != -EEXIST)
+                                return r;
+                } else {
+                        r = mkdir_safe_internal(t, mode, uid, gid, flags, _mkdir);
+                        if (r < 0 && r != -EEXIST)
+                                return r;
+                }
         }
 }
 
 int mkdir_parents(const char *path, mode_t mode) {
-        return mkdir_parents_internal(NULL, path, mode, mkdir_errno_wrapper);
+        return mkdir_parents_internal(NULL, path, mode, UID_INVALID, UID_INVALID, 0, mkdir_errno_wrapper);
 }
 
-int mkdir_p_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir) {
+int mkdir_parents_safe(const char *prefix, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags) {
+        return mkdir_parents_internal(prefix, path, mode, uid, gid, flags, mkdir_errno_wrapper);
+}
+
+int mkdir_p_internal(const char *prefix, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags, mkdir_func_t _mkdir) {
         int r;
 
         /* Like mkdir -p */
 
         assert(_mkdir != mkdir);
 
-        r = mkdir_parents_internal(prefix, path, mode, _mkdir);
+        r = mkdir_parents_internal(prefix, path, mode, uid, gid, flags, _mkdir);
         if (r < 0)
                 return r;
 
-        r = _mkdir(path, mode);
-        if (r < 0 && (r != -EEXIST || is_dir(path, true) <= 0))
-                return r;
+        if (!uid_is_valid(uid) && !gid_is_valid(gid) && flags == 0) {
+                r = _mkdir(path, mode);
+                if (r < 0 && (r != -EEXIST || is_dir(path, true) <= 0))
+                        return r;
+        } else {
+                r = mkdir_safe_internal(path, mode, uid, gid, flags, _mkdir);
+                if (r < 0 && r != -EEXIST)
+                        return r;
+        }
 
         return 0;
 }
 
 int mkdir_p(const char *path, mode_t mode) {
-        return mkdir_p_internal(NULL, path, mode, mkdir_errno_wrapper);
+        return mkdir_p_internal(NULL, path, mode, UID_INVALID, UID_INVALID, 0, mkdir_errno_wrapper);
+}
+
+int mkdir_p_safe(const char *prefix, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags) {
+        return mkdir_p_internal(prefix, path, mode, uid, gid, flags, mkdir_errno_wrapper);
 }
index eb54853ea78b45dc46d9ab2c8215ebfea3951d39..8bfaaf405bcb888a93611d1b9a00f938560a180d 100644 (file)
@@ -12,15 +12,17 @@ int mkdir_errno_wrapper(const char *pathname, mode_t mode);
 int mkdirat_errno_wrapper(int dirfd, const char *pathname, mode_t mode);
 int mkdir_safe(const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags);
 int mkdir_parents(const char *path, mode_t mode);
+int mkdir_parents_safe(const char *prefix, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags);
 int mkdir_p(const char *path, mode_t mode);
+int mkdir_p_safe(const char *prefix, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags);
 
 /* mandatory access control(MAC) versions */
 int mkdir_safe_label(const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags);
-int mkdir_parents_label(const char *path, mode_t mode);
+int mkdir_parents_label(const char *path, mode_t mod);
 int mkdir_p_label(const char *path, mode_t mode);
 
 /* internally used */
 typedef int (*mkdir_func_t)(const char *pathname, mode_t mode);
 int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags, mkdir_func_t _mkdir);
-int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir);
-int mkdir_p_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir);
+int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags, mkdir_func_t _mkdir);
+int mkdir_p_internal(const char *prefix, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags, mkdir_func_t _mkdir);
index c483b353cde49572a7d9db2b326973a4c0a1677f..df1f0ac34cdde7a78eeed4ecda8390a6476508fe 100644 (file)
@@ -338,6 +338,16 @@ bool fstype_is_api_vfs(const char *fstype) {
                           "tracefs");
 }
 
+bool fstype_is_blockdev_backed(const char *fstype) {
+        const char *x;
+
+        x = startswith(fstype, "fuse.");
+        if (x)
+                fstype = x;
+
+        return !streq(fstype, "9p") && !fstype_is_network(fstype) && !fstype_is_api_vfs(fstype);
+}
+
 bool fstype_is_ro(const char *fstype) {
         /* All Linux file systems that are necessarily read-only */
         return STR_IN_SET(fstype,
index 5398836fed4642f0d49095723075397d4c7a776f..ab4ed193945c74ecad9d55559e75744db71647ee 100644 (file)
@@ -14,6 +14,7 @@ int path_is_mount_point(const char *path, const char *root, int flags);
 
 bool fstype_is_network(const char *fstype);
 bool fstype_is_api_vfs(const char *fstype);
+bool fstype_is_blockdev_backed(const char *fstype);
 bool fstype_is_ro(const char *fsype);
 bool fstype_can_discard(const char *fstype);
 bool fstype_can_uid_gid(const char *fstype);
index b0168ae227caf2fee9344867e05b92f8cc119f60..b34c532604a5fe85b88e6fae3b49f32300d13de6 100644 (file)
@@ -2,6 +2,7 @@
 
 #include <fcntl.h>
 #include <sys/ioctl.h>
+#include <sys/mount.h>
 
 #include "fd-util.h"
 #include "missing_fs.h"
@@ -169,3 +170,16 @@ int fd_is_network_ns(int fd) {
 
         return r == CLONE_NEWNET;
 }
+
+int detach_mount_namespace(void) {
+
+        /* Detaches the mount namespace, disabling propagation from our namespace to the host */
+
+        if (unshare(CLONE_NEWNS) < 0)
+                return -errno;
+
+        if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0)
+                return -errno;
+
+        return 0;
+}
index 8c17ce91b21ca4aa781a7c46239734c4e9be870f..99d9b977edf118614406faa34f23381040a9acf1 100644 (file)
@@ -7,3 +7,5 @@ int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *
 int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd);
 
 int fd_is_network_ns(int fd);
+
+int detach_mount_namespace(void);
index 383a729cab869c834b409656d140d8447d97dfc8..a42a57eb49232c66172637798aa856806aa0a0ef 100644 (file)
@@ -59,7 +59,7 @@ static inline void* ordered_set_steal_first(OrderedSet *s) {
 }
 
 static inline char **ordered_set_get_strv(OrderedSet *s) {
-        return internal_hashmap_get_strv(HASHMAP_BASE((OrderedHashmap*) s));
+        return _hashmap_get_strv(HASHMAP_BASE((OrderedHashmap*) s));
 }
 
 int ordered_set_consume(OrderedSet *s, void *p);
index a092a9c733aae24e0401ba6718b17d73c953e3f0..44f0438cf468f7ec12ac427a9f5560048d638da1 100644 (file)
@@ -24,9 +24,22 @@ int parse_boolean(const char *v) {
         if (!v)
                 return -EINVAL;
 
-        if (streq(v, "1") || strcaseeq(v, "yes") || strcaseeq(v, "y") || strcaseeq(v, "true") || strcaseeq(v, "t") || strcaseeq(v, "on"))
+        if (STRCASE_IN_SET(v,
+                           "1",
+                           "yes",
+                           "y",
+                           "true",
+                           "t",
+                           "on"))
                 return 1;
-        else if (streq(v, "0") || strcaseeq(v, "no") || strcaseeq(v, "n") || strcaseeq(v, "false") || strcaseeq(v, "f") || strcaseeq(v, "off"))
+
+        if (STRCASE_IN_SET(v,
+                           "0",
+                           "no",
+                           "n",
+                           "false",
+                           "f",
+                           "off"))
                 return 0;
 
         return -EINVAL;
diff --git a/src/basic/path-lookup.c b/src/basic/path-lookup.c
new file mode 100644 (file)
index 0000000..52968de
--- /dev/null
@@ -0,0 +1,869 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "alloc-util.h"
+#include "fs-util.h"
+#include "log.h"
+#include "macro.h"
+#include "path-lookup.h"
+#include "path-util.h"
+#include "stat-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "tmpfile-util.h"
+#include "user-util.h"
+
+int xdg_user_runtime_dir(char **ret, const char *suffix) {
+        const char *e;
+        char *j;
+
+        assert(ret);
+        assert(suffix);
+
+        e = getenv("XDG_RUNTIME_DIR");
+        if (!e)
+                return -ENXIO;
+
+        j = strjoin(e, suffix);
+        if (!j)
+                return -ENOMEM;
+
+        *ret = j;
+        return 0;
+}
+
+int xdg_user_config_dir(char **ret, const char *suffix) {
+        const char *e;
+        char *j;
+        int r;
+
+        assert(ret);
+
+        e = getenv("XDG_CONFIG_HOME");
+        if (e)
+                j = strjoin(e, suffix);
+        else {
+                _cleanup_free_ char *home = NULL;
+
+                r = get_home_dir(&home);
+                if (r < 0)
+                        return r;
+
+                j = strjoin(home, "/.config", suffix);
+        }
+
+        if (!j)
+                return -ENOMEM;
+
+        *ret = j;
+        return 0;
+}
+
+int xdg_user_data_dir(char **ret, const char *suffix) {
+        const char *e;
+        char *j;
+        int r;
+
+        assert(ret);
+        assert(suffix);
+
+        /* We don't treat /etc/xdg/systemd here as the spec
+         * suggests because we assume that is a link to
+         * /etc/systemd/ anyway. */
+
+        e = getenv("XDG_DATA_HOME");
+        if (e)
+                j = strjoin(e, suffix);
+        else {
+                _cleanup_free_ char *home = NULL;
+
+                r = get_home_dir(&home);
+                if (r < 0)
+                        return r;
+
+                j = strjoin(home, "/.local/share", suffix);
+        }
+        if (!j)
+                return -ENOMEM;
+
+        *ret = j;
+        return 1;
+}
+
+static const char* const user_data_unit_paths[] = {
+        "/usr/local/lib/systemd/user",
+        "/usr/local/share/systemd/user",
+        USER_DATA_UNIT_DIR,
+        "/usr/lib/systemd/user",
+        "/usr/share/systemd/user",
+        NULL
+};
+
+static const char* const user_config_unit_paths[] = {
+        USER_CONFIG_UNIT_DIR,
+        "/etc/systemd/user",
+        NULL
+};
+
+int xdg_user_dirs(char ***ret_config_dirs, char ***ret_data_dirs) {
+        /* Implement the mechanisms defined in
+         *
+         * http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
+         *
+         * We look in both the config and the data dirs because we
+         * want to encourage that distributors ship their unit files
+         * as data, and allow overriding as configuration.
+         */
+        const char *e;
+        _cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
+
+        e = getenv("XDG_CONFIG_DIRS");
+        if (e)
+                config_dirs = strv_split(e, ":");
+        else
+                config_dirs = strv_new("/etc/xdg");
+        if (!config_dirs)
+                return -ENOMEM;
+
+        e = getenv("XDG_DATA_DIRS");
+        if (e)
+                data_dirs = strv_split(e, ":");
+        else
+                data_dirs = strv_new("/usr/local/share",
+                                     "/usr/share");
+        if (!data_dirs)
+                return -ENOMEM;
+
+        *ret_config_dirs = TAKE_PTR(config_dirs);
+        *ret_data_dirs = TAKE_PTR(data_dirs);
+
+        return 0;
+}
+
+static char** user_dirs(
+                const char *persistent_config,
+                const char *runtime_config,
+                const char *global_persistent_config,
+                const char *global_runtime_config,
+                const char *generator,
+                const char *generator_early,
+                const char *generator_late,
+                const char *transient,
+                const char *persistent_control,
+                const char *runtime_control) {
+
+        _cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
+        _cleanup_free_ char *data_home = NULL;
+        _cleanup_strv_free_ char **res = NULL;
+        int r;
+
+        r = xdg_user_dirs(&config_dirs, &data_dirs);
+        if (r < 0)
+                return NULL;
+
+        r = xdg_user_data_dir(&data_home, "/systemd/user");
+        if (r < 0 && r != -ENXIO)
+                return NULL;
+
+        /* Now merge everything we found. */
+        if (strv_extend(&res, persistent_control) < 0)
+                return NULL;
+
+        if (strv_extend(&res, runtime_control) < 0)
+                return NULL;
+
+        if (strv_extend(&res, transient) < 0)
+                return NULL;
+
+        if (strv_extend(&res, generator_early) < 0)
+                return NULL;
+
+        if (strv_extend_strv_concat(&res, config_dirs, "/systemd/user") < 0)
+                return NULL;
+
+        if (strv_extend(&res, persistent_config) < 0)
+                return NULL;
+
+        /* global config has lower priority than the user config of the same type */
+        if (strv_extend(&res, global_persistent_config) < 0)
+                return NULL;
+
+        if (strv_extend_strv(&res, (char**) user_config_unit_paths, false) < 0)
+                return NULL;
+
+        if (strv_extend(&res, runtime_config) < 0)
+                return NULL;
+
+        if (strv_extend(&res, global_runtime_config) < 0)
+                return NULL;
+
+        if (strv_extend(&res, generator) < 0)
+                return NULL;
+
+        if (strv_extend(&res, data_home) < 0)
+                return NULL;
+
+        if (strv_extend_strv_concat(&res, data_dirs, "/systemd/user") < 0)
+                return NULL;
+
+        if (strv_extend_strv(&res, (char**) user_data_unit_paths, false) < 0)
+                return NULL;
+
+        if (strv_extend(&res, generator_late) < 0)
+                return NULL;
+
+        if (path_strv_make_absolute_cwd(res) < 0)
+                return NULL;
+
+        return TAKE_PTR(res);
+}
+
+bool path_is_user_data_dir(const char *path) {
+        assert(path);
+
+        return strv_contains((char**) user_data_unit_paths, path);
+}
+
+bool path_is_user_config_dir(const char *path) {
+        assert(path);
+
+        return strv_contains((char**) user_config_unit_paths, path);
+}
+
+static int acquire_generator_dirs(
+                UnitFileScope scope,
+                const char *tempdir,
+                char **generator,
+                char **generator_early,
+                char **generator_late) {
+
+        _cleanup_free_ char *x = NULL, *y = NULL, *z = NULL;
+        const char *prefix;
+
+        assert(generator);
+        assert(generator_early);
+        assert(generator_late);
+        assert(IN_SET(scope, UNIT_FILE_SYSTEM, UNIT_FILE_USER, UNIT_FILE_GLOBAL));
+
+        if (scope == UNIT_FILE_GLOBAL)
+                return -EOPNOTSUPP;
+
+        if (tempdir)
+                prefix = tempdir;
+        else if (scope == UNIT_FILE_SYSTEM)
+                prefix = "/run/systemd";
+        else {
+                /* UNIT_FILE_USER */
+                const char *e;
+
+                e = getenv("XDG_RUNTIME_DIR");
+                if (!e)
+                        return -ENXIO;
+
+                prefix = strjoina(e, "/systemd");
+        }
+
+        x = path_join(prefix, "generator");
+        if (!x)
+                return -ENOMEM;
+
+        y = path_join(prefix, "generator.early");
+        if (!y)
+                return -ENOMEM;
+
+        z = path_join(prefix, "generator.late");
+        if (!z)
+                return -ENOMEM;
+
+        *generator = TAKE_PTR(x);
+        *generator_early = TAKE_PTR(y);
+        *generator_late = TAKE_PTR(z);
+
+        return 0;
+}
+
+static int acquire_transient_dir(
+                UnitFileScope scope,
+                const char *tempdir,
+                char **ret) {
+
+        char *transient;
+
+        assert(ret);
+        assert(IN_SET(scope, UNIT_FILE_SYSTEM, UNIT_FILE_USER, UNIT_FILE_GLOBAL));
+
+        if (scope == UNIT_FILE_GLOBAL)
+                return -EOPNOTSUPP;
+
+        if (tempdir)
+                transient = path_join(tempdir, "transient");
+        else if (scope == UNIT_FILE_SYSTEM)
+                transient = strdup("/run/systemd/transient");
+        else
+                return xdg_user_runtime_dir(ret, "/systemd/transient");
+
+        if (!transient)
+                return -ENOMEM;
+        *ret = transient;
+        return 0;
+}
+
+static int acquire_config_dirs(UnitFileScope scope, char **persistent, char **runtime) {
+        _cleanup_free_ char *a = NULL, *b = NULL;
+        int r;
+
+        assert(persistent);
+        assert(runtime);
+
+        switch (scope) {
+
+        case UNIT_FILE_SYSTEM:
+                a = strdup(SYSTEM_CONFIG_UNIT_DIR);
+                b = strdup("/run/systemd/system");
+                break;
+
+        case UNIT_FILE_GLOBAL:
+                a = strdup(USER_CONFIG_UNIT_DIR);
+                b = strdup("/run/systemd/user");
+                break;
+
+        case UNIT_FILE_USER:
+                r = xdg_user_config_dir(&a, "/systemd/user");
+                if (r < 0 && r != -ENXIO)
+                        return r;
+
+                r = xdg_user_runtime_dir(runtime, "/systemd/user");
+                if (r < 0) {
+                        if (r != -ENXIO)
+                                return r;
+
+                        /* If XDG_RUNTIME_DIR is not set, don't consider that fatal, simply initialize the runtime
+                         * directory to NULL */
+                        *runtime = NULL;
+                }
+
+                *persistent = TAKE_PTR(a);
+
+                return 0;
+
+        default:
+                assert_not_reached("Hmm, unexpected scope value.");
+        }
+
+        if (!a || !b)
+                return -ENOMEM;
+
+        *persistent = TAKE_PTR(a);
+        *runtime = TAKE_PTR(b);
+
+        return 0;
+}
+
+static int acquire_control_dirs(UnitFileScope scope, char **persistent, char **runtime) {
+        _cleanup_free_ char *a = NULL;
+        int r;
+
+        assert(persistent);
+        assert(runtime);
+
+        switch (scope) {
+
+        case UNIT_FILE_SYSTEM:  {
+                _cleanup_free_ char *b = NULL;
+
+                a = strdup("/etc/systemd/system.control");
+                if (!a)
+                        return -ENOMEM;
+
+                b = strdup("/run/systemd/system.control");
+                if (!b)
+                        return -ENOMEM;
+
+                *runtime = TAKE_PTR(b);
+
+                break;
+        }
+
+        case UNIT_FILE_USER:
+                r = xdg_user_config_dir(&a, "/systemd/user.control");
+                if (r < 0 && r != -ENXIO)
+                        return r;
+
+                r = xdg_user_runtime_dir(runtime, "/systemd/user.control");
+                if (r < 0) {
+                        if (r != -ENXIO)
+                                return r;
+
+                        /* If XDG_RUNTIME_DIR is not set, don't consider this fatal, simply initialize the directory to
+                         * NULL */
+                        *runtime = NULL;
+                }
+
+                break;
+
+        case UNIT_FILE_GLOBAL:
+                return -EOPNOTSUPP;
+
+        default:
+                assert_not_reached("Hmm, unexpected scope value.");
+        }
+
+        *persistent = TAKE_PTR(a);
+
+        return 0;
+}
+
+static int acquire_attached_dirs(
+                UnitFileScope scope,
+                char **ret_persistent,
+                char **ret_runtime) {
+
+        _cleanup_free_ char *a = NULL, *b = NULL;
+
+        assert(ret_persistent);
+        assert(ret_runtime);
+
+        /* Portable services are not available to regular users for now. */
+        if (scope != UNIT_FILE_SYSTEM)
+                return -EOPNOTSUPP;
+
+        a = strdup("/etc/systemd/system.attached");
+        if (!a)
+                return -ENOMEM;
+
+        b = strdup("/run/systemd/system.attached");
+        if (!b)
+                return -ENOMEM;
+
+        *ret_persistent = TAKE_PTR(a);
+        *ret_runtime = TAKE_PTR(b);
+
+        return 0;
+}
+
+static int patch_root_prefix(char **p, const char *root_dir) {
+        char *c;
+
+        assert(p);
+
+        if (!*p)
+                return 0;
+
+        c = path_join(root_dir, *p);
+        if (!c)
+                return -ENOMEM;
+
+        free_and_replace(*p, c);
+        return 0;
+}
+
+static int patch_root_prefix_strv(char **l, const char *root_dir) {
+        char **i;
+        int r;
+
+        if (!root_dir)
+                return 0;
+
+        STRV_FOREACH(i, l) {
+                r = patch_root_prefix(i, root_dir);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+static int get_paths_from_environ(const char *var, char ***paths, bool *append) {
+        const char *e;
+        int r;
+
+        assert(var);
+        assert(paths);
+        assert(append);
+
+        *append = false;
+
+        e = getenv(var);
+        if (e) {
+                const char *k;
+
+                k = endswith(e, ":");
+                if (k) {
+                        e = strndupa(e, k - e);
+                        *append = true;
+                }
+
+                /* FIXME: empty components in other places should be rejected. */
+
+                r = path_split_and_make_absolute(e, paths);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+int lookup_paths_init(
+                LookupPaths *p,
+                UnitFileScope scope,
+                LookupPathsFlags flags,
+                const char *root_dir) {
+
+        _cleanup_(rmdir_and_freep) char *tempdir = NULL;
+        _cleanup_free_ char
+                *root = NULL,
+                *persistent_config = NULL, *runtime_config = NULL,
+                *global_persistent_config = NULL, *global_runtime_config = NULL,
+                *generator = NULL, *generator_early = NULL, *generator_late = NULL,
+                *transient = NULL,
+                *persistent_control = NULL, *runtime_control = NULL,
+                *persistent_attached = NULL, *runtime_attached = NULL;
+        bool append = false; /* Add items from SYSTEMD_UNIT_PATH before normal directories */
+        _cleanup_strv_free_ char **paths = NULL;
+        int r;
+
+        assert(p);
+        assert(scope >= 0);
+        assert(scope < _UNIT_FILE_SCOPE_MAX);
+
+#if HAVE_SPLIT_USR
+        flags |= LOOKUP_PATHS_SPLIT_USR;
+#endif
+
+        if (!empty_or_root(root_dir)) {
+                if (scope == UNIT_FILE_USER)
+                        return -EINVAL;
+
+                r = is_dir(root_dir, true);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        return -ENOTDIR;
+
+                root = strdup(root_dir);
+                if (!root)
+                        return -ENOMEM;
+        }
+
+        if (flags & LOOKUP_PATHS_TEMPORARY_GENERATED) {
+                r = mkdtemp_malloc("/tmp/systemd-temporary-XXXXXX", &tempdir);
+                if (r < 0)
+                        return log_debug_errno(r, "Failed to create temporary directory: %m");
+        }
+
+        /* Note: when XDG_RUNTIME_DIR is not set this will not return -ENXIO, but simply set runtime_config to NULL */
+        r = acquire_config_dirs(scope, &persistent_config, &runtime_config);
+        if (r < 0)
+                return r;
+
+        if (scope == UNIT_FILE_USER) {
+                r = acquire_config_dirs(UNIT_FILE_GLOBAL, &global_persistent_config, &global_runtime_config);
+                if (r < 0)
+                        return r;
+        }
+
+        if ((flags & LOOKUP_PATHS_EXCLUDE_GENERATED) == 0) {
+                /* Note: if XDG_RUNTIME_DIR is not set, this will fail completely with ENXIO */
+                r = acquire_generator_dirs(scope, tempdir,
+                                           &generator, &generator_early, &generator_late);
+                if (r < 0 && !IN_SET(r, -EOPNOTSUPP, -ENXIO))
+                        return r;
+        }
+
+        /* Note: if XDG_RUNTIME_DIR is not set, this will fail completely with ENXIO */
+        r = acquire_transient_dir(scope, tempdir, &transient);
+        if (r < 0 && !IN_SET(r, -EOPNOTSUPP, -ENXIO))
+                return r;
+
+        /* Note: when XDG_RUNTIME_DIR is not set this will not return -ENXIO, but simply set runtime_control to NULL */
+        r = acquire_control_dirs(scope, &persistent_control, &runtime_control);
+        if (r < 0 && r != -EOPNOTSUPP)
+                return r;
+
+        r = acquire_attached_dirs(scope, &persistent_attached, &runtime_attached);
+        if (r < 0 && r != -EOPNOTSUPP)
+                return r;
+
+        /* First priority is whatever has been passed to us via env vars */
+        r = get_paths_from_environ("SYSTEMD_UNIT_PATH", &paths, &append);
+        if (r < 0)
+                return r;
+
+        if (!paths || append) {
+                /* Let's figure something out. */
+
+                _cleanup_strv_free_ char **add = NULL;
+
+                /* For the user units we include share/ in the search
+                 * path in order to comply with the XDG basedir spec.
+                 * For the system stuff we avoid such nonsense. OTOH
+                 * we include /lib in the search path for the system
+                 * stuff but avoid it for user stuff. */
+
+                switch (scope) {
+
+                case UNIT_FILE_SYSTEM:
+                        add = strv_new(
+                                        /* If you modify this you also want to modify
+                                         * systemdsystemunitpath= in systemd.pc.in! */
+                                        STRV_IFNOTNULL(persistent_control),
+                                        STRV_IFNOTNULL(runtime_control),
+                                        STRV_IFNOTNULL(transient),
+                                        STRV_IFNOTNULL(generator_early),
+                                        persistent_config,
+                                        SYSTEM_CONFIG_UNIT_DIR,
+                                        "/etc/systemd/system",
+                                        STRV_IFNOTNULL(persistent_attached),
+                                        runtime_config,
+                                        "/run/systemd/system",
+                                        STRV_IFNOTNULL(runtime_attached),
+                                        STRV_IFNOTNULL(generator),
+                                        "/usr/local/lib/systemd/system",
+                                        SYSTEM_DATA_UNIT_PATH,
+                                        "/usr/lib/systemd/system",
+                                        STRV_IFNOTNULL(flags & LOOKUP_PATHS_SPLIT_USR ? "/lib/systemd/system" : NULL),
+                                        STRV_IFNOTNULL(generator_late));
+                        break;
+
+                case UNIT_FILE_GLOBAL:
+                        add = strv_new(
+                                        /* If you modify this you also want to modify
+                                         * systemduserunitpath= in systemd.pc.in, and
+                                         * the arrays in user_dirs() above! */
+                                        STRV_IFNOTNULL(persistent_control),
+                                        STRV_IFNOTNULL(runtime_control),
+                                        STRV_IFNOTNULL(transient),
+                                        STRV_IFNOTNULL(generator_early),
+                                        persistent_config,
+                                        USER_CONFIG_UNIT_DIR,
+                                        "/etc/systemd/user",
+                                        runtime_config,
+                                        "/run/systemd/user",
+                                        STRV_IFNOTNULL(generator),
+                                        "/usr/local/share/systemd/user",
+                                        "/usr/share/systemd/user",
+                                        "/usr/local/lib/systemd/user",
+                                        USER_DATA_UNIT_DIR,
+                                        "/usr/lib/systemd/user",
+                                        STRV_IFNOTNULL(generator_late));
+                        break;
+
+                case UNIT_FILE_USER:
+                        add = user_dirs(persistent_config, runtime_config,
+                                        global_persistent_config, global_runtime_config,
+                                        generator, generator_early, generator_late,
+                                        transient,
+                                        persistent_control, runtime_control);
+                        break;
+
+                default:
+                        assert_not_reached("Hmm, unexpected scope?");
+                }
+
+                if (!add)
+                        return -ENOMEM;
+
+                if (paths) {
+                        r = strv_extend_strv(&paths, add, true);
+                        if (r < 0)
+                                return r;
+                } else
+                        /* Small optimization: if paths is NULL (and it usually is), we can simply assign 'add' to it,
+                         * and don't have to copy anything */
+                        paths = TAKE_PTR(add);
+        }
+
+        r = patch_root_prefix(&persistent_config, root);
+        if (r < 0)
+                return r;
+        r = patch_root_prefix(&runtime_config, root);
+        if (r < 0)
+                return r;
+
+        r = patch_root_prefix(&generator, root);
+        if (r < 0)
+                return r;
+        r = patch_root_prefix(&generator_early, root);
+        if (r < 0)
+                return r;
+        r = patch_root_prefix(&generator_late, root);
+        if (r < 0)
+                return r;
+
+        r = patch_root_prefix(&transient, root);
+        if (r < 0)
+                return r;
+
+        r = patch_root_prefix(&persistent_control, root);
+        if (r < 0)
+                return r;
+        r = patch_root_prefix(&runtime_control, root);
+        if (r < 0)
+                return r;
+
+        r = patch_root_prefix(&persistent_attached, root);
+        if (r < 0)
+                return r;
+        r = patch_root_prefix(&runtime_attached, root);
+        if (r < 0)
+                return r;
+
+        r = patch_root_prefix_strv(paths, root);
+        if (r < 0)
+                return -ENOMEM;
+
+        *p = (LookupPaths) {
+                .search_path = strv_uniq(TAKE_PTR(paths)),
+
+                .persistent_config = TAKE_PTR(persistent_config),
+                .runtime_config = TAKE_PTR(runtime_config),
+
+                .generator = TAKE_PTR(generator),
+                .generator_early = TAKE_PTR(generator_early),
+                .generator_late = TAKE_PTR(generator_late),
+
+                .transient = TAKE_PTR(transient),
+
+                .persistent_control = TAKE_PTR(persistent_control),
+                .runtime_control = TAKE_PTR(runtime_control),
+
+                .persistent_attached = TAKE_PTR(persistent_attached),
+                .runtime_attached = TAKE_PTR(runtime_attached),
+
+                .root_dir = TAKE_PTR(root),
+                .temporary_dir = TAKE_PTR(tempdir),
+        };
+
+        return 0;
+}
+
+void lookup_paths_free(LookupPaths *p) {
+        if (!p)
+                return;
+
+        p->search_path = strv_free(p->search_path);
+
+        p->persistent_config = mfree(p->persistent_config);
+        p->runtime_config = mfree(p->runtime_config);
+
+        p->persistent_attached = mfree(p->persistent_attached);
+        p->runtime_attached = mfree(p->runtime_attached);
+
+        p->generator = mfree(p->generator);
+        p->generator_early = mfree(p->generator_early);
+        p->generator_late = mfree(p->generator_late);
+
+        p->transient = mfree(p->transient);
+
+        p->persistent_control = mfree(p->persistent_control);
+        p->runtime_control = mfree(p->runtime_control);
+
+        p->root_dir = mfree(p->root_dir);
+        p->temporary_dir = mfree(p->temporary_dir);
+}
+
+void lookup_paths_log(LookupPaths *p) {
+        assert(p);
+
+        if (strv_isempty(p->search_path)) {
+                log_debug("Ignoring unit files.");
+                p->search_path = strv_free(p->search_path);
+        } else {
+                _cleanup_free_ char *t;
+
+                t = strv_join(p->search_path, "\n\t");
+                log_debug("Looking for unit files in (higher priority first):\n\t%s", strna(t));
+        }
+}
+
+char **generator_binary_paths(UnitFileScope scope) {
+        bool append = false; /* Add items from SYSTEMD_GENERATOR_PATH before normal directories */
+        _cleanup_strv_free_ char **paths = NULL;
+        int r;
+
+        /* First priority is whatever has been passed to us via env vars */
+        r = get_paths_from_environ("SYSTEMD_GENERATOR_PATH", &paths, &append);
+        if (r < 0)
+                return NULL;
+
+        if (!paths || append) {
+                _cleanup_strv_free_ char **add = NULL;
+
+                switch (scope) {
+
+                case UNIT_FILE_SYSTEM:
+                        add = strv_new("/run/systemd/system-generators",
+                                       "/etc/systemd/system-generators",
+                                       "/usr/local/lib/systemd/system-generators",
+                                       SYSTEM_GENERATOR_DIR);
+                        break;
+
+                case UNIT_FILE_GLOBAL:
+                case UNIT_FILE_USER:
+                        add = strv_new("/run/systemd/user-generators",
+                                       "/etc/systemd/user-generators",
+                                       "/usr/local/lib/systemd/user-generators",
+                                       USER_GENERATOR_DIR);
+                        break;
+
+                default:
+                        assert_not_reached("Hmm, unexpected scope.");
+                }
+
+                if (!add)
+                        return NULL;
+
+                if (paths) {
+                        r = strv_extend_strv(&paths, add, true);
+                        if (r < 0)
+                                return NULL;
+                } else
+                        /* Small optimization: if paths is NULL (and it usually is), we can simply assign 'add' to it,
+                         * and don't have to copy anything */
+                        paths = TAKE_PTR(add);
+        }
+
+        return TAKE_PTR(paths);
+}
+
+char **env_generator_binary_paths(bool is_system) {
+        bool append = false; /* Add items from SYSTEMD_ENVIRONMENT_GENERATOR_PATH before normal directories */
+        _cleanup_strv_free_ char **paths = NULL;
+        _cleanup_strv_free_ char **add = NULL;
+        int r;
+
+        /* First priority is whatever has been passed to us via env vars */
+        r = get_paths_from_environ("SYSTEMD_ENVIRONMENT_GENERATOR_PATH", &paths, &append);
+        if (r < 0)
+                return NULL;
+
+        if (!paths || append) {
+                if (is_system)
+                        add = strv_new("/run/systemd/system-environment-generators",
+                                        "/etc/systemd/system-environment-generators",
+                                        "/usr/local/lib/systemd/system-environment-generators",
+                                        SYSTEM_ENV_GENERATOR_DIR);
+                else
+                        add = strv_new("/run/systemd/user-environment-generators",
+                                       "/etc/systemd/user-environment-generators",
+                                       "/usr/local/lib/systemd/user-environment-generators",
+                                       USER_ENV_GENERATOR_DIR);
+
+                if (!add)
+                        return NULL;
+        }
+
+        if (paths) {
+                r = strv_extend_strv(&paths, add, true);
+                if (r < 0)
+                        return NULL;
+        } else
+                /* Small optimization: if paths is NULL (and it usually is), we can simply assign 'add' to it,
+                 * and don't have to copy anything */
+                paths = TAKE_PTR(add);
+
+        return TAKE_PTR(paths);
+}
diff --git a/src/basic/path-lookup.h b/src/basic/path-lookup.h
new file mode 100644 (file)
index 0000000..ae37f9f
--- /dev/null
@@ -0,0 +1,74 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <stdbool.h>
+
+typedef struct LookupPaths LookupPaths;
+
+#include "def.h"
+#include "unit-file.h"
+#include "macro.h"
+
+typedef enum LookupPathsFlags {
+        LOOKUP_PATHS_EXCLUDE_GENERATED   = 1 << 0,
+        LOOKUP_PATHS_TEMPORARY_GENERATED = 1 << 1,
+        LOOKUP_PATHS_SPLIT_USR           = 1 << 2,
+} LookupPathsFlags;
+
+struct LookupPaths {
+        /* Where we look for unit files. This includes the individual special paths below, but also any vendor
+         * supplied, static unit file paths. */
+        char **search_path;
+
+        /* Where we shall create or remove our installation symlinks, aka "configuration", and where the user/admin
+         * shall place his own unit files. */
+        char *persistent_config;
+        char *runtime_config;
+
+        /* Where units from a portable service image shall be placed. */
+        char *persistent_attached;
+        char *runtime_attached;
+
+        /* Where to place generated unit files (i.e. those a "generator" tool generated). Note the special semantics of
+         * this directory: the generators are flushed each time a "systemctl daemon-reload" is issued. The user should
+         * not alter these directories directly. */
+        char *generator;
+        char *generator_early;
+        char *generator_late;
+
+        /* Where to place transient unit files (i.e. those created dynamically via the bus API). Note the special
+         * semantics of this directory: all units created transiently have their unit files removed as the transient
+         * unit is unloaded. The user should not alter this directory directly. */
+        char *transient;
+
+        /* Where the snippets created by "systemctl set-property" are placed. Note that for transient units, the
+         * snippets are placed in the transient directory though (see above). The user should not alter this directory
+         * directly. */
+        char *persistent_control;
+        char *runtime_control;
+
+        /* The root directory prepended to all items above, or NULL */
+        char *root_dir;
+
+        /* A temporary directory when running in test mode, to be nuked */
+        char *temporary_dir;
+};
+
+int lookup_paths_init(LookupPaths *p, UnitFileScope scope, LookupPathsFlags flags, const char *root_dir);
+
+int xdg_user_dirs(char ***ret_config_dirs, char ***ret_data_dirs);
+int xdg_user_runtime_dir(char **ret, const char *suffix);
+int xdg_user_config_dir(char **ret, const char *suffix);
+int xdg_user_data_dir(char **ret, const char *suffix);
+
+bool path_is_user_data_dir(const char *path);
+bool path_is_user_config_dir(const char *path);
+
+void lookup_paths_log(LookupPaths *p);
+void lookup_paths_free(LookupPaths *p);
+
+char **generator_binary_paths(UnitFileScope scope);
+char **env_generator_binary_paths(bool is_system);
+
+#define NETWORK_DIRS ((const char* const*) CONF_PATHS_STRV("systemd/network"))
+#define NETWORK_DIRS_NULSTR CONF_PATHS_NULSTR("systemd/network")
index 986dfe94a4b719b16e13c50b94bf6d041887c42d..c4e022b3a1187806635f771c7825322b42e4913c 100644 (file)
@@ -640,10 +640,8 @@ int find_binary(const char *name, char **ret) {
                 if (access(j, X_OK) >= 0) {
                         /* Found it! */
 
-                        if (ret) {
-                                *ret = path_simplify(j, false);
-                                j = NULL;
-                        }
+                        if (ret)
+                                *ret = path_simplify(TAKE_PTR(j), false);
 
                         return 0;
                 }
index 6b1fb13d00b5b8600a86797c7f38b5a62f802f2e..ba47ca5812bd9b884375f1f09e343721cd01823f 100644 (file)
@@ -118,16 +118,19 @@ int proc_cmdline_parse(proc_cmdline_parse_t parse_item, void *data, ProcCmdlineF
         assert(parse_item);
 
         /* We parse the EFI variable first, because later settings have higher priority. */
-        r = systemd_efi_options_variable(&line);
-        if (r < 0) {
-                if (r != -ENODATA)
-                        log_debug_errno(r, "Failed to get SystemdOptions EFI variable, ignoring: %m");
-        } else {
-                r = proc_cmdline_parse_given(line, parse_item, data, flags);
-                if (r < 0)
-                        return r;
 
-                line = mfree(line);
+        if (!FLAGS_SET(flags, PROC_CMDLINE_IGNORE_EFI_OPTIONS)) {
+                r = systemd_efi_options_variable(&line);
+                if (r < 0) {
+                        if (r != -ENODATA)
+                                log_debug_errno(r, "Failed to get SystemdOptions EFI variable, ignoring: %m");
+                } else {
+                        r = proc_cmdline_parse_given(line, parse_item, data, flags);
+                        if (r < 0)
+                                return r;
+
+                        line = mfree(line);
+                }
         }
 
         r = proc_cmdline(&line);
@@ -220,7 +223,7 @@ static int cmdline_get_key(const char *line, const char *key, ProcCmdlineFlags f
 }
 
 int proc_cmdline_get_key(const char *key, ProcCmdlineFlags flags, char **ret_value) {
-        _cleanup_free_ char *line = NULL;
+        _cleanup_free_ char *line = NULL, *v = NULL;
         int r;
 
         /* Looks for a specific key on the kernel command line and (with lower priority) the EFI variable.
@@ -247,14 +250,27 @@ int proc_cmdline_get_key(const char *key, ProcCmdlineFlags flags, char **ret_val
         if (r < 0)
                 return r;
 
-        r = cmdline_get_key(line, key, flags, ret_value);
-        if (r != 0) /* Either error or true if found. */
+        if (FLAGS_SET(flags, PROC_CMDLINE_IGNORE_EFI_OPTIONS)) /* Shortcut */
+                return cmdline_get_key(line, key, flags, ret_value);
+
+        r = cmdline_get_key(line, key, flags, ret_value ? &v : NULL);
+        if (r < 0)
                 return r;
+        if (r > 0) {
+                if (ret_value)
+                        *ret_value = TAKE_PTR(v);
+
+                return r;
+        }
 
         line = mfree(line);
         r = systemd_efi_options_variable(&line);
-        if (r == -ENODATA)
+        if (r == -ENODATA) {
+                if (ret_value)
+                        *ret_value = NULL;
+
                 return false; /* Not found */
+        }
         if (r < 0)
                 return r;
 
@@ -270,17 +286,17 @@ int proc_cmdline_get_bool(const char *key, bool *ret) {
         r = proc_cmdline_get_key(key, PROC_CMDLINE_VALUE_OPTIONAL, &v);
         if (r < 0)
                 return r;
-        if (r == 0) {
+        if (r == 0) { /* key not specified at all */
                 *ret = false;
                 return 0;
         }
 
-        if (v) { /* parameter passed */
+        if (v) { /* key with parameter passed */
                 r = parse_boolean(v);
                 if (r < 0)
                         return r;
                 *ret = r;
-        } else /* no parameter passed */
+        } else /* key without parameter passed */
                 *ret = true;
 
         return 1;
@@ -288,6 +304,7 @@ int proc_cmdline_get_bool(const char *key, bool *ret) {
 
 int proc_cmdline_get_key_many_internal(ProcCmdlineFlags flags, ...) {
         _cleanup_free_ char *line = NULL;
+        bool processing_efi = true;
         const char *p;
         va_list ap;
         int r, ret = 0;
@@ -298,9 +315,11 @@ int proc_cmdline_get_key_many_internal(ProcCmdlineFlags flags, ...) {
 
         /* This call may clobber arguments on failure! */
 
-        r = proc_cmdline(&line);
-        if (r < 0)
-                return r;
+        if (!FLAGS_SET(flags, PROC_CMDLINE_IGNORE_EFI_OPTIONS)) {
+                r = systemd_efi_options_variable(&line);
+                if (r < 0 && r != -ENODATA)
+                        log_debug_errno(r, "Failed to get SystemdOptions EFI variable, ignoring: %m");
+        }
 
         p = line;
         for (;;) {
@@ -309,8 +328,22 @@ int proc_cmdline_get_key_many_internal(ProcCmdlineFlags flags, ...) {
                 r = proc_cmdline_extract_first(&p, &word, flags);
                 if (r < 0)
                         return r;
-                if (r == 0)
+                if (r == 0) {
+                        /* We finished with this command line. If this was the EFI one, then let's proceed with the regular one */
+                        if (processing_efi) {
+                                processing_efi = false;
+
+                                line = mfree(line);
+                                r = proc_cmdline(&line);
+                                if (r < 0)
+                                        return r;
+
+                                p = line;
+                                continue;
+                        }
+
                         break;
+                }
 
                 va_start(ap, flags);
 
index 4115fdbc99fd7d00dbfc33b19681d16d54efcac6..077d3a99fb91e72fc66aa55eb92a02576358a2d2 100644 (file)
@@ -6,9 +6,10 @@
 #include "log.h"
 
 typedef enum ProcCmdlineFlags {
-        PROC_CMDLINE_STRIP_RD_PREFIX = 1 << 0,
-        PROC_CMDLINE_VALUE_OPTIONAL  = 1 << 1,
-        PROC_CMDLINE_RD_STRICT       = 1 << 2,
+        PROC_CMDLINE_STRIP_RD_PREFIX    = 1 << 0, /* automatically strip "rd." prefix if it is set (and we are in the initrd, since otherwise we'd not consider it anyway) */
+        PROC_CMDLINE_VALUE_OPTIONAL     = 1 << 1, /* the value is optional (for boolean switches that can omit the value) */
+        PROC_CMDLINE_RD_STRICT          = 1 << 2, /* ignore this in the initrd */
+        PROC_CMDLINE_IGNORE_EFI_OPTIONS = 1 << 3, /* don't check systemd's private EFI variable */
 } ProcCmdlineFlags;
 
 typedef int (*proc_cmdline_parse_t)(const char *key, const char *value, void *data);
index 0a1dac758b53d415c0daea6b60d66561503a30ea..80f13048c1065e033daa42c59be79e453d4c318b 100644 (file)
@@ -206,50 +206,12 @@ int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags
         return 0;
 }
 
-int rename_process(const char name[]) {
-        static size_t mm_size = 0;
-        static char *mm = NULL;
-        bool truncated = false;
-        size_t l;
-
-        /* This is a like a poor man's setproctitle(). It changes the comm field, argv[0], and also the glibc's
-         * internally used name of the process. For the first one a limit of 16 chars applies; to the second one in
-         * many cases one of 10 (i.e. length of "/sbin/init") — however if we have CAP_SYS_RESOURCES it is unbounded;
-         * to the third one 7 (i.e. the length of "systemd". If you pass a longer string it will likely be
-         * truncated.
-         *
-         * Returns 0 if a name was set but truncated, > 0 if it was set but not truncated. */
+static int update_argv(const char name[], size_t l) {
+        static int can_do = -1;
 
-        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. 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 >= TASK_COMM_LEN) /* Linux userspace process names can be 15 chars at max */
-                truncated = true;
-
-        /* Second step, change glibc's ID of the process name. */
-        if (program_invocation_name) {
-                size_t k;
-
-                k = strlen(program_invocation_name);
-                strncpy(program_invocation_name, name, k);
-                if (l > k)
-                        truncated = true;
-        }
-
-        /* Third step, completely replace the argv[] array the kernel maintains for us. This requires privileges, but
-         * has the advantage that the argv[] array is exactly what we want it to be, and not filled up with zeros at
-         * the end. This is the best option for changing /proc/self/cmdline. */
+        if (can_do == 0)
+                return 0;
+        can_do = false; /* We'll set it to true only if the whole process works */
 
         /* Let's not bother with this if we don't have euid == 0. Strictly speaking we should check for the
          * CAP_SYS_RESOURCE capability which is independent of the euid. In our own code the capability generally is
@@ -257,22 +219,29 @@ int rename_process(const char name[]) {
          * PR_SET_MM_ARG_{START,END} fails with EPERM later on anyway. After all geteuid() is dead cheap to call, but
          * mmap() is not. */
         if (geteuid() != 0)
-                log_debug("Skipping PR_SET_MM, as we don't have privileges.");
-        else if (mm_size < l+1) {
+                return log_debug_errno(SYNTHETIC_ERRNO(EPERM),
+                                       "Skipping PR_SET_MM, as we don't have privileges.");
+
+        static size_t mm_size = 0;
+        static char *mm = NULL;
+        int r;
+
+        if (mm_size < l+1) {
                 size_t nn_size;
                 char *nn;
 
                 nn_size = PAGE_ALIGN(l+1);
                 nn = mmap(NULL, nn_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
-                if (nn == MAP_FAILED) {
-                        log_debug_errno(errno, "mmap() failed: %m");
-                        goto use_saved_argv;
-                }
+                if (nn == MAP_FAILED)
+                        return log_debug_errno(errno, "mmap() failed: %m");
 
                 strncpy(nn, name, nn_size);
 
                 /* Now, let's tell the kernel about this new memory */
                 if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) {
+                        if (ERRNO_IS_PRIVILEGE(errno))
+                                return log_debug_errno(errno, "PR_SET_MM_ARG_START failed: %m");
+
                         /* HACK: prctl() API is kind of dumb on this point.  The existing end address may already be
                          * below the desired start address, in which case the kernel may have kicked this back due
                          * to a range-check failure (see linux/kernel/sys.c:validate_prctl_map() to see this in
@@ -284,15 +253,13 @@ int rename_process(const char name[]) {
                         log_debug_errno(errno, "PR_SET_MM_ARG_START failed, attempting PR_SET_MM_ARG_END hack: %m");
 
                         if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0) {
-                                log_debug_errno(errno, "PR_SET_MM_ARG_END hack failed, proceeding without: %m");
+                                r = log_debug_errno(errno, "PR_SET_MM_ARG_END hack failed, proceeding without: %m");
                                 (void) munmap(nn, nn_size);
-                                goto use_saved_argv;
+                                return r;
                         }
 
-                        if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) {
-                                log_debug_errno(errno, "PR_SET_MM_ARG_START still failed, proceeding without: %m");
-                                goto use_saved_argv;
-                        }
+                        if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0)
+                                return log_debug_errno(errno, "PR_SET_MM_ARG_START still failed, proceeding without: %m");
                 } else {
                         /* And update the end pointer to the new end, too. If this fails, we don't really know what
                          * to do, it's pretty unlikely that we can rollback, hence we'll just accept the failure,
@@ -314,13 +281,56 @@ int rename_process(const char name[]) {
                         log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m");
         }
 
-use_saved_argv:
+        can_do = true;
+        return 0;
+}
+
+int rename_process(const char name[]) {
+        bool truncated = false;
+
+        /* This is a like a poor man's setproctitle(). It changes the comm field, argv[0], and also the glibc's
+         * internally used name of the process. For the first one a limit of 16 chars applies; to the second one in
+         * many cases one of 10 (i.e. length of "/sbin/init") — however if we have CAP_SYS_RESOURCES it is unbounded;
+         * to the third one 7 (i.e. the length of "systemd". If you pass a longer string it will likely be
+         * truncated.
+         *
+         * Returns 0 if a name was set but truncated, > 0 if it was set but not truncated. */
+
+        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 */
+
+        size_t l = strlen(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 >= TASK_COMM_LEN) /* Linux userspace process names can be 15 chars at max */
+                truncated = true;
+
+        /* Second step, change glibc's ID of the process name. */
+        if (program_invocation_name) {
+                size_t k;
+
+                k = strlen(program_invocation_name);
+                strncpy(program_invocation_name, name, k);
+                if (l > k)
+                        truncated = true;
+        }
+
+        /* Third step, completely replace the argv[] array the kernel maintains for us. This requires privileges, but
+         * has the advantage that the argv[] array is exactly what we want it to be, and not filled up with zeros at
+         * the end. This is the best option for changing /proc/self/cmdline. */
+        (void) update_argv(name, l);
+
         /* Fourth step: in all cases we'll also update the original argv[], so that our own code gets it right too if
          * it still looks here */
-
         if (saved_argc > 0) {
-                int i;
-
                 if (saved_argv[0]) {
                         size_t k;
 
@@ -330,7 +340,7 @@ use_saved_argv:
                                 truncated = true;
                 }
 
-                for (i = 1; i < saved_argc; i++) {
+                for (int i = 1; i < saved_argc; i++) {
                         if (!saved_argv[i])
                                 break;
 
@@ -628,6 +638,23 @@ int get_process_ppid(pid_t pid, pid_t *_ppid) {
         return 0;
 }
 
+int get_process_umask(pid_t pid, mode_t *umask) {
+        _cleanup_free_ char *m = NULL;
+        const char *p;
+        int r;
+
+        assert(umask);
+        assert(pid >= 0);
+
+        p = procfs_file_alloca(pid, "status");
+
+        r = get_proc_field(p, "Umask", WHITESPACE, &m);
+        if (r == -ENOENT)
+                return -ESRCH;
+
+        return parse_mode(m, umask);
+}
+
 int wait_for_terminate(pid_t pid, siginfo_t *status) {
         siginfo_t dummy;
 
@@ -1178,6 +1205,11 @@ int must_be_root(void) {
         return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Need to be root.");
 }
 
+static void restore_sigsetp(sigset_t **ssp) {
+        if (*ssp)
+                (void) sigprocmask(SIG_SETMASK, *ssp, NULL);
+}
+
 int safe_fork_full(
                 const char *name,
                 const int except_fds[],
@@ -1187,7 +1219,8 @@ int safe_fork_full(
 
         pid_t original_pid, pid;
         sigset_t saved_ss, ss;
-        bool block_signals = false;
+        _cleanup_(restore_sigsetp) sigset_t *saved_ssp = NULL;
+        bool block_signals = false, block_all = false;
         int prio, r;
 
         /* A wrapper around fork(), that does a couple of important initializations in addition to mere forking. Always
@@ -1202,7 +1235,7 @@ int safe_fork_full(
                  * be sure that SIGTERMs are not lost we might send to the child. */
 
                 assert_se(sigfillset(&ss) >= 0);
-                block_signals = true;
+                block_signals = block_all = true;
 
         } else if (flags & FORK_WAIT) {
                 /* Let's block SIGCHLD at least, so that we can safely watch for the child process */
@@ -1212,28 +1245,31 @@ int safe_fork_full(
                 block_signals = true;
         }
 
-        if (block_signals)
+        if (block_signals) {
                 if (sigprocmask(SIG_SETMASK, &ss, &saved_ss) < 0)
                         return log_full_errno(prio, errno, "Failed to set signal mask: %m");
+                saved_ssp = &saved_ss;
+        }
 
         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)
+                return log_full_errno(prio, errno, "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) {
+                        if (block_all) {
+                                /* undo everything except SIGCHLD */
+                                ss = saved_ss;
+                                assert_se(sigaddset(&ss, SIGCHLD) >= 0);
+                                (void) sigprocmask(SIG_SETMASK, &ss, NULL);
+                        }
+
                         r = wait_for_terminate_and_check(name, pid, (flags & FORK_LOG ? WAIT_LOG : 0));
                         if (r < 0)
                                 return r;
@@ -1241,9 +1277,6 @@ int safe_fork_full(
                                 return -EPROTO;
                 }
 
-                if (block_signals) /* undo what we did above */
-                        (void) sigprocmask(SIG_SETMASK, &saved_ss, NULL);
-
                 if (ret_pid)
                         *ret_pid = pid;
 
@@ -1252,6 +1285,9 @@ int safe_fork_full(
 
         /* We are in the child process */
 
+        /* Restore signal mask manually */
+        saved_ssp = NULL;
+
         if (flags & FORK_REOPEN_LOG) {
                 /* Close the logs if requested, before we log anything. And make sure we reopen it if needed. */
                 log_close();
@@ -1298,7 +1334,7 @@ int safe_fork_full(
 
                 ppid = getppid();
                 if (ppid == 0)
-                        /* Parent is in a differn't PID namespace. */;
+                        /* Parent is in a different PID namespace. */;
                 else if (ppid != original_pid) {
                         log_debug("Parent died early, raising SIGTERM.");
                         (void) raise(SIGTERM);
index 5116d79669168cad8c154937705939f1aaaa27d2..49bb74ac0ff0c5e026253787628c2d61d8779863 100644 (file)
 #define procfs_file_alloca(pid, field)                                  \
         ({                                                              \
                 pid_t _pid_ = (pid);                                    \
-                const char *_r_;                                        \
+                const char *_field_ = (field);                          \
+                char *_r_;                                              \
                 if (_pid_ == 0) {                                       \
-                        _r_ = ("/proc/self/" field);                    \
+                        _r_ = newa(char, STRLEN("/proc/self/") + strlen(_field_) + 1); \
+                        strcpy(stpcpy(_r_, "/proc/self/"), _field_);    \
                 } else {                                                \
-                        _r_ = newa(char, STRLEN("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + sizeof(field)); \
-                        sprintf((char*) _r_, "/proc/"PID_FMT"/" field, _pid_); \
+                        _r_ = newa(char, STRLEN("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + strlen(_field_) + 1); \
+                        sprintf(_r_, "/proc/" PID_FMT "/%s", _pid_, _field_); \
                 }                                                       \
-                _r_;                                                    \
+                (const char*) _r_;                                      \
         })
 
 typedef enum ProcessCmdlineFlags {
@@ -45,6 +47,7 @@ int get_process_cwd(pid_t pid, char **cwd);
 int get_process_root(pid_t pid, char **root);
 int get_process_environ(pid_t pid, char **environ);
 int get_process_ppid(pid_t pid, pid_t *ppid);
+int get_process_umask(pid_t pid, mode_t *umask);
 
 int wait_for_terminate(pid_t pid, siginfo_t *status);
 
diff --git a/src/basic/pthread-util.h b/src/basic/pthread-util.h
new file mode 100644 (file)
index 0000000..dc3eaba
--- /dev/null
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <pthread.h>
+
+#include "macro.h"
+
+static inline pthread_mutex_t* pthread_mutex_lock_assert(pthread_mutex_t *mutex) {
+        assert_se(pthread_mutex_lock(mutex) == 0);
+        return mutex;
+}
+
+static inline void pthread_mutex_unlock_assertp(pthread_mutex_t **mutexp) {
+        if (*mutexp)
+                assert_se(pthread_mutex_unlock(*mutexp) == 0);
+}
index 8717a49ba77867cee6fcf502473fc70b10a33f5a..6eeed9af346cc9037fae96fd240d79efdaa61d6e 100644 (file)
@@ -7,11 +7,13 @@
 #include <elf.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <linux/random.h>
 #include <pthread.h>
 #include <stdbool.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/ioctl.h>
 #include <sys/time.h>
 
 #if HAVE_SYS_AUXV_H
@@ -75,7 +77,7 @@ int rdrand(unsigned long *ret) {
          *           hash functions for its hash tables, with a seed generated randomly. The hash tables
          *           systemd employs watch the fill level closely and reseed if necessary. This allows use of
          *           a low quality RNG initially, as long as it improves should a hash table be under attack:
-         *           the attacker after all needs to to trigger many collisions to exploit it for the purpose
+         *           the attacker after all needs to trigger many collisions to exploit it for the purpose
          *           of DoS, but if doing so improves the seed the attack surface is reduced as the attack
          *           takes place.
          *
@@ -208,7 +210,9 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) {
         if (have_syscall != 0 && !HAS_FEATURE_MEMORY_SANITIZER) {
 
                 for (;;) {
-                        r = getrandom(p, n, FLAGS_SET(flags, RANDOM_BLOCK) ? 0 : GRND_NONBLOCK);
+                        r = getrandom(p, n,
+                                      (FLAGS_SET(flags, RANDOM_BLOCK) ? 0 : GRND_NONBLOCK) |
+                                      (FLAGS_SET(flags, RANDOM_ALLOW_INSECURE) ? GRND_INSECURE : 0));
                         if (r > 0) {
                                 have_syscall = true;
 
@@ -264,6 +268,18 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) {
 
                                 /* Use /dev/urandom instead */
                                 break;
+
+                        } else if (errno == EINVAL) {
+
+                                /* Most likely: unknown flag. We know that GRND_INSECURE might cause this,
+                                 * hence try without. */
+
+                                if (FLAGS_SET(flags, RANDOM_ALLOW_INSECURE)) {
+                                        flags = flags &~ RANDOM_ALLOW_INSECURE;
+                                        continue;
+                                }
+
+                                return -errno;
                         } else
                                 return -errno;
                 }
@@ -326,9 +342,11 @@ void initialize_srand(void) {
 
 /* INT_MAX gives us only 31 bits, so use 24 out of that. */
 #if RAND_MAX >= INT_MAX
+assert_cc(RAND_MAX >= 16777215);
 #  define RAND_STEP 3
 #else
-/* SHORT_INT_MAX or lower gives at most 15 bits, we just just 8 out of that. */
+/* SHORT_INT_MAX or lower gives at most 15 bits, we just use 8 out of that. */
+assert_cc(RAND_MAX >= 255);
 #  define RAND_STEP 1
 #endif
 
@@ -393,7 +411,7 @@ void random_bytes(void *p, size_t n) {
          * This function is hence not useful for generating UUIDs or cryptographic key material.
          */
 
-        if (genuine_random_bytes(p, n, RANDOM_EXTEND_WITH_PSEUDO|RANDOM_MAY_FAIL|RANDOM_ALLOW_RDRAND) >= 0)
+        if (genuine_random_bytes(p, n, RANDOM_EXTEND_WITH_PSEUDO|RANDOM_MAY_FAIL|RANDOM_ALLOW_RDRAND|RANDOM_ALLOW_INSECURE) >= 0)
                 return;
 
         /* If for some reason some user made /dev/urandom unavailable to us, or the kernel has no entropy, use a PRNG instead. */
@@ -422,3 +440,36 @@ size_t random_pool_size(void) {
         /* Use the minimum as default, if we can't retrieve the correct value */
         return RANDOM_POOL_SIZE_MIN;
 }
+
+int random_write_entropy(int fd, const void *seed, size_t size, bool credit) {
+        int r;
+
+        assert(fd >= 0);
+        assert(seed && size > 0);
+
+        if (credit) {
+                _cleanup_free_ struct rand_pool_info *info = NULL;
+
+                /* The kernel API only accepts "int" as entropy count (which is in bits), let's avoid any
+                 * chance for confusion here. */
+                if (size > INT_MAX / 8)
+                        return -EOVERFLOW;
+
+                info = malloc(offsetof(struct rand_pool_info, buf) + size);
+                if (!info)
+                        return -ENOMEM;
+
+                info->entropy_count = size * 8;
+                info->buf_size = size;
+                memcpy(info->buf, seed, size);
+
+                if (ioctl(fd, RNDADDENTROPY, info) < 0)
+                        return -errno;
+        } else {
+                r = loop_write(fd, seed, size, false);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
index facc11b976fbdba8376920af2966cb3f6cb4de21..7824ffacebb9255ac81783b5866acc6afb6600e0 100644 (file)
@@ -10,6 +10,7 @@ typedef enum RandomFlags {
         RANDOM_BLOCK              = 1 << 1, /* Rather block than return crap randomness (only if the kernel supports that) */
         RANDOM_MAY_FAIL           = 1 << 2, /* If we can't get any randomness at all, return early with -ENODATA */
         RANDOM_ALLOW_RDRAND       = 1 << 3, /* Allow usage of the CPU RNG */
+        RANDOM_ALLOW_INSECURE     = 1 << 4, /* Allow usage of GRND_INSECURE flag to kernel's getrandom() API */
 } RandomFlags;
 
 int genuine_random_bytes(void *p, size_t n, RandomFlags flags); /* returns "genuine" randomness, optionally filled up with pseudo random, if not enough is available */
@@ -37,3 +38,5 @@ int rdrand(unsigned long *ret);
 #define RANDOM_POOL_SIZE_MAX (10U*1024U*1024U)
 
 size_t random_pool_size(void);
+
+int random_write_entropy(int fd, const void *seed, size_t size, bool credit);
index 1095cb426cce815d829dd0d8744b72fb15be9110..c94ee26bd900af070e7b13bcd99a4c0577c1eab5 100644 (file)
@@ -12,6 +12,7 @@
 #include <syslog.h>
 
 #if HAVE_SELINUX
+#include <selinux/avc.h>
 #include <selinux/context.h>
 #include <selinux/label.h>
 #include <selinux/selinux.h>
 DEFINE_TRIVIAL_CLEANUP_FUNC(context_t, context_free);
 #define _cleanup_context_free_ _cleanup_(context_freep)
 
+static int mac_selinux_reload(int seqno);
+
 static int cached_use = -1;
 static struct selabel_handle *label_hnd = NULL;
 
-#define log_enforcing(...) log_full(security_getenforce() == 1 ? LOG_ERR : LOG_WARNING, __VA_ARGS__)
-#define log_enforcing_errno(r, ...) log_full_errno(security_getenforce() == 1 ? LOG_ERR : LOG_WARNING, r, __VA_ARGS__)
+#define log_enforcing(...)                                              \
+        log_full(security_getenforce() != 0 ? LOG_ERR : LOG_WARNING, __VA_ARGS__)
+
+#define log_enforcing_errno(error, ...)                                 \
+        ({                                                              \
+                bool _enforcing = security_getenforce() != 0;           \
+                int _level = _enforcing ? LOG_ERR : LOG_WARNING;        \
+                int _e = (error);                                       \
+                                                                        \
+                int _r = (log_get_max_level() >= LOG_PRI(_level))       \
+                        ? log_internal_realm(_level, _e, PROJECT_FILE, __LINE__, __func__, __VA_ARGS__) \
+                        : -ERRNO_VALUE(_e);                             \
+                _enforcing ? _r : 0;                                    \
+        })
 #endif
 
 bool mac_selinux_use(void) {
 #if HAVE_SELINUX
-        if (cached_use < 0)
+        if (_unlikely_(cached_use < 0)) {
                 cached_use = is_selinux_enabled() > 0;
+                log_debug("SELinux enabled state cached to: %s", cached_use ? "enabled" : "disabled");
+        }
 
         return cached_use;
 #else
@@ -56,11 +73,13 @@ void mac_selinux_retest(void) {
 }
 
 int mac_selinux_init(void) {
-        int r = 0;
-
 #if HAVE_SELINUX
         usec_t before_timestamp, after_timestamp;
         struct mallinfo before_mallinfo, after_mallinfo;
+        char timespan[FORMAT_TIMESPAN_MAX];
+        int l;
+
+        selinux_set_callback(SELINUX_CB_POLICYLOAD, (union selinux_callback) mac_selinux_reload);
 
         if (label_hnd)
                 return 0;
@@ -72,25 +91,20 @@ int mac_selinux_init(void) {
         before_timestamp = now(CLOCK_MONOTONIC);
 
         label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);
-        if (!label_hnd) {
-                log_enforcing_errno(errno, "Failed to initialize SELinux context: %m");
-                r = security_getenforce() == 1 ? -errno : 0;
-        } else  {
-                char timespan[FORMAT_TIMESPAN_MAX];
-                int l;
+        if (!label_hnd)
+                return log_enforcing_errno(errno, "Failed to initialize SELinux labeling handle: %m");
 
-                after_timestamp = now(CLOCK_MONOTONIC);
-                after_mallinfo = mallinfo();
+        after_timestamp = now(CLOCK_MONOTONIC);
+        after_mallinfo = mallinfo();
 
-                l = after_mallinfo.uordblks > before_mallinfo.uordblks ? after_mallinfo.uordblks - before_mallinfo.uordblks : 0;
+        l = after_mallinfo.uordblks > before_mallinfo.uordblks ? after_mallinfo.uordblks - before_mallinfo.uordblks : 0;
 
-                log_debug("Successfully loaded SELinux database in %s, size on heap is %iK.",
-                          format_timespan(timespan, sizeof(timespan), after_timestamp - before_timestamp, 0),
-                          (l+1023)/1024);
-        }
-#endif
+        log_debug("Successfully loaded SELinux database in %s, size on heap is %iK.",
+                  format_timespan(timespan, sizeof(timespan), after_timestamp - before_timestamp, 0),
+                  (l+1023)/1024);
 
-        return r;
+#endif
+        return 0;
 }
 
 void mac_selinux_finish(void) {
@@ -104,13 +118,12 @@ void mac_selinux_finish(void) {
 #endif
 }
 
-void mac_selinux_reload(void) {
-
 #if HAVE_SELINUX
+static int mac_selinux_reload(int seqno) {
         struct selabel_handle *backup_label_hnd;
 
         if (!label_hnd)
-                return;
+                return 0;
 
         backup_label_hnd = TAKE_PTR(label_hnd);
 
@@ -121,10 +134,12 @@ void mac_selinux_reload(void) {
                 selabel_close(backup_label_hnd);
         else
                 label_hnd = backup_label_hnd;
-#endif
+
+        return 0;
 }
+#endif
 
-int mac_selinux_fix(const char *path, LabelFixFlags flags) {
+int mac_selinux_fix_container(const char *path, const char *inside_path, LabelFixFlags flags) {
 
 #if HAVE_SELINUX
         char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
@@ -151,7 +166,10 @@ int mac_selinux_fix(const char *path, LabelFixFlags flags) {
         if (fstat(fd, &st) < 0)
                 return -errno;
 
-        if (selabel_lookup_raw(label_hnd, &fcon, path, st.st_mode) < 0) {
+        /* Check for policy reload so 'label_hnd' is kept up-to-date by callbacks */
+        (void) avc_netlink_check_nb();
+
+        if (selabel_lookup_raw(label_hnd, &fcon, inside_path, st.st_mode) < 0) {
                 r = -errno;
 
                 /* If there's no label to set, then exit without warning */
@@ -185,9 +203,7 @@ int mac_selinux_fix(const char *path, LabelFixFlags flags) {
         return 0;
 
 fail:
-        log_enforcing_errno(r, "Unable to fix SELinux security context of %s: %m", path);
-        if (security_getenforce() == 1)
-                return r;
+        return log_enforcing_errno(r, "Unable to fix SELinux security context of %s (%s): %m", path, inside_path);
 #endif
 
         return 0;
@@ -202,21 +218,17 @@ int mac_selinux_apply(const char *path, const char *label) {
         assert(path);
         assert(label);
 
-        if (setfilecon(path, label) < 0) {
-                log_enforcing_errno(errno, "Failed to set SELinux security context %s on path %s: %m", label, path);
-                if (security_getenforce() > 0)
-                        return -errno;
-        }
+        if (setfilecon(path, label) < 0)
+                return log_enforcing_errno(errno, "Failed to set SELinux security context %s on path %s: %m", label, path);
 #endif
         return 0;
 }
 
 int mac_selinux_get_create_label_from_exe(const char *exe, char **label) {
-        int r = -EOPNOTSUPP;
-
 #if HAVE_SELINUX
         _cleanup_freecon_ char *mycon = NULL, *fcon = NULL;
         security_class_t sclass;
+        int r;
 
         assert(exe);
         assert(label);
@@ -239,36 +251,39 @@ int mac_selinux_get_create_label_from_exe(const char *exe, char **label) {
         r = security_compute_create_raw(mycon, fcon, sclass, label);
         if (r < 0)
                 return -errno;
-#endif
 
-        return r;
+        return 0;
+#else
+        return -EOPNOTSUPP;
+#endif
 }
 
 int mac_selinux_get_our_label(char **label) {
-        int r = -EOPNOTSUPP;
+#if HAVE_SELINUX
+        int r;
 
         assert(label);
 
-#if HAVE_SELINUX
         if (!mac_selinux_use())
                 return -EOPNOTSUPP;
 
         r = getcon_raw(label);
         if (r < 0)
                 return -errno;
-#endif
 
-        return r;
+        return 0;
+#else
+        return -EOPNOTSUPP;
+#endif
 }
 
 int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *exec_label, char **label) {
-        int r = -EOPNOTSUPP;
-
 #if HAVE_SELINUX
         _cleanup_freecon_ char *mycon = NULL, *peercon = NULL, *fcon = NULL;
         _cleanup_context_free_ context_t pcon = NULL, bcon = NULL;
         security_class_t sclass;
         const char *range = NULL;
+        int r;
 
         assert(socket_fd >= 0);
         assert(exe);
@@ -321,21 +336,19 @@ int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *
         r = security_compute_create_raw(mycon, fcon, sclass, label);
         if (r < 0)
                 return -errno;
-#endif
 
-        return r;
+        return 0;
+#else
+        return -EOPNOTSUPP;
+#endif
 }
 
 char* mac_selinux_free(char *label) {
 
 #if HAVE_SELINUX
-        if (!label)
-                return NULL;
-
-        if (!mac_selinux_use())
-                return NULL;
-
         freecon(label);
+#else
+        assert(!label);
 #endif
 
         return NULL;
@@ -349,32 +362,30 @@ static int selinux_create_file_prepare_abspath(const char *abspath, mode_t mode)
         assert(abspath);
         assert(path_is_absolute(abspath));
 
+        /* Check for policy reload so 'label_hnd' is kept up-to-date by callbacks */
+        (void) avc_netlink_check_nb();
+
         r = selabel_lookup_raw(label_hnd, &filecon, abspath, mode);
         if (r < 0) {
                 /* No context specified by the policy? Proceed without setting it. */
                 if (errno == ENOENT)
                         return 0;
 
-                log_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", abspath);
-        } else {
-                if (setfscreatecon_raw(filecon) >= 0)
-                        return 0; /* Success! */
-
-                log_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", filecon, abspath);
+                return log_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", abspath);
         }
 
-        if (security_getenforce() > 0)
-                return -errno;
+        if (setfscreatecon_raw(filecon) < 0)
+                return log_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", filecon, abspath);
 
         return 0;
 }
 #endif
 
 int mac_selinux_create_file_prepare_at(int dirfd, const char *path, mode_t mode) {
-        int r = 0;
-
 #if HAVE_SELINUX
         _cleanup_free_ char *abspath = NULL;
+        int r;
+
 
         assert(path);
 
@@ -396,15 +407,16 @@ int mac_selinux_create_file_prepare_at(int dirfd, const char *path, mode_t mode)
                         return -ENOMEM;
         }
 
-        r = selinux_create_file_prepare_abspath(path, mode);
+        return selinux_create_file_prepare_abspath(path, mode);
+#else
+        return 0;
 #endif
-        return r;
 }
 
 int mac_selinux_create_file_prepare(const char *path, mode_t mode) {
-        int r = 0;
-
 #if HAVE_SELINUX
+        int r;
+
         _cleanup_free_ char *abspath = NULL;
 
         assert(path);
@@ -416,9 +428,10 @@ int mac_selinux_create_file_prepare(const char *path, mode_t mode) {
         if (r < 0)
                 return r;
 
-        r = selinux_create_file_prepare_abspath(abspath, mode);
+        return selinux_create_file_prepare_abspath(abspath, mode);
+#else
+        return 0;
 #endif
-        return r;
 }
 
 void mac_selinux_create_file_clear(void) {
@@ -436,17 +449,13 @@ void mac_selinux_create_file_clear(void) {
 int mac_selinux_create_socket_prepare(const char *label) {
 
 #if HAVE_SELINUX
-        if (!mac_selinux_use())
-                return 0;
-
         assert(label);
 
-        if (setsockcreatecon(label) < 0) {
-                log_enforcing_errno(errno, "Failed to set SELinux security context %s for sockets: %m", label);
+        if (!mac_selinux_use())
+                return 0;
 
-                if (security_getenforce() == 1)
-                        return -errno;
-        }
+        if (setsockcreatecon(label) < 0)
+                return log_enforcing_errno(errno, "Failed to set SELinux security context %s for sockets: %m", label);
 #endif
 
         return 0;
@@ -497,6 +506,9 @@ int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
 
         path = strndupa(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path));
 
+        /* Check for policy reload so 'label_hnd' is kept up-to-date by callbacks */
+        (void) avc_netlink_check_nb();
+
         if (path_is_absolute(path))
                 r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK);
         else {
@@ -514,15 +526,14 @@ int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
                 if (errno == ENOENT)
                         goto skipped;
 
-                log_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", path);
-                if (security_getenforce() > 0)
-                        return -errno;
-
+                r = log_enforcing_errno(errno, "Failed to determine SELinux security context for %s: %m", path);
+                if (r < 0)
+                        return r;
         } else {
                 if (setfscreatecon_raw(fcon) < 0) {
-                        log_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", fcon, path);
-                        if (security_getenforce() > 0)
-                                return -errno;
+                        r = log_enforcing_errno(errno, "Failed to set SELinux security context %s for %s: %m", fcon, path);
+                        if (r < 0)
+                                return r;
                 } else
                         context_changed = true;
         }
@@ -530,7 +541,7 @@ int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) {
         r = bind(fd, addr, addrlen) < 0 ? -errno : 0;
 
         if (context_changed)
-                setfscreatecon_raw(NULL);
+                (void) setfscreatecon_raw(NULL);
 
         return r;
 
index b73b7c50e07414e3cba0e0037a5610d6fbf1fb65..d053b00b5c9980c37cf5ee5241d71f3b47a6a0fa 100644 (file)
@@ -20,9 +20,12 @@ void mac_selinux_retest(void);
 
 int mac_selinux_init(void);
 void mac_selinux_finish(void);
-void mac_selinux_reload(void);
 
-int mac_selinux_fix(const char *path, LabelFixFlags flags);
+int mac_selinux_fix_container(const char *path, const char *inside_path, LabelFixFlags flags);
+static inline int mac_selinux_fix(const char *path, LabelFixFlags flags) {
+        return mac_selinux_fix_container(path, path, flags);
+}
+
 int mac_selinux_apply(const char *path, const char *label);
 
 int mac_selinux_get_create_label_from_exe(const char *exe, char **label);
index 5f1956177e016669877fe90140aca8e9abd63b4a..e4fc1e3c4a01755fbd76873f8a45efee8879c782 100644 (file)
@@ -5,40 +5,46 @@
 #include "hashmap.h"
 #include "macro.h"
 
-Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
-#define set_new(ops) internal_set_new(ops HASHMAP_DEBUG_SRC_ARGS)
+#define set_free_and_replace(a, b)              \
+        ({                                      \
+                set_free(a);                    \
+                (a) = (b);                      \
+                (b) = NULL;                     \
+                0;                              \
+        })
+
+Set *_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
+#define set_new(ops) _set_new(ops HASHMAP_DEBUG_SRC_ARGS)
 
 static inline Set *set_free(Set *s) {
-        return (Set*) internal_hashmap_free(HASHMAP_BASE(s), NULL, NULL);
+        return (Set*) _hashmap_free(HASHMAP_BASE(s), NULL, NULL);
 }
 
 static inline Set *set_free_free(Set *s) {
-        return (Set*) internal_hashmap_free(HASHMAP_BASE(s), free, NULL);
+        return (Set*) _hashmap_free(HASHMAP_BASE(s), free, NULL);
 }
 
 /* no set_free_free_free */
 
-static inline Set *set_copy(Set *s) {
-        return (Set*) internal_hashmap_copy(HASHMAP_BASE(s));
-}
+#define set_copy(s) ((Set*) _hashmap_copy(HASHMAP_BASE(h)  HASHMAP_DEBUG_SRC_ARGS))
 
-int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
-#define set_ensure_allocated(h, ops) internal_set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
+int _set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
+#define set_ensure_allocated(h, ops) _set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
 
 int set_put(Set *s, const void *key);
 /* no set_update */
 /* no set_replace */
 static inline void *set_get(const Set *s, void *key) {
-        return internal_hashmap_get(HASHMAP_BASE((Set *) s), key);
+        return _hashmap_get(HASHMAP_BASE((Set *) s), key);
 }
 /* no set_get2 */
 
 static inline bool set_contains(const Set *s, const void *key) {
-        return internal_hashmap_contains(HASHMAP_BASE((Set *) s), key);
+        return _hashmap_contains(HASHMAP_BASE((Set *) s), key);
 }
 
 static inline void *set_remove(Set *s, const void *key) {
-        return internal_hashmap_remove(HASHMAP_BASE(s), key);
+        return _hashmap_remove(HASHMAP_BASE(s), key);
 }
 
 /* no set_remove2 */
@@ -48,19 +54,19 @@ int set_remove_and_put(Set *s, const void *old_key, const void *new_key);
 int set_merge(Set *s, Set *other);
 
 static inline int set_reserve(Set *h, unsigned entries_add) {
-        return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add);
+        return _hashmap_reserve(HASHMAP_BASE(h), entries_add);
 }
 
 static inline int set_move(Set *s, Set *other) {
-        return internal_hashmap_move(HASHMAP_BASE(s), HASHMAP_BASE(other));
+        return _hashmap_move(HASHMAP_BASE(s), HASHMAP_BASE(other));
 }
 
 static inline int set_move_one(Set *s, Set *other, const void *key) {
-        return internal_hashmap_move_one(HASHMAP_BASE(s), HASHMAP_BASE(other), key);
+        return _hashmap_move_one(HASHMAP_BASE(s), HASHMAP_BASE(other), key);
 }
 
 static inline unsigned set_size(const Set *s) {
-        return internal_hashmap_size(HASHMAP_BASE((Set *) s));
+        return _hashmap_size(HASHMAP_BASE((Set *) s));
 }
 
 static inline bool set_isempty(const Set *s) {
@@ -68,23 +74,23 @@ static inline bool set_isempty(const Set *s) {
 }
 
 static inline unsigned set_buckets(const Set *s) {
-        return internal_hashmap_buckets(HASHMAP_BASE((Set *) s));
+        return _hashmap_buckets(HASHMAP_BASE((Set *) s));
 }
 
 bool set_iterate(const Set *s, Iterator *i, void **value);
 
 static inline void set_clear(Set *s) {
-        internal_hashmap_clear(HASHMAP_BASE(s), NULL, NULL);
+        _hashmap_clear(HASHMAP_BASE(s), NULL, NULL);
 }
 
 static inline void set_clear_free(Set *s) {
-        internal_hashmap_clear(HASHMAP_BASE(s), free, NULL);
+        _hashmap_clear(HASHMAP_BASE(s), free, NULL);
 }
 
 /* no set_clear_free_free */
 
 static inline void *set_steal_first(Set *s) {
-        return internal_hashmap_first_key_and_value(HASHMAP_BASE(s), true, NULL);
+        return _hashmap_first_key_and_value(HASHMAP_BASE(s), true, NULL);
 }
 
 #define set_clear_with_destructor(_s, _f)               \
@@ -103,18 +109,28 @@ static inline void *set_steal_first(Set *s) {
 /* no set_first_key */
 
 static inline void *set_first(const Set *s) {
-        return internal_hashmap_first_key_and_value(HASHMAP_BASE((Set *) s), false, NULL);
+        return _hashmap_first_key_and_value(HASHMAP_BASE((Set *) s), false, NULL);
 }
 
 /* no set_next */
 
 static inline char **set_get_strv(Set *s) {
-        return internal_hashmap_get_strv(HASHMAP_BASE(s));
+        return _hashmap_get_strv(HASHMAP_BASE(s));
 }
 
+int _set_ensure_put(Set **s, const struct hash_ops *hash_ops, const void *key  HASHMAP_DEBUG_PARAMS);
+#define set_ensure_put(s, hash_ops, key) _set_ensure_put(s, hash_ops, key  HASHMAP_DEBUG_SRC_ARGS)
+
+int _set_ensure_consume(Set **s, const struct hash_ops *hash_ops, void *key  HASHMAP_DEBUG_PARAMS);
+#define set_ensure_consume(s, hash_ops, key) _set_ensure_consume(s, hash_ops, key  HASHMAP_DEBUG_SRC_ARGS)
+
 int set_consume(Set *s, void *value);
-int set_put_strdup(Set *s, const char *p);
-int set_put_strdupv(Set *s, char **l);
+
+int _set_put_strdup(Set **s, const char *p  HASHMAP_DEBUG_PARAMS);
+#define set_put_strdup(s, p) _set_put_strdup(s, p  HASHMAP_DEBUG_SRC_ARGS)
+int _set_put_strdupv(Set **s, char **l  HASHMAP_DEBUG_PARAMS);
+#define set_put_strdupv(s, l) _set_put_strdupv(s, l  HASHMAP_DEBUG_SRC_ARGS)
+
 int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags flags);
 
 #define SET_FOREACH(e, s, i) \
index 666431aa31fd0592bd4cb9ed72d01803b6d911fc..61180819b1bf0f995cddb9ca2ee9f02e1be9bb28 100644 (file)
@@ -151,12 +151,6 @@ void siphash24_compress(const void *_in, size_t inlen, struct siphash *state) {
         }
 }
 
-void siphash24_compress_boolean(bool in, struct siphash *state) {
-        int i = in;
-
-        siphash24_compress(&i, sizeof i, state);
-}
-
 uint64_t siphash24_finalize(struct siphash *state) {
         uint64_t b;
 
index 1937fea2985fa1f43fbbc4efe5d4a025e7424bf7..7f799ede3d19628261ac85912c6001334f8b329c 100644 (file)
@@ -17,9 +17,21 @@ struct siphash {
 
 void siphash24_init(struct siphash *state, const uint8_t k[static 16]);
 void siphash24_compress(const void *in, size_t inlen, struct siphash *state);
-void siphash24_compress_boolean(bool in, struct siphash *state);
 #define siphash24_compress_byte(byte, state) siphash24_compress((const uint8_t[]) { (byte) }, 1, (state))
 
+static inline void siphash24_compress_boolean(bool in, struct siphash *state) {
+        uint8_t i = in;
+
+        siphash24_compress(&i, sizeof i, state);
+}
+
+static inline void siphash24_compress_string(const char *in, struct siphash *state) {
+        if (!in)
+                return;
+
+        siphash24_compress(in, strlen(in), state);
+}
+
 uint64_t siphash24_finalize(struct siphash *state);
 
 uint64_t siphash24(const void *in, size_t inlen, const uint8_t k[static 16]);
index da9a2139d31a2d3b3c6f23da5b6e7054ea69d38d..8043a97c3594b01ffc0d2b1423ae2ab1d299be69 100644 (file)
@@ -206,7 +206,7 @@ int mac_smack_fix_at(int dirfd, const char *path, LabelFixFlags flags) {
         return smack_fix_fd(fd, path, flags);
 }
 
-int mac_smack_fix(const char *path, LabelFixFlags flags) {
+int mac_smack_fix_container(const char *path, const char *inside_path, LabelFixFlags flags) {
         _cleanup_free_ char *abspath = NULL;
         _cleanup_close_ int fd = -1;
         int r;
@@ -228,7 +228,7 @@ int mac_smack_fix(const char *path, LabelFixFlags flags) {
                 return -errno;
         }
 
-        return smack_fix_fd(fd, abspath, flags);
+        return smack_fix_fd(fd, inside_path, flags);
 }
 
 int mac_smack_copy(const char *dest, const char *src) {
@@ -274,7 +274,7 @@ int mac_smack_apply_pid(pid_t pid, const char *label) {
         return 0;
 }
 
-int mac_smack_fix(const char *path, LabelFixFlags flags) {
+int mac_smack_fix_container(const char *path, const char *inside_path, LabelFixFlags flags) {
         return 0;
 }
 
index 395ec07b57c7aa462e537201e705b38602bd6778..df2ce370716910741110adab5495b700868ae12a 100644 (file)
@@ -29,7 +29,11 @@ typedef enum SmackAttr {
 
 bool mac_smack_use(void);
 
-int mac_smack_fix(const char *path, LabelFixFlags flags);
+int mac_smack_fix_container(const char *path, const char *inside_path, LabelFixFlags flags);
+static inline int mac_smack_fix(const char *path, LabelFixFlags flags) {
+        return mac_smack_fix_container(path, path, flags);
+}
+
 int mac_smack_fix_at(int dirfd, const char *path, LabelFixFlags flags);
 
 const char* smack_attr_to_string(SmackAttr i) _const_;
index b797a52180f959d3bc34ef6235314f54f7502d97..fb1265985786c711daee7008b2293eed2ea95bb6 100644 (file)
@@ -21,6 +21,7 @@
 #include "fd-util.h"
 #include "fileio.h"
 #include "format-util.h"
+#include "io-util.h"
 #include "log.h"
 #include "macro.h"
 #include "memory-util.h"
@@ -104,7 +105,7 @@ int socket_address_verify(const SocketAddress *a, bool strict) {
                                 if (a->size != offsetof(struct sockaddr_un, sun_path) + (e - a->sockaddr.un.sun_path) + 1)
                                         return -EINVAL;
                         } else {
-                                /* If there's no embedded NUL byte, then then the size needs to match the whole
+                                /* If there's no embedded NUL byte, then the size needs to match the whole
                                  * structure or the structure with one extra NUL byte suffixed. (Yeah, Linux is awful,
                                  * and considers both equivalent: getsockname() even extends sockaddr_un beyond its
                                  * size if the path is non NUL terminated.)*/
@@ -818,10 +819,7 @@ ssize_t send_one_fd_iov_sa(
                 const struct sockaddr *sa, socklen_t len,
                 int flags) {
 
-        union {
-                struct cmsghdr cmsghdr;
-                uint8_t buf[CMSG_SPACE(sizeof(int))];
-        } control = {};
+        CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int))) control = {};
         struct msghdr mh = {
                 .msg_name = (struct sockaddr*) sa,
                 .msg_namelen = len,
@@ -850,8 +848,6 @@ ssize_t send_one_fd_iov_sa(
                 cmsg->cmsg_type = SCM_RIGHTS;
                 cmsg->cmsg_len = CMSG_LEN(sizeof(int));
                 memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
-
-                mh.msg_controllen = CMSG_SPACE(sizeof(int));
         }
         k = sendmsg(transport_fd, &mh, MSG_NOSIGNAL | flags);
         if (k < 0)
@@ -877,17 +873,14 @@ ssize_t receive_one_fd_iov(
                 int flags,
                 int *ret_fd) {
 
-        union {
-                struct cmsghdr cmsghdr;
-                uint8_t buf[CMSG_SPACE(sizeof(int))];
-        } control = {};
+        CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int))) control;
         struct msghdr mh = {
                 .msg_control = &control,
                 .msg_controllen = sizeof(control),
                 .msg_iov = iov,
                 .msg_iovlen = iovlen,
         };
-        struct cmsghdr *cmsg, *found = NULL;
+        struct cmsghdr *found;
         ssize_t k;
 
         assert(transport_fd >= 0);
@@ -905,16 +898,7 @@ ssize_t receive_one_fd_iov(
         if (k < 0)
                 return k;
 
-        CMSG_FOREACH(cmsg, &mh) {
-                if (cmsg->cmsg_level == SOL_SOCKET &&
-                    cmsg->cmsg_type == SCM_RIGHTS &&
-                    cmsg->cmsg_len == CMSG_LEN(sizeof(int))) {
-                        assert(!found);
-                        found = cmsg;
-                        break;
-                }
-        }
-
+        found = cmsg_find(&mh, SOL_SOCKET, SCM_RIGHTS, CMSG_LEN(sizeof(int)));
         if (!found) {
                 cmsg_close_all(&mh);
 
@@ -985,10 +969,6 @@ fallback:
 
 int flush_accept(int fd) {
 
-        struct pollfd pollfd = {
-                .fd = fd,
-                .events = POLLIN,
-        };
         int r, b;
         socklen_t l = sizeof(b);
 
@@ -1009,12 +989,12 @@ int flush_accept(int fd) {
         for (unsigned iteration = 0;; iteration++) {
                 int cfd;
 
-                r = poll(&pollfd, 1, 0);
+                r = fd_wait_for_event(fd, POLLIN, 0);
                 if (r < 0) {
-                        if (errno == EINTR)
+                        if (r == -EINTR)
                                 continue;
 
-                        return -errno;
+                        return r;
                 }
                 if (r == 0)
                         return 0;
@@ -1150,6 +1130,7 @@ int socket_bind_to_ifname(int fd, const char *ifname) {
 
 int socket_bind_to_ifindex(int fd, int ifindex) {
         char ifname[IF_NAMESIZE + 1];
+        int r;
 
         assert(fd >= 0);
 
@@ -1161,10 +1142,9 @@ int socket_bind_to_ifindex(int fd, int ifindex) {
                 return 0;
         }
 
-        if (setsockopt(fd, SOL_SOCKET, SO_BINDTOIFINDEX, &ifindex, sizeof(ifindex)) >= 0)
-                return 0;
-        if (errno != ENOPROTOOPT)
-                return -errno;
+        r = setsockopt_int(fd, SOL_SOCKET, SO_BINDTOIFINDEX, ifindex);
+        if (r != -ENOPROTOOPT)
+                return r;
 
         /* Fall back to SO_BINDTODEVICE on kernels < 5.0 which didn't have SO_BINDTOIFINDEX */
         if (!format_ifname(ifindex, ifname))
@@ -1191,5 +1171,27 @@ ssize_t recvmsg_safe(int sockfd, struct msghdr *msg, int flags) {
         }
 
         return n;
+}
 
+int socket_pass_pktinfo(int fd, bool b) {
+        int af;
+        socklen_t sl = sizeof(af);
+
+        if (getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &af, &sl) < 0)
+                return -errno;
+
+        switch (af) {
+
+        case AF_INET:
+                return setsockopt_int(fd, IPPROTO_IP, IP_PKTINFO, b);
+
+        case AF_INET6:
+                return setsockopt_int(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, b);
+
+        case AF_NETLINK:
+                return setsockopt_int(fd, SOL_NETLINK, NETLINK_PKTINFO, b);
+
+        default:
+                return -EAFNOSUPPORT;
+        }
 }
index 95643135df7cbca6db0b3e734c94eaf9680e41f6..9e02e398875480396669fd598c27df015c8ac299 100644 (file)
@@ -158,6 +158,25 @@ int flush_accept(int fd);
 
 struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t length);
 
+/* Type-safe, dereferencing version of cmsg_find() */
+#define CMSG_FIND_DATA(mh, level, type, ctype) \
+        ({                                                            \
+                struct cmsghdr *_found;                               \
+                _found = cmsg_find(mh, level, type, CMSG_LEN(sizeof(ctype))); \
+                (ctype*) (_found ? CMSG_DATA(_found) : NULL);         \
+        })
+
+/* Resolves to a type that can carry cmsghdr structures. Make sure things are properly aligned, i.e. the type
+ * itself is placed properly in memory and the size is also aligned to what's appropriate for "cmsghdr"
+ * structures. */
+#define CMSG_BUFFER_TYPE(size)                                          \
+        union {                                                         \
+                struct cmsghdr cmsghdr;                                 \
+                uint8_t buf[size];                                      \
+                uint8_t align_check[(size) >= CMSG_SPACE(0) &&          \
+                                    (size) == CMSG_ALIGN(size) ? 1 : -1]; \
+        }
+
 /*
  * Certain hardware address types (e.g Infiniband) do not fit into sll_addr
  * (8 bytes) and run over the structure. This macro returns the correct size that
@@ -201,3 +220,5 @@ int socket_bind_to_ifname(int fd, const char *ifname);
 int socket_bind_to_ifindex(int fd, int ifindex);
 
 ssize_t recvmsg_safe(int sockfd, struct msghdr *msg, int flags);
+
+int socket_pass_pktinfo(int fd, bool b);
index e029f8646eb0e4b2b21dd8e46146a80e51b86a79..a8dc3bb6edb10d754ee245778f2f45d8586fffd7 100644 (file)
@@ -39,7 +39,7 @@ static inline void* bsearch_safe(const void *key, const void *base,
  * Normal qsort requires base to be nonnull. Here were require
  * that only if nmemb > 0.
  */
-static inline void qsort_safe(void *base, size_t nmemb, size_t size, __compar_fn_t compar) {
+static inline void _qsort_safe(void *base, size_t nmemb, size_t size, __compar_fn_t compar) {
         if (nmemb <= 1)
                 return;
 
@@ -52,7 +52,7 @@ static inline void qsort_safe(void *base, size_t nmemb, size_t size, __compar_fn
 #define typesafe_qsort(p, n, func)                                      \
         ({                                                              \
                 int (*_func_)(const typeof(p[0])*, const typeof(p[0])*) = func; \
-                qsort_safe((p), (n), sizeof((p)[0]), (__compar_fn_t) _func_); \
+                _qsort_safe((p), (n), sizeof((p)[0]), (__compar_fn_t) _func_); \
         })
 
 static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, __compar_d_fn_t compar, void *userdata) {
index 0eb3f3a36866ddd1a7f6ff251468429f1a8aec9c..19ee30cd412cc7c915ae327c8735c3c93ee3b0c3 100644 (file)
@@ -82,6 +82,7 @@
 #define SPECIAL_QUOTAON_SERVICE "quotaon.service"
 #define SPECIAL_REMOUNT_FS_SERVICE "systemd-remount-fs.service"
 #define SPECIAL_VOLATILE_ROOT_SERVICE "systemd-volatile-root.service"
+#define SPECIAL_UDEVD_SERVICE "systemd-udevd.service"
 
 /* Services systemd relies on */
 #define SPECIAL_DBUS_SERVICE "dbus.service"
index 4b3d84034d57781a3af206aef8d331c627b98650..904584a9857c7fc7b875493ca932fabdc342a904 100644 (file)
@@ -10,6 +10,7 @@
 #include "alloc-util.h"
 #include "dirent-util.h"
 #include "fd-util.h"
+#include "fileio.h"
 #include "fs-util.h"
 #include "macro.h"
 #include "missing_fs.h"
@@ -77,10 +78,9 @@ int dir_is_empty_at(int dir_fd, const char *path) {
         if (fd < 0)
                 return -errno;
 
-        d = fdopendir(fd);
+        d = take_fdopendir(&fd);
         if (!d)
                 return -errno;
-        fd = -1;
 
         FOREACH_DIRENT(de, d, return -errno)
                 return 0;
@@ -380,3 +380,36 @@ int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret
 
         return 0;
 }
+
+int proc_mounted(void) {
+        int r;
+
+        /* A quick check of procfs is properly mounted */
+
+        r = path_is_fs_type("/proc/", PROC_SUPER_MAGIC);
+        if (r == -ENOENT) /* not mounted at all */
+                return false;
+
+        return r;
+}
+
+bool stat_inode_unmodified(const struct stat *a, const struct stat *b) {
+
+        /* Returns if the specified stat structures reference the same, unmodified inode. This check tries to
+         * be reasonably careful when detecting changes: we check both inode and mtime, to cater for file
+         * systems where mtimes are fixed to 0 (think: ostree/nixos type installations). We also check file
+         * size, backing device, inode type and if this refers to a device not the major/minor.
+         *
+         * Note that we don't care if file attributes such as ownership or access mode change, this here is
+         * about contents of the file. The purpose here is to detect file contents changes, and nothing
+         * else. */
+
+        return a && b &&
+                (a->st_mode & S_IFMT) != 0 && /* We use the check for .st_mode if the structure was ever initialized */
+                ((a->st_mode ^ b->st_mode) & S_IFMT) == 0 &&  /* same inode type */
+                a->st_mtime == b->st_mtime &&
+                (!S_ISREG(a->st_mode) || a->st_size == b->st_size) && /* if regular file, compare file size */
+                a->st_dev == b->st_dev &&
+                a->st_ino == b->st_ino &&
+                (!(S_ISCHR(a->st_mode) || S_ISBLK(a->st_mode)) || a->st_rdev == b->st_rdev); /* if device node, also compare major/minor, because we can */
+}
index 7824af35006e5ed03b7a8296f6dd524f223532ce..59aedcb7c4d727bfa1ecce990e6148c3c6cb21be 100644 (file)
@@ -87,3 +87,7 @@ int fd_verify_directory(int fd);
 int device_path_make_major_minor(mode_t mode, dev_t devno, char **ret);
 int device_path_make_canonical(mode_t mode, dev_t devno, char **ret);
 int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret_devno);
+
+int proc_mounted(void);
+
+bool stat_inode_unmodified(const struct stat *a, const struct stat *b);
index 34931b03d8c4a0ce70e6433e126fc0116a182eda..0168cff886dcf262f2915ebd73f2b5a7ebcdca55 100644 (file)
@@ -4,12 +4,10 @@
 #include "string-util.h"
 
 ssize_t string_table_lookup(const char * const *table, size_t len, const char *key) {
-        size_t i;
-
         if (!key)
                 return -1;
 
-        for (i = 0; i < len; ++i)
+        for (size_t i = 0; i < len; ++i)
                 if (streq_ptr(table[i], key))
                         return (ssize_t) i;
 
index 93b7a220351e890b822cd6b8f3beccae6f47c925..755a37f6675aaf4abe6cc0f1e3356437ea3125d8 100644 (file)
 #include "util.h"
 
 int strcmp_ptr(const char *a, const char *b) {
-
         /* Like strcmp(), but tries to make sense of NULL pointers */
+
         if (a && b)
                 return strcmp(a, b);
+        return CMP(a, b); /* Direct comparison of pointers, one of which is NULL */
+}
 
-        if (!a && b)
-                return -1;
-
-        if (a && !b)
-                return 1;
+int strcasecmp_ptr(const char *a, const char *b) {
+        /* Like strcasecmp(), but tries to make sense of NULL pointers */
 
-        return 0;
+        if (a && b)
+                return strcasecmp(a, b);
+        return CMP(a, b); /* Direct comparison of pointers, one of which is NULL */
 }
 
 char* endswith(const char *s, const char *postfix) {
@@ -126,45 +127,58 @@ static size_t strcspn_escaped(const char *s, const char *reject) {
 }
 
 /* Split a string into words. */
-const char* split(const char **state, size_t *l, const char *separator, SplitFlags flags) {
+const char* split(
+                const char **state,
+                size_t *l,
+                const char *separator,
+                SplitFlags flags) {
+
         const char *current;
 
+        assert(state);
+        assert(l);
+
+        if (!separator)
+                separator = WHITESPACE;
+
         current = *state;
 
-        if (!*current) {
-                assert(**state == '\0');
+        if (*current == '\0') /* already at the end? */
                 return NULL;
-        }
 
-        current += strspn(current, separator);
-        if (!*current) {
+        current += strspn(current, separator); /* skip leading separators */
+        if (*current == '\0') { /* at the end now? */
                 *state = current;
                 return NULL;
         }
 
-        if (flags & SPLIT_QUOTES && strchr("\'\"", *current)) {
-                char quotechars[2] = {*current, '\0'};
-
-                *l = strcspn_escaped(current + 1, quotechars);
-                if (current[*l + 1] == '\0' || current[*l + 1] != quotechars[0] ||
-                    (current[*l + 2] && !strchr(separator, current[*l + 2]))) {
-                        /* right quote missing or garbage at the end */
-                        if (flags & SPLIT_RELAX) {
-                                *state = current + *l + 1 + (current[*l + 1] != '\0');
-                                return current + 1;
+        if (FLAGS_SET(flags, SPLIT_QUOTES)) {
+
+                if (strchr(QUOTES, *current)) {
+                        /* We are looking at a quote */
+                        *l = strcspn_escaped(current + 1, CHAR_TO_STR(*current));
+                        if (current[*l + 1] != *current ||
+                            (current[*l + 2] != 0 && !strchr(separator, current[*l + 2]))) {
+                                /* right quote missing or garbage at the end */
+                                if (FLAGS_SET(flags, SPLIT_RELAX)) {
+                                        *state = current + *l + 1 + (current[*l + 1] != '\0');
+                                        return current + 1;
+                                }
+                                *state = current;
+                                return NULL;
                         }
-                        *state = current;
-                        return NULL;
-                }
-                *state = current++ + *l + 2;
-        } else if (flags & SPLIT_QUOTES) {
-                *l = strcspn_escaped(current, separator);
-                if (current[*l] && !strchr(separator, current[*l]) && !(flags & SPLIT_RELAX)) {
-                        /* unfinished escape */
-                        *state = current;
-                        return NULL;
+                        *state = current++ + *l + 2;
+
+                } else {
+                        /* We are looking at a something that is not a quote */
+                        *l = strcspn_escaped(current, separator);
+                        if (current[*l] && !strchr(separator, current[*l]) && !FLAGS_SET(flags, SPLIT_RELAX)) {
+                                /* unfinished escape */
+                                *state = current;
+                                return NULL;
+                        }
+                        *state = current + *l;
                 }
-                *state = current + *l;
         } else {
                 *l = strcspn(current, separator);
                 *state = current + *l;
index f98fbddddadd411afdecaa5d2fed636c8ffb6dd9..09131455bf649df216175162a8d4cd22c48909fb 100644 (file)
@@ -27,6 +27,7 @@
 #define strncaseeq(a, b, n) (strncasecmp((a), (b), (n)) == 0)
 
 int strcmp_ptr(const char *a, const char *b) _pure_;
+int strcasecmp_ptr(const char *a, const char *b) _pure_;
 
 static inline bool streq_ptr(const char *a, const char *b) {
         return strcmp_ptr(a, b) == 0;
@@ -112,8 +113,10 @@ typedef enum SplitFlags {
         SPLIT_RELAX                      = 0x01 << 1,
 } SplitFlags;
 
+/* Smelly. Do not use this anymore. Use extract_first_word() instead! */
 const char* split(const char **state, size_t *l, const char *separator, SplitFlags flags);
 
+/* Similar, don't use this anymore */
 #define FOREACH_WORD(word, length, s, state)                            \
         _FOREACH_WORD(word, length, s, WHITESPACE, 0, state)
 
index 096cb4e5d4d2fa545d99dba10d468d36471e4d9f..858e1e62ecc3bc5ed6c585d5f8ab1795174e7a2f 100644 (file)
@@ -28,6 +28,18 @@ char *strv_find(char * const *l, const char *name) {
         return NULL;
 }
 
+char *strv_find_case(char * const *l, const char *name) {
+        char * const *i;
+
+        assert(name);
+
+        STRV_FOREACH(i, l)
+                if (strcaseeq(*i, name))
+                        return *i;
+
+        return NULL;
+}
+
 char *strv_find_prefix(char * const *l, const char *name) {
         char * const *i;
 
@@ -934,20 +946,20 @@ static int string_strv_hashmap_put_internal(Hashmap *h, const char *key, const c
         return 1;
 }
 
-int string_strv_hashmap_put(Hashmap **h, const char *key, const char *value) {
+int _string_strv_hashmap_put(Hashmap **h, const char *key, const char *value  HASHMAP_DEBUG_PARAMS) {
         int r;
 
-        r = hashmap_ensure_allocated(h, &string_strv_hash_ops);
+        r = _hashmap_ensure_allocated(h, &string_strv_hash_ops  HASHMAP_DEBUG_PASS_ARGS);
         if (r < 0)
                 return r;
 
         return string_strv_hashmap_put_internal(*h, key, value);
 }
 
-int string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value) {
+int _string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value  HASHMAP_DEBUG_PARAMS) {
         int r;
 
-        r = ordered_hashmap_ensure_allocated(h, &string_strv_hash_ops);
+        r = _ordered_hashmap_ensure_allocated(h, &string_strv_hash_ops  HASHMAP_DEBUG_PASS_ARGS);
         if (r < 0)
                 return r;
 
index 637a98121bd84707abcf902ea6a9873f007e7a86..2ad927bce5163df4e0beebc11d96c0e14ac68411 100644 (file)
 #include "string-util.h"
 
 char *strv_find(char * const *l, const char *name) _pure_;
+char *strv_find_case(char * const *l, const char *name) _pure_;
 char *strv_find_prefix(char * const *l, const char *name) _pure_;
 char *strv_find_startswith(char * const *l, const char *name) _pure_;
 
+#define strv_contains(l, s) (!!strv_find((l), (s)))
+#define strv_contains_case(l, s) (!!strv_find_case((l), (s)))
+
 char **strv_free(char **l);
 DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free);
 #define _cleanup_strv_free_ _cleanup_(strv_freep)
@@ -54,8 +58,6 @@ static inline bool strv_equal(char * const *a, char * const *b) {
         return strv_compare(a, b) == 0;
 }
 
-#define strv_contains(l, s) (!!strv_find((l), (s)))
-
 char **strv_new_internal(const char *x, ...) _sentinel_;
 char **strv_new_ap(const char *x, va_list ap);
 #define strv_new(...) strv_new_internal(__VA_ARGS__, NULL)
@@ -87,6 +89,16 @@ char **strv_parse_nulstr(const char *s, size_t l);
 char **strv_split_nulstr(const char *s);
 int strv_make_nulstr(char * const *l, char **p, size_t *n);
 
+static inline int strv_from_nulstr(char ***a, const char *nulstr) {
+        char **t;
+
+        t = strv_split_nulstr(nulstr);
+        if (!t)
+                return -ENOMEM;
+        *a = t;
+        return 0;
+}
+
 bool strv_overlap(char * const *a, char * const *b) _pure_;
 
 #define STRV_FOREACH(s, l)                      \
@@ -94,7 +106,7 @@ bool strv_overlap(char * const *a, char * const *b) _pure_;
 
 #define STRV_FOREACH_BACKWARDS(s, l)                                \
         for (s = ({                                                 \
-                        char **_l = l;                              \
+                        typeof(l) _l = l;                           \
                         _l ? _l + strv_length(_l) - 1U : NULL;      \
                         });                                         \
              (l) && ((s) >= (l));                                   \
@@ -146,6 +158,13 @@ void strv_print(char * const *l);
                 _x && strv_contains(STRV_MAKE(__VA_ARGS__), _x); \
         })
 
+#define STRCASE_IN_SET(x, ...) strv_contains_case(STRV_MAKE(__VA_ARGS__), x)
+#define STRCASEPTR_IN_SET(x, ...)                                    \
+        ({                                                       \
+                const char* _x = (x);                            \
+                _x && strv_contains_case(STRV_MAKE(__VA_ARGS__), _x); \
+        })
+
 #define STARTSWITH_SET(p, ...)                                  \
         ({                                                      \
                 const char *_p = (p);                           \
@@ -207,5 +226,7 @@ int fputstrv(FILE *f, char * const *l, const char *separator, bool *space);
         })
 
 extern const struct hash_ops string_strv_hash_ops;
-int string_strv_hashmap_put(Hashmap **h, const char *key, const char *value);
-int string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value);
+int _string_strv_hashmap_put(Hashmap **h, const char *key, const char *value  HASHMAP_DEBUG_PARAMS);
+int _string_strv_ordered_hashmap_put(OrderedHashmap **h, const char *key, const char *value  HASHMAP_DEBUG_PARAMS);
+#define string_strv_hashmap_put(h, k, v) _string_strv_hashmap_put(h, k, v  HASHMAP_DEBUG_SRC_ARGS)
+#define string_strv_ordered_hashmap_put(h, k, v) _string_strv_ordered_hashmap_put(h, k, v  HASHMAP_DEBUG_SRC_ARGS)
index efc22b1591773a066ff8c56c19ac60533b582beb..67634b4d4698a377b1050c8502f10d3ffada14b4 100644 (file)
 #define ANSI_HIGHLIGHT_YELLOW4           "\x1B[0;1;38;5;100m"
 
 /* Underlined */
+#define ANSI_GREY_UNDERLINE              "\x1B[0;4;38;5;245m"
 #define ANSI_HIGHLIGHT_RED_UNDERLINE     "\x1B[0;1;4;31m"
 #define ANSI_HIGHLIGHT_GREEN_UNDERLINE   "\x1B[0;1;4;32m"
-#define ANSI_HIGHLIGHT_YELLOW_UNDERLINE  "\x1B[0;1;4;33m"
+#define ANSI_HIGHLIGHT_YELLOW_UNDERLINE  "\x1B[0;1;4;38;5;185m"
 #define ANSI_HIGHLIGHT_BLUE_UNDERLINE    "\x1B[0;1;4;34m"
 #define ANSI_HIGHLIGHT_MAGENTA_UNDERLINE "\x1B[0;1;4;35m"
 #define ANSI_HIGHLIGHT_GREY_UNDERLINE    "\x1B[0;1;4;38;5;245m"
@@ -138,6 +139,7 @@ DEFINE_ANSI_FUNC(highlight_grey,    HIGHLIGHT_GREY);
 
 DEFINE_ANSI_FUNC_UNDERLINE(underline,                   UNDERLINE, NORMAL);
 DEFINE_ANSI_FUNC_UNDERLINE(highlight_underline,         HIGHLIGHT_UNDERLINE, HIGHLIGHT);
+DEFINE_ANSI_FUNC_UNDERLINE(grey_underline,              GREY_UNDERLINE, GREY);
 DEFINE_ANSI_FUNC_UNDERLINE(highlight_red_underline,     HIGHLIGHT_RED_UNDERLINE, HIGHLIGHT_RED);
 DEFINE_ANSI_FUNC_UNDERLINE(highlight_green_underline,   HIGHLIGHT_GREEN_UNDERLINE, HIGHLIGHT_GREEN);
 DEFINE_ANSI_FUNC_UNDERLINE(highlight_yellow_underline,  HIGHLIGHT_YELLOW_UNDERLINE, HIGHLIGHT_YELLOW);
index 105584e2e72ff91aed4369bb1503367b0085908e..15cc1b885121b0c78a57c11f85bf070183648842 100644 (file)
@@ -82,43 +82,82 @@ triple_timestamp* triple_timestamp_get(triple_timestamp *ts) {
         return ts;
 }
 
+static usec_t map_clock_usec_internal(usec_t from, usec_t from_base, usec_t to_base) {
+
+        /* Maps the time 'from' between two clocks, based on a common reference point where the first clock
+         * is at 'from_base' and the second clock at 'to_base'. Basically calculates:
+         *
+         *         from - from_base + to_base
+         *
+         * But takes care of overflows/underflows and avoids signed operations. */
+
+        if (from >= from_base) { /* In the future */
+                usec_t delta = from - from_base;
+
+                if (to_base >= USEC_INFINITY - delta) /* overflow? */
+                        return USEC_INFINITY;
+
+                return to_base + delta;
+
+        } else { /* In the past */
+                usec_t delta = from_base - from;
+
+                if (to_base <= delta) /* underflow? */
+                        return 0;
+
+                return to_base - delta;
+        }
+}
+
+usec_t map_clock_usec(usec_t from, clockid_t from_clock, clockid_t to_clock) {
+
+        /* Try to avoid any inaccuracy needlessly added in case we convert from effectively the same clock
+         * onto itself */
+        if (map_clock_id(from_clock) == map_clock_id(to_clock))
+                return from;
+
+        /* Keep infinity as is */
+        if (from == USEC_INFINITY)
+                return from;
+
+        return map_clock_usec_internal(from, now(from_clock), now(to_clock));
+}
+
 dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
-        int64_t delta;
         assert(ts);
 
-        if (u == USEC_INFINITY || u <= 0) {
+        if (u == USEC_INFINITY || u == 0) {
                 ts->realtime = ts->monotonic = u;
                 return ts;
         }
 
         ts->realtime = u;
-
-        delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
-        ts->monotonic = usec_sub_signed(now(CLOCK_MONOTONIC), delta);
-
+        ts->monotonic = map_clock_usec(u, CLOCK_REALTIME, CLOCK_MONOTONIC);
         return ts;
 }
 
 triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u) {
-        int64_t delta;
+        usec_t nowr;
 
         assert(ts);
 
-        if (u == USEC_INFINITY || u <= 0) {
+        if (u == USEC_INFINITY || u == 0) {
                 ts->realtime = ts->monotonic = ts->boottime = u;
                 return ts;
         }
 
+        nowr = now(CLOCK_REALTIME);
+
         ts->realtime = u;
-        delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
-        ts->monotonic = usec_sub_signed(now(CLOCK_MONOTONIC), delta);
-        ts->boottime = clock_boottime_supported() ? usec_sub_signed(now(CLOCK_BOOTTIME), delta) : USEC_INFINITY;
+        ts->monotonic = map_clock_usec_internal(u, nowr, now(CLOCK_MONOTONIC));
+        ts->boottime = clock_boottime_supported() ?
+                map_clock_usec_internal(u, nowr, now(CLOCK_BOOTTIME)) :
+                USEC_INFINITY;
 
         return ts;
 }
 
 dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
-        int64_t delta;
         assert(ts);
 
         if (u == USEC_INFINITY) {
@@ -127,25 +166,28 @@ dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
         }
 
         ts->monotonic = u;
-        delta = (int64_t) now(CLOCK_MONOTONIC) - (int64_t) u;
-        ts->realtime = usec_sub_signed(now(CLOCK_REALTIME), delta);
-
+        ts->realtime = map_clock_usec(u, CLOCK_MONOTONIC, CLOCK_REALTIME);
         return ts;
 }
 
 dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u) {
-        int64_t delta;
+        clockid_t cid;
+        usec_t nowm;
 
         if (u == USEC_INFINITY) {
                 ts->realtime = ts->monotonic = USEC_INFINITY;
                 return ts;
         }
 
-        dual_timestamp_get(ts);
-        delta = (int64_t) now(clock_boottime_or_monotonic()) - (int64_t) u;
-        ts->realtime = usec_sub_signed(ts->realtime, delta);
-        ts->monotonic = usec_sub_signed(ts->monotonic, delta);
+        cid = clock_boottime_or_monotonic();
+        nowm = now(cid);
+
+        if (cid == CLOCK_MONOTONIC)
+                ts->monotonic = u;
+        else
+                ts->monotonic = map_clock_usec_internal(u, nowm, now(CLOCK_MONOTONIC));
 
+        ts->realtime = map_clock_usec_internal(u, nowm, now(CLOCK_REALTIME));
         return ts;
 }
 
index 4c371257e332c7056407afe2cfff01c516e04102..9bbe9863062cae405984748365b60ebb7ca8cae9 100644 (file)
@@ -29,8 +29,8 @@ typedef struct triple_timestamp {
         usec_t boottime;
 } triple_timestamp;
 
-#define USEC_INFINITY ((usec_t) -1)
-#define NSEC_INFINITY ((nsec_t) -1)
+#define USEC_INFINITY ((usec_t) UINT64_MAX)
+#define NSEC_INFINITY ((nsec_t) UINT64_MAX)
 
 #define MSEC_PER_SEC  1000ULL
 #define USEC_PER_SEC  ((usec_t) 1000000ULL)
@@ -67,6 +67,8 @@ typedef struct triple_timestamp {
 usec_t now(clockid_t clock);
 nsec_t now_nsec(clockid_t clock);
 
+usec_t map_clock_usec(usec_t from, clockid_t from_clock, clockid_t to_clock);
+
 dual_timestamp* dual_timestamp_get(dual_timestamp *ts);
 dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u);
 dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u);
index decdafb9c958159b1ef0c1ec2db32e90c57c71f8..a49f7eee702efa7778534e7fcf590d8459926a1e 100644 (file)
@@ -48,14 +48,12 @@ int fopen_temporary(const char *path, FILE **ret_f, char **ret_temp_path) {
         /* This assumes that returned FILE object is short-lived and used within the same single-threaded
          * context and never shared externally, hence locking is not necessary. */
 
-        r = fdopen_unlocked(fd, "w", &f);
+        r = take_fdopen_unlocked(&fd, "w", &f);
         if (r < 0) {
                 (void) unlink(t);
                 return r;
         }
 
-        TAKE_FD(fd);
-
         if (ret_f)
                 *ret_f = TAKE_PTR(f);
 
@@ -80,18 +78,16 @@ int mkostemp_safe(char *pattern) {
 }
 
 int fmkostemp_safe(char *pattern, const char *mode, FILE **ret_f) {
-        int fd;
+        _cleanup_close_ int fd = -1;
         FILE *f;
 
         fd = mkostemp_safe(pattern);
         if (fd < 0)
                 return fd;
 
-        f = fdopen(fd, mode);
-        if (!f) {
-                safe_close(fd);
+        f = take_fdopen(&fd, mode);
+        if (!f)
                 return -errno;
-        }
 
         *ret_f = f;
         return 0;
@@ -261,7 +257,7 @@ int open_tmpfile_linkable(const char *target, int flags, char **ret_path) {
         assert((flags & O_EXCL) == 0);
 
         /* Creates a temporary file, that shall be renamed to "target" later. If possible, this uses O_TMPFILE – in
-         * which case "ret_path" will be returned as NULL. If not possible a the tempoary path name used is returned in
+         * which case "ret_path" will be returned as NULL. If not possible the temporary path name used is returned in
          * "ret_path". Use link_tmpfile() below to rename the result after writing the file in full. */
 
         fd = open_parent(target, O_TMPFILE|flags, 0640);
index dba218b38869f755bc3f49a67f663cc834f7d200..94ec1f3d19846307442a2c453ff653c7b00ce730 100644 (file)
@@ -108,6 +108,15 @@ static const char* const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = {
 
 DEFINE_STRING_TABLE_LOOKUP(unit_active_state, UnitActiveState);
 
+static const char* const freezer_state_table[_FREEZER_STATE_MAX] = {
+        [FREEZER_RUNNING] = "running",
+        [FREEZER_FREEZING] = "freezing",
+        [FREEZER_FROZEN] = "frozen",
+        [FREEZER_THAWING] = "thawing",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(freezer_state, FreezerState);
+
 static const char* const automount_state_table[_AUTOMOUNT_STATE_MAX] = {
         [AUTOMOUNT_DEAD] = "dead",
         [AUTOMOUNT_WAITING] = "waiting",
@@ -176,6 +185,7 @@ static const char* const service_state_table[_SERVICE_STATE_MAX] = {
         [SERVICE_STOP_SIGTERM] = "stop-sigterm",
         [SERVICE_STOP_SIGKILL] = "stop-sigkill",
         [SERVICE_STOP_POST] = "stop-post",
+        [SERVICE_FINAL_WATCHDOG] = "final-watchdog",
         [SERVICE_FINAL_SIGTERM] = "final-sigterm",
         [SERVICE_FINAL_SIGKILL] = "final-sigkill",
         [SERVICE_FAILED] = "failed",
index 5979819dc095240812b5f95de62ab52e3a9a8ffa..53419ecd8a187f9b5f962648ac356bfc84c1f5b7 100644 (file)
@@ -7,7 +7,7 @@
 
 /* The enum order is used to order unit jobs in the job queue
  * when other criteria (cpu weight, nice level) are identical.
- * In this case service units have the hightest priority. */
+ * In this case service units have the highest priority. */
 typedef enum UnitType {
         UNIT_SERVICE = 0,
         UNIT_MOUNT,
@@ -48,6 +48,15 @@ typedef enum UnitActiveState {
         _UNIT_ACTIVE_STATE_INVALID = -1
 } UnitActiveState;
 
+typedef enum FreezerState {
+        FREEZER_RUNNING,
+        FREEZER_FREEZING,
+        FREEZER_FROZEN,
+        FREEZER_THAWING,
+        _FREEZER_STATE_MAX,
+        _FREEZER_STATE_INVALID = -1
+} FreezerState;
+
 typedef enum AutomountState {
         AUTOMOUNT_DEAD,
         AUTOMOUNT_WAITING,
@@ -118,6 +127,7 @@ typedef enum ServiceState {
         SERVICE_STOP_SIGTERM,
         SERVICE_STOP_SIGKILL,
         SERVICE_STOP_POST,
+        SERVICE_FINAL_WATCHDOG,    /* In case the STOP_POST executable needs to be aborted. */
         SERVICE_FINAL_SIGTERM,     /* In case the STOP_POST executable hangs, we shoot that down, too */
         SERVICE_FINAL_SIGKILL,
         SERVICE_FAILED,
@@ -253,6 +263,9 @@ UnitLoadState unit_load_state_from_string(const char *s) _pure_;
 const char *unit_active_state_to_string(UnitActiveState i) _const_;
 UnitActiveState unit_active_state_from_string(const char *s) _pure_;
 
+const char *freezer_state_to_string(FreezerState i) _const_;
+FreezerState freezer_state_from_string(const char *s) _pure_;
+
 const char* automount_state_to_string(AutomountState i) _const_;
 AutomountState automount_state_from_string(const char *s) _pure_;
 
index 521364124fa3a5cdaecff2b9c5a9ec39589397bd..43d8b3477e17ed4ece831387afdf4aa63ac7cf24 100644 (file)
@@ -207,8 +207,9 @@ UnitType unit_name_to_type(const char *n) {
 }
 
 int unit_name_change_suffix(const char *n, const char *suffix, char **ret) {
-        char *e, *s;
+        _cleanup_free_ char *s = NULL;
         size_t a, b;
+        char *e;
 
         assert(n);
         assert(suffix);
@@ -230,8 +231,12 @@ int unit_name_change_suffix(const char *n, const char *suffix, char **ret) {
                 return -ENOMEM;
 
         strcpy(mempcpy(s, n, a), suffix);
-        *ret = s;
 
+        /* Make sure the name is still valid (i.e. didn't grow too large due to longer suffix) */
+        if (!unit_name_is_valid(s, UNIT_NAME_ANY))
+                return -EINVAL;
+
+        *ret = TAKE_PTR(s);
         return 0;
 }
 
@@ -253,8 +258,8 @@ int unit_name_build(const char *prefix, const char *instance, const char *suffix
 }
 
 int unit_name_build_from_type(const char *prefix, const char *instance, UnitType type, char **ret) {
+        _cleanup_free_ char *s = NULL;
         const char *ut;
-        char *s;
 
         assert(prefix);
         assert(type >= 0);
@@ -264,19 +269,23 @@ int unit_name_build_from_type(const char *prefix, const char *instance, UnitType
         if (!unit_prefix_is_valid(prefix))
                 return -EINVAL;
 
-        if (instance && !unit_instance_is_valid(instance))
-                return -EINVAL;
-
         ut = unit_type_to_string(type);
 
-        if (!instance)
-                s = strjoin(prefix, ".", ut);
-        else
+        if (instance) {
+                if (!unit_instance_is_valid(instance))
+                        return -EINVAL;
+
                 s = strjoin(prefix, "@", instance, ".", ut);
+        } else
+                s = strjoin(prefix, ".", ut);
         if (!s)
                 return -ENOMEM;
 
-        *ret = s;
+        /* Verify that this didn't grow too large (or otherwise is invalid) */
+        if (!unit_name_is_valid(s, instance ? UNIT_NAME_INSTANCE : UNIT_NAME_PLAIN))
+                return -EINVAL;
+
+        *ret = TAKE_PTR(s);
         return 0;
 }
 
@@ -441,8 +450,8 @@ int unit_name_path_unescape(const char *f, char **ret) {
 }
 
 int unit_name_replace_instance(const char *f, const char *i, char **ret) {
+        _cleanup_free_ char *s = NULL;
         const char *p, *e;
-        char *s;
         size_t a, b;
 
         assert(f);
@@ -466,7 +475,11 @@ int unit_name_replace_instance(const char *f, const char *i, char **ret) {
 
         strcpy(mempcpy(mempcpy(s, f, a + 1), i, b), e);
 
-        *ret = s;
+        /* Make sure the resulting name still is valid, i.e. didn't grow too large */
+        if (!unit_name_is_valid(s, UNIT_NAME_INSTANCE))
+                return -EINVAL;
+
+        *ret = TAKE_PTR(s);
         return 0;
 }
 
@@ -497,8 +510,7 @@ int unit_name_template(const char *f, char **ret) {
 }
 
 int unit_name_from_path(const char *path, const char *suffix, char **ret) {
-        _cleanup_free_ char *p = NULL;
-        char *s = NULL;
+        _cleanup_free_ char *p = NULL, *s = NULL;
         int r;
 
         assert(path);
@@ -516,13 +528,16 @@ int unit_name_from_path(const char *path, const char *suffix, char **ret) {
         if (!s)
                 return -ENOMEM;
 
-        *ret = s;
+        /* Refuse this if this got too long or for some other reason didn't result in a valid name */
+        if (!unit_name_is_valid(s, UNIT_NAME_PLAIN))
+                return -EINVAL;
+
+        *ret = TAKE_PTR(s);
         return 0;
 }
 
 int unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix, char **ret) {
-        _cleanup_free_ char *p = NULL;
-        char *s;
+        _cleanup_free_ char *p = NULL, *s = NULL;
         int r;
 
         assert(prefix);
@@ -544,7 +559,11 @@ int unit_name_from_path_instance(const char *prefix, const char *path, const cha
         if (!s)
                 return -ENOMEM;
 
-        *ret = s;
+        /* Refuse this if this got too long or for some other reason didn't result in a valid name */
+        if (!unit_name_is_valid(s, UNIT_NAME_INSTANCE))
+                return -EINVAL;
+
+        *ret = TAKE_PTR(s);
         return 0;
 }
 
@@ -597,9 +616,9 @@ static bool do_escape_mangle(const char *f, bool allow_globs, char *t) {
  *  If @allow_globs, globs characters are preserved. Otherwise, they are escaped.
  */
 int unit_name_mangle_with_suffix(const char *name, const char *operation, UnitNameMangle flags, const char *suffix, char **ret) {
-        char *s;
-        int r;
+        _cleanup_free_ char *s = NULL;
         bool mangled, suggest_escape = true;
+        int r;
 
         assert(name);
         assert(suffix);
@@ -657,7 +676,12 @@ int unit_name_mangle_with_suffix(const char *name, const char *operation, UnitNa
         if ((!(flags & UNIT_NAME_MANGLE_GLOB) || !string_is_glob(s)) && unit_name_to_type(s) < 0)
                 strcat(s, suffix);
 
-        *ret = s;
+        /* Make sure mangling didn't grow this too large (but don't do this check if globbing is allowed,
+         * since globs generally do not qualify as valid unit names) */
+        if (!FLAGS_SET(flags, UNIT_NAME_MANGLE_GLOB) && !unit_name_is_valid(s, UNIT_NAME_ANY))
+                return -EINVAL;
+
+        *ret = TAKE_PTR(s);
         return 1;
 
 good:
@@ -665,12 +689,13 @@ good:
         if (!s)
                 return -ENOMEM;
 
-        *ret = s;
+        *ret = TAKE_PTR(s);
         return 0;
 }
 
 int slice_build_parent_slice(const char *slice, char **ret) {
-        char *s, *dash;
+        _cleanup_free_ char *s = NULL;
+        char *dash;
         int r;
 
         assert(slice);
@@ -693,13 +718,11 @@ int slice_build_parent_slice(const char *slice, char **ret) {
                 strcpy(dash, ".slice");
         else {
                 r = free_and_strdup(&s, SPECIAL_ROOT_SLICE);
-                if (r < 0) {
-                        free(s);
+                if (r < 0)
                         return r;
-                }
         }
 
-        *ret = s;
+        *ret = TAKE_PTR(s);
         return 1;
 }
 
index 7dd2f6664af6afee792110f95fd64e4ba77482b6..8115065b5eca119a8515a776cb27428ee9d76b18 100644 (file)
@@ -290,7 +290,7 @@ int get_user_creds(
                     (empty_or_root(p->pw_dir) ||
                      !path_is_valid(p->pw_dir) ||
                      !path_is_absolute(p->pw_dir)))
-                    *home = NULL; /* Note: we don't insist on normalized paths, since there are setups that have /./ in the path */
+                        *home = NULL; /* Note: we don't insist on normalized paths, since there are setups that have /./ in the path */
                 else
                         *home = p->pw_dir;
         }
@@ -777,7 +777,7 @@ bool valid_user_group_name(const char *u, ValidUserFlags flags) {
                         return false;
 
                 if (in_charset(u, "0123456789")) /* Don't allow fully numeric strings, they might be confused
-                                                  * with with UIDs (note that this test is more broad than
+                                                  * with UIDs (note that this test is more broad than
                                                   * the parse_uid() test above, as it will cover more than
                                                   * the 32bit range, and it will detect 65535 (which is in
                                                   * invalid UID, even though in the unsigned 32 bit range) */
index f567696265a0399f10f91567d85227d010941e76..c6bff6b16e814a3d50968e3f7d4622eccdfee7f3 100644 (file)
@@ -441,6 +441,7 @@ static const char *const container_table[_VIRTUALIZATION_MAX] = {
         [VIRTUALIZATION_PODMAN]         = "podman",
         [VIRTUALIZATION_RKT]            = "rkt",
         [VIRTUALIZATION_WSL]            = "wsl",
+        [VIRTUALIZATION_PROOT]          = "proot",
 };
 
 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(container, int);
@@ -449,6 +450,7 @@ int detect_container(void) {
         static thread_local int cached_found = _VIRTUALIZATION_INVALID;
         _cleanup_free_ char *m = NULL;
         _cleanup_free_ char *o = NULL;
+        _cleanup_free_ char *p = NULL;
         const char *e = NULL;
         int r;
 
@@ -462,16 +464,32 @@ int detect_container(void) {
                 goto finish;
         }
 
-        /* "Official" way of detecting WSL https://github.com/Microsoft/WSL/issues/423#issuecomment-221627364,
-         * ... and a working one, since the official one doesn't actually work ;(
-         */
+        /* "Official" way of detecting WSL https://github.com/Microsoft/WSL/issues/423#issuecomment-221627364 */
         r = read_one_line_file("/proc/sys/kernel/osrelease", &o);
         if (r >= 0 &&
-            (strstr(o, "Microsoft") || strstr(o, "microsoft") || strstr(o, "WSL"))) {
+            (strstr(o, "Microsoft") || strstr(o, "WSL"))) {
                 r = VIRTUALIZATION_WSL;
                 goto finish;
         }
 
+        /* proot doesn't use PID namespacing, so we can just check if we have a matching tracer for this
+         * invocation without worrying about it being elsewhere.
+         */
+        r = get_proc_field("/proc/self/status", "TracerPid", WHITESPACE, &p);
+        if (r == 0 && !streq(p, "0")) {
+                pid_t ptrace_pid;
+                r = parse_pid(p, &ptrace_pid);
+                if (r == 0) {
+                        const char *pf = procfs_file_alloca(ptrace_pid, "comm");
+                        _cleanup_free_ char *ptrace_comm = NULL;
+                        r = read_one_line_file(pf, &ptrace_comm);
+                        if (r >= 0 && startswith(ptrace_comm, "proot")) {
+                                r = VIRTUALIZATION_PROOT;
+                                goto finish;
+                        }
+                }
+        }
+
         if (getpid_cached() == 1) {
                 /* If we are PID 1 we can just check our own environment variable, and that's authoritative.
                  * We distinguish three cases:
@@ -660,6 +678,7 @@ static const char *const virtualization_table[_VIRTUALIZATION_MAX] = {
         [VIRTUALIZATION_PODMAN] = "podman",
         [VIRTUALIZATION_RKT] = "rkt",
         [VIRTUALIZATION_WSL] = "wsl",
+        [VIRTUALIZATION_PROOT] = "proot",
         [VIRTUALIZATION_CONTAINER_OTHER] = "container-other",
 };
 
index 26f409afd018576f3ec17fdd6261fe900b6bf936..d58c582c912f11f27f84c6c9222e1bdea287585c 100644 (file)
@@ -34,6 +34,7 @@ enum {
         VIRTUALIZATION_PODMAN,
         VIRTUALIZATION_RKT,
         VIRTUALIZATION_WSL,
+        VIRTUALIZATION_PROOT,
         VIRTUALIZATION_CONTAINER_OTHER,
         VIRTUALIZATION_CONTAINER_LAST = VIRTUALIZATION_CONTAINER_OTHER,
 
index 3525021839faa08552c43713d5c48f5e48357980..a663fc5c2d3ec748f9320e4d4ac5ed6f126ff993 100644 (file)
@@ -982,8 +982,9 @@ static int install_loader_config(const char *esp_path, sd_id128_t machine_id) {
         char machine_string[SD_ID128_STRING_MAX];
         _cleanup_(unlink_and_freep) char *t = NULL;
         _cleanup_fclose_ FILE *f = NULL;
+        _cleanup_close_ int fd = -1;
         const char *p;
-        int r, fd;
+        int r;
 
         p = prefix_roota(esp_path, "/loader/loader.conf");
         if (access(p, F_OK) >= 0) /* Silently skip creation if the file already exists (early check) */
@@ -993,11 +994,9 @@ static int install_loader_config(const char *esp_path, sd_id128_t machine_id) {
         if (fd < 0)
                 return log_error_errno(fd, "Failed to open \"%s\" for writing: %m", p);
 
-        f = fdopen(fd, "w");
-        if (!f) {
-                safe_close(fd);
+        f = take_fdopen(&fd, "w");
+        if (!f)
                 return log_oom();
-        }
 
         fprintf(f, "#timeout 3\n"
                    "#console-mode keep\n"
@@ -1007,7 +1006,7 @@ static int install_loader_config(const char *esp_path, sd_id128_t machine_id) {
         if (r < 0)
                 return log_error_errno(r, "Failed to write \"%s\": %m", p);
 
-        r = link_tmpfile(fd, t, p);
+        r = link_tmpfile(fileno(f), t, p);
         if (r == -EEXIST)
                 return 0; /* Silently skip creation if the file exists now (recheck) */
         if (r < 0)
@@ -1042,7 +1041,10 @@ static int help(int argc, char *argv[], void *userdata) {
                "  remove              Remove systemd-boot from the ESP and EFI variables\n"
                "  is-installed        Test whether systemd-boot is installed in the ESP\n"
                "  random-seed         Initialize random seed in ESP and EFI variables\n"
-               "  systemd-efi-options Query or set system options string in EFI variable\n"
+               "  systemd-efi-options [STRING]\n"
+               "                      Query or set system options string in EFI variable\n"
+               "  reboot-to-firmware [BOOL]\n"
+               "                      Query or set reboot-to-firmware EFI flag\n"
                "\nBoot Loader Entries Commands:\n"
                "  list                List boot loader entries\n"
                "  set-default ID      Set default boot loader entry\n"
@@ -1246,6 +1248,18 @@ static int verb_status(int argc, char *argv[], void *userdata) {
                 printf("     Firmware: %s%s (%s)%s\n", ansi_highlight(), strna(fw_type), strna(fw_info), ansi_normal());
                 printf("  Secure Boot: %sd\n", enable_disable(is_efi_secure_boot()));
                 printf("   Setup Mode: %s\n", is_efi_secure_boot_setup_mode() ? "setup" : "user");
+
+                r = efi_get_reboot_to_firmware();
+                if (r > 0)
+                        printf(" Boot into FW: %sactive%s\n", ansi_highlight_yellow(), ansi_normal());
+                else if (r == 0)
+                        printf(" Boot into FW: supported\n");
+                else if (r == -EOPNOTSUPP)
+                        printf(" Boot into FW: not supported\n");
+                else {
+                        errno = -r;
+                        printf(" Boot into FW: %sfailed%s (%m)\n", ansi_highlight_red(), ansi_normal());
+                }
                 printf("\n");
 
                 printf("Current Boot Loader:\n");
@@ -1312,6 +1326,7 @@ static int verb_status(int argc, char *argv[], void *userdata) {
 
 static int verb_list(int argc, char *argv[], void *userdata) {
         _cleanup_(boot_config_free) BootConfig config = {};
+        _cleanup_strv_free_ char **efi_entries = NULL;
         int r;
 
         /* If we lack privileges we invoke find_esp_and_warn() in "unprivileged mode" here, which does two things: turn
@@ -1334,7 +1349,13 @@ static int verb_list(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return r;
 
-        (void) boot_entries_augment_from_loader(&config, false);
+        r = efi_loader_get_entries(&efi_entries);
+        if (r == -ENOENT || ERRNO_IS_NOT_SUPPORTED(r))
+                log_debug_errno(r, "Boot loader reported no entries.");
+        else if (r < 0)
+                log_warning_errno(r, "Failed to determine entries reported by boot loader, ignoring: %m");
+        else
+                (void) boot_entries_augment_from_loader(&config, efi_entries, false);
 
         if (config.n_entries == 0)
                 log_info("No boot loader entries found.");
@@ -1436,7 +1457,7 @@ static int install_random_seed(const char *esp) {
                          * the EFI variable space we can make sure that even though the random seeds on disk
                          * are all the same they will be different on each system under the assumption that
                          * the EFI variable space is maintained separate from the random seed storage. That
-                         * is generally the case on physical systems, as the ESP is stored on persistant
+                         * is generally the case on physical systems, as the ESP is stored on persistent
                          * storage, and the EFI variables in NVRAM. However in virtualized environments this
                          * is generally not true: the EFI variable set is typically stored along with the
                          * disk image itself. For example, using the OVMF EFI firmware the EFI variables are
@@ -1767,6 +1788,39 @@ static int verb_systemd_efi_options(int argc, char *argv[], void *userdata) {
         return 0;
 }
 
+static int verb_reboot_to_firmware(int argc, char *argv[], void *userdata) {
+        int r;
+
+        if (argc < 2) {
+                r = efi_get_reboot_to_firmware();
+                if (r > 0) {
+                        puts("active");
+                        return EXIT_SUCCESS; /* success */
+                }
+                if (r == 0) {
+                        puts("supported");
+                        return 1; /* recognizable error #1 */
+                }
+                if (r == -EOPNOTSUPP) {
+                        puts("not supported");
+                        return 2; /* recognizable error #2 */
+                }
+
+                log_error_errno(r, "Failed to query reboot-to-firmware state: %m");
+                return 3; /* other kind of error */
+        } else {
+                r = parse_boolean(argv[1]);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to parse argument: %s", argv[1]);
+
+                r = efi_set_reboot_to_firmware(r);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to set reboot-to-firmware option: %m");
+
+                return 0;
+        }
+}
+
 static int bootctl_main(int argc, char *argv[]) {
         static const Verb verbs[] = {
                 { "help",                VERB_ANY, VERB_ANY, 0,            help                     },
@@ -1780,6 +1834,7 @@ static int bootctl_main(int argc, char *argv[]) {
                 { "set-oneshot",         2,        2,        0,            verb_set_default         },
                 { "random-seed",         VERB_ANY, 1,        0,            verb_random_seed         },
                 { "systemd-efi-options", VERB_ANY, 2,        0,            verb_systemd_efi_options },
+                { "reboot-to-firmware",  VERB_ANY, 2,        0,            verb_reboot_to_firmware  },
                 {}
         };
 
@@ -1803,4 +1858,4 @@ static int run(int argc, char *argv[]) {
         return bootctl_main(argc, argv);
 }
 
-DEFINE_MAIN_FUNCTION(run);
+DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);
index 99938c547a25cd7d846aa4c9707c57fdcb07f378..5189d86d1a90f1ba03955e7d39a2b4e3f24de27f 100644 (file)
@@ -272,6 +272,8 @@ static BOOLEAN line_edit(
 
                 case KEYPRESS(0, 0, CHAR_LINEFEED):
                 case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN):
+                case KEYPRESS(0, CHAR_CARRIAGE_RETURN, 0): /* EZpad Mini 4s firmware sends malformed events */
+                case KEYPRESS(0, CHAR_CARRIAGE_RETURN, CHAR_CARRIAGE_RETURN): /* Teclast X98+ II firmware sends malformed events */
                         if (StrCmp(line, line_in) != 0)
                                 *line_out = TAKE_PTR(line);
                         enter = TRUE;
@@ -742,6 +744,9 @@ static BOOLEAN menu_run(
 
                 case KEYPRESS(0, 0, CHAR_LINEFEED):
                 case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN):
+                case KEYPRESS(0, CHAR_CARRIAGE_RETURN, 0): /* EZpad Mini 4s firmware sends malformed events */
+                case KEYPRESS(0, CHAR_CARRIAGE_RETURN, CHAR_CARRIAGE_RETURN): /* Teclast X98+ II firmware sends malformed events */
+                case KEYPRESS(0, SCAN_RIGHT, 0):
                         exit = TRUE;
                         break;
 
@@ -1200,7 +1205,7 @@ static VOID config_entry_parse_tries(
                         }
 
                         new_factor = factor * 10;
-                        if (new_factor < factor) /* overflow chck */
+                        if (new_factor < factor) /* overflow check */
                                 return;
 
                         factor = new_factor;
@@ -1518,11 +1523,11 @@ static VOID config_load_entries(
 static INTN config_entry_compare(ConfigEntry *a, ConfigEntry *b) {
         INTN r;
 
-        /* Order entries that have no tries left to the end of the list */
+        /* Order entries that have no tries left to the beginning of the list */
         if (a->tries_left != 0 && b->tries_left == 0)
-                return -1;
-        if (a->tries_left == 0 && b->tries_left != 0)
                 return 1;
+        if (a->tries_left == 0 && b->tries_left != 0)
+                return -1;
 
         r = str_verscmp(a->id, b->id);
         if (r != 0)
@@ -1532,17 +1537,17 @@ static INTN config_entry_compare(ConfigEntry *a, ConfigEntry *b) {
             b->tries_left == (UINTN) -1)
                 return 0;
 
-        /* If both items have boot counting, and otherwise are identical, put the entry with more tries left first */
+        /* If both items have boot counting, and otherwise are identical, put the entry with more tries left last */
         if (a->tries_left > b->tries_left)
-                return -1;
-        if (a->tries_left < b->tries_left)
                 return 1;
+        if (a->tries_left < b->tries_left)
+                return -1;
 
         /* If they have the same number of tries left, then let the one win which was tried fewer times so far */
         if (a->tries_done < b->tries_done)
-                return -1;
-        if (a->tries_done > b->tries_done)
                 return 1;
+        if (a->tries_done > b->tries_done)
+                return -1;
 
         return 0;
 }
index 768cb51d09e3a09ce532c1f0073d3d7b4efbd1c6..0b712d856f00fa83cd3c848be44597df775ec360 100644 (file)
@@ -302,9 +302,8 @@ EFI_STATUS tpm_log_event(UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, UIN
         EFI_TCG2 *tpm2;
 
         tpm2 = tcg2_interface_check();
-        if (tpm2) {
+        if (tpm2)
                 return tpm2_measure_to_pcr_and_event_log(tpm2, pcrindex, buffer, buffer_size, description);
-        }
 
         tpm1 = tcg1_interface_check();
         if (tpm1)
index 3c75be381fc6c9bf2cfb8c20b84df416390cf76b..ccb22d5f8bc4d17c298eb7883308574206e584f0 100644 (file)
@@ -101,7 +101,8 @@ static int acquire_bus(bool set_monitor, sd_bus **ret) {
 
         r = sd_bus_set_watch_bind(bus, arg_watch_bind);
         if (r < 0)
-                return log_error_errno(r, "Failed to set watch-bind setting to '%s': %m", yes_no(arg_watch_bind));
+                return log_error_errno(r, "Failed to set watch-bind setting to '%s': %m",
+                                       yes_no(arg_watch_bind));
 
         if (arg_address)
                 r = sd_bus_set_address(bus, arg_address);
@@ -109,13 +110,10 @@ static int acquire_bus(bool set_monitor, sd_bus **ret) {
                 switch (arg_transport) {
 
                 case BUS_TRANSPORT_LOCAL:
-                        if (arg_user) {
-                                bus->is_user = true;
+                        if (arg_user)
                                 r = bus_set_address_user(bus);
-                        } else {
-                                bus->is_system = true;
+                        else
                                 r = bus_set_address_system(bus);
-                        }
                         break;
 
                 case BUS_TRANSPORT_REMOTE:
@@ -172,7 +170,9 @@ static int list_bus_names(int argc, char **argv, void *userdata) {
         if (r < 0)
                 return r;
 
-        r = sd_bus_list_names(bus, (arg_acquired || arg_unique) ? &acquired : NULL, arg_activatable ? &activatable : NULL);
+        r = sd_bus_list_names(bus,
+                              (arg_acquired || arg_unique) ? &acquired : NULL,
+                              arg_activatable ? &activatable : NULL);
         if (r < 0)
                 return log_error_errno(r, "Failed to list names: %m");
 
@@ -192,7 +192,16 @@ static int list_bus_names(int argc, char **argv, void *userdata) {
                         return log_error_errno(r, "Failed to add to hashmap: %m");
         }
 
-        table = table_new("activatable", "name", "pid", "process", "user", "connection", "unit", "session", "description", "machine");
+        table = table_new("activatable",
+                          "name",
+                          "pid",
+                          "process",
+                          "user",
+                          "connection",
+                          "unit",
+                          "session",
+                          "description",
+                          "machine");
         if (!table)
                 return log_oom();
 
@@ -354,15 +363,15 @@ static int list_bus_names(int argc, char **argv, void *userdata) {
                         return log_error_errno(r, "Failed to fill line: %m");
         }
 
-        if (IN_SET(arg_json, JSON_OFF, JSON_PRETTY))
-                (void) pager_open(arg_pager_flags);
+        (void) pager_open(arg_pager_flags);
 
         if (arg_json)
-                r = table_print_json(table, stdout, (arg_json == JSON_PRETTY ? JSON_FORMAT_PRETTY : JSON_FORMAT_NEWLINE) | JSON_FORMAT_COLOR_AUTO);
+                r = table_print_json(table, stdout,
+                                     (arg_json == JSON_PRETTY ? JSON_FORMAT_PRETTY : JSON_FORMAT_NEWLINE) | JSON_FORMAT_COLOR_AUTO);
         else
                 r = table_print(table, stdout);
         if (r < 0)
-                return log_error_errno(r, "Failed to show table: %m");
+                return table_log_print_error(r);
 
         return 0;
 }
@@ -405,36 +414,25 @@ static void print_subtree(const char *prefix, const char *path, char **l) {
                         n++;
                 }
 
-                printf("%s%s%s\n", prefix, special_glyph(has_more ? SPECIAL_GLYPH_TREE_BRANCH : SPECIAL_GLYPH_TREE_RIGHT), *l);
+                printf("%s%s%s\n",
+                       prefix,
+                       special_glyph(has_more ? SPECIAL_GLYPH_TREE_BRANCH : SPECIAL_GLYPH_TREE_RIGHT),
+                       *l);
 
                 print_subtree(has_more ? vertical : space, *l, l);
                 l = n;
         }
 }
 
-static void print_tree(const char *prefix, char **l) {
-
-        prefix = strempty(prefix);
-
-        if (arg_list) {
-                char **i;
-
-                STRV_FOREACH(i, l)
-                        printf("%s%s\n", prefix, *i);
-                return;
-        }
-
-        if (strv_isempty(l)) {
+static void print_tree(char **l) {
+        if (arg_list)
+                strv_print(l);
+        else if (strv_isempty(l))
                 printf("No objects discovered.\n");
-                return;
-        }
-
-        if (streq(l[0], "/") && !l[1]) {
+        else if (streq(l[0], "/") && !l[1])
                 printf("Only root object discovered.\n");
-                return;
-        }
-
-        print_subtree(prefix, "/", l);
+        else
+                print_subtree("", "/", l);
 }
 
 static int on_path(const char *path, void *userdata) {
@@ -443,14 +441,14 @@ static int on_path(const char *path, void *userdata) {
 
         assert(paths);
 
-        r = set_put_strdup(paths, path);
+        r = set_put_strdup(&paths, path);
         if (r < 0)
                 return log_oom();
 
         return 0;
 }
 
-static int find_nodes(sd_bus *bus, const char *service, const char *path, Set *paths, bool many) {
+static int find_nodes(sd_bus *bus, const char *service, const char *path, Set *paths) {
         static const XMLIntrospectOps ops = {
                 .on_path = on_path,
         };
@@ -460,12 +458,14 @@ static int find_nodes(sd_bus *bus, const char *service, const char *path, Set *p
         const char *xml;
         int r;
 
-        r = sd_bus_call_method(bus, service, path, "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
+        r = sd_bus_call_method(bus, service, path,
+                               "org.freedesktop.DBus.Introspectable", "Introspect",
+                               &error, &reply, "");
         if (r < 0) {
-                if (many)
-                        printf("Failed to introspect object %s of service %s: %s\n", path, service, bus_error_message(&error, r));
-                else
-                        log_error_errno(r, "Failed to introspect object %s of service %s: %s", path, service, bus_error_message(&error, r));
+                printf("%sFailed to introspect object %s of service %s: %s%s\n",
+                       ansi_highlight_red(),
+                       path, service, bus_error_message(&error, r),
+                       ansi_normal());
                 return r;
         }
 
@@ -476,34 +476,23 @@ static int find_nodes(sd_bus *bus, const char *service, const char *path, Set *p
         return parse_xml_introspect(path, xml, &ops, paths);
 }
 
-static int tree_one(sd_bus *bus, const char *service, const char *prefix, bool many) {
-        _cleanup_set_free_free_ Set *paths = NULL, *done = NULL, *failed = NULL;
+static int tree_one(sd_bus *bus, const char *service) {
+        _cleanup_set_free_ Set *paths = NULL, *done = NULL, *failed = NULL;
         _cleanup_free_ char **l = NULL;
-        char *m;
         int r;
 
-        paths = set_new(&string_hash_ops);
-        if (!paths)
+        r = set_put_strdup(&paths, "/");
+        if (r < 0)
                 return log_oom();
 
-        done = set_new(&string_hash_ops);
+        done = set_new(&string_hash_ops_free);
         if (!done)
                 return log_oom();
 
-        failed = set_new(&string_hash_ops);
+        failed = set_new(&string_hash_ops_free);
         if (!failed)
                 return log_oom();
 
-        m = strdup("/");
-        if (!m)
-                return log_oom();
-
-        r = set_put(paths, m);
-        if (r < 0) {
-                free(m);
-                return log_oom();
-        }
-
         for (;;) {
                 _cleanup_free_ char *p = NULL;
                 int q;
@@ -516,20 +505,14 @@ static int tree_one(sd_bus *bus, const char *service, const char *prefix, bool m
                     set_contains(failed, p))
                         continue;
 
-                q = find_nodes(bus, service, p, paths, many);
-                if (q < 0) {
-                        if (r >= 0)
-                                r = q;
-
-                        q = set_put(failed, p);
-                } else
-                        q = set_put(done, p);
+                q = find_nodes(bus, service, p, paths);
+                if (q < 0 && r >= 0)
+                        r = q;
 
+                q = set_consume(q < 0 ? failed : done, TAKE_PTR(p));
+                assert(q != 0);
                 if (q < 0)
                         return log_oom();
-
-                assert(q != 0);
-                p = NULL;
         }
 
         (void) pager_open(arg_pager_flags);
@@ -539,7 +522,7 @@ static int tree_one(sd_bus *bus, const char *service, const char *prefix, bool m
                 return log_oom();
 
         strv_sort(l);
-        print_tree(prefix, l);
+        print_tree(l);
 
         fflush(stdout);
 
@@ -551,6 +534,12 @@ static int tree(int argc, char **argv, void *userdata) {
         char **i;
         int r = 0;
 
+        /* Do superficial verification of arguments before even opening the bus */
+        STRV_FOREACH(i, strv_skip(argv, 1))
+                if (!sd_bus_service_name_is_valid(*i))
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                               "Invalid bus service name: %s", *i);
+
         if (!arg_unique && !arg_acquired)
                 arg_acquired = true;
 
@@ -582,14 +571,14 @@ static int tree(int argc, char **argv, void *userdata) {
 
                         printf("Service %s%s%s:\n", ansi_highlight(), *i, ansi_normal());
 
-                        q = tree_one(bus, *i, NULL, true);
+                        q = tree_one(bus, *i);
                         if (q < 0 && r >= 0)
                                 r = q;
 
                         not_first = true;
                 }
-        } else {
-                STRV_FOREACH(i, argv+1) {
+        } else
+                STRV_FOREACH(i, strv_skip(argv, 1)) {
                         int q;
 
                         if (i > argv+1)
@@ -600,11 +589,10 @@ static int tree(int argc, char **argv, void *userdata) {
                                 printf("Service %s%s%s:\n", ansi_highlight(), *i, ansi_normal());
                         }
 
-                        q = tree_one(bus, *i, NULL, !!argv[2]);
+                        q = tree_one(bus, *i);
                         if (q < 0 && r >= 0)
                                 r = q;
                 }
-        }
 
         return r;
 }
@@ -837,20 +825,24 @@ static int on_interface(const char *interface, uint64_t flags, void *userdata) {
         assert(interface);
         assert(members);
 
-        m = new0(Member, 1);
+        m = new(Member, 1);
         if (!m)
                 return log_oom();
 
-        m->type = "interface";
-        m->flags = flags;
+        *m = (Member) {
+                .type = "interface",
+                .flags = flags,
+        };
 
         r = free_and_strdup(&m->interface, interface);
         if (r < 0)
                 return log_oom();
 
         r = set_put(members, m);
-        if (r <= 0)
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Duplicate interface");
+        if (r == -EEXIST)
+                return log_error_errno(r,  "Invalid introspection data: duplicate interface '%s'.", interface);
+        if (r < 0)
+                return log_oom();
 
         m = NULL;
         return 0;
@@ -864,12 +856,14 @@ static int on_method(const char *interface, const char *name, const char *signat
         assert(interface);
         assert(name);
 
-        m = new0(Member, 1);
+        m = new(Member, 1);
         if (!m)
                 return log_oom();
 
-        m->type = "method";
-        m->flags = flags;
+        *m = (Member) {
+                .type = "method",
+                .flags = flags,
+        };
 
         r = free_and_strdup(&m->interface, interface);
         if (r < 0)
@@ -888,8 +882,10 @@ static int on_method(const char *interface, const char *name, const char *signat
                 return log_oom();
 
         r = set_put(members, m);
-        if (r <= 0)
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Duplicate method");
+        if (r == -EEXIST)
+                return log_error_errno(r, "Invalid introspection data: duplicate method '%s' on interface '%s'.", name, interface);
+        if (r < 0)
+                return log_oom();
 
         m = NULL;
         return 0;
@@ -903,12 +899,14 @@ static int on_signal(const char *interface, const char *name, const char *signat
         assert(interface);
         assert(name);
 
-        m = new0(Member, 1);
+        m = new(Member, 1);
         if (!m)
                 return log_oom();
 
-        m->type = "signal";
-        m->flags = flags;
+        *m = (Member) {
+                .type = "signal",
+                .flags = flags,
+        };
 
         r = free_and_strdup(&m->interface, interface);
         if (r < 0)
@@ -923,8 +921,10 @@ static int on_signal(const char *interface, const char *name, const char *signat
                 return log_oom();
 
         r = set_put(members, m);
-        if (r <= 0)
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Duplicate signal");
+        if (r == -EEXIST)
+                return log_error_errno(r, "Invalid introspection data: duplicate signal '%s' on interface '%s'.", name, interface);
+        if (r < 0)
+                return log_oom();
 
         m = NULL;
         return 0;
@@ -938,13 +938,15 @@ static int on_property(const char *interface, const char *name, const char *sign
         assert(interface);
         assert(name);
 
-        m = new0(Member, 1);
+        m = new(Member, 1);
         if (!m)
                 return log_oom();
 
-        m->type = "property";
-        m->flags = flags;
-        m->writable = writable;
+        *m = (Member) {
+                .type = "property",
+                .flags = flags,
+                .writable = writable,
+        };
 
         r = free_and_strdup(&m->interface, interface);
         if (r < 0)
@@ -959,8 +961,10 @@ static int on_property(const char *interface, const char *name, const char *sign
                 return log_oom();
 
         r = set_put(members, m);
-        if (r <= 0)
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Duplicate property");
+        if (r == -EEXIST)
+                return log_error_errno(r, "Invalid introspection data: duplicate property '%s' on interface '%s'.", name, interface);
+        if (r < 0)
+                return log_oom();
 
         m = NULL;
         return 0;
@@ -994,9 +998,12 @@ static int introspect(int argc, char **argv, void *userdata) {
         if (!members)
                 return log_oom();
 
-        r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply_xml, "");
+        r = sd_bus_call_method(bus, argv[1], argv[2],
+                               "org.freedesktop.DBus.Introspectable", "Introspect",
+                               &error, &reply_xml, "");
         if (r < 0)
-                return log_error_errno(r, "Failed to introspect object %s of service %s: %s", argv[2], argv[1], bus_error_message(&error, r));
+                return log_error_errno(r, "Failed to introspect object %s of service %s: %s",
+                                       argv[2], argv[1], bus_error_message(&error, r));
 
         r = sd_bus_message_read(reply_xml, "s", &xml);
         if (r < 0)
@@ -1004,6 +1011,7 @@ static int introspect(int argc, char **argv, void *userdata) {
 
         if (arg_xml_interface) {
                 /* Just dump the received XML and finish */
+                (void) pager_open(arg_pager_flags);
                 puts(xml);
                 return 0;
         }
@@ -1026,7 +1034,9 @@ static int introspect(int argc, char **argv, void *userdata) {
                 if (argv[3] && !streq(argv[3], m->interface))
                         continue;
 
-                r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", m->interface);
+                r = sd_bus_call_method(bus, argv[1], argv[2],
+                                       "org.freedesktop.DBus.Properties", "GetAll",
+                                       &error, &reply, "s", m->interface);
                 if (r < 0)
                         return log_error_errno(r, "Failed to get all properties on interface %s: %s",
                                                m->interface, bus_error_message(&error, r));
@@ -1088,17 +1098,14 @@ static int introspect(int argc, char **argv, void *userdata) {
                         return bus_log_parse_error(r);
         }
 
-        (void) pager_open(arg_pager_flags);
-
-        name_width = STRLEN("NAME");
-        type_width = STRLEN("TYPE");
-        signature_width = STRLEN("SIGNATURE");
-        result_width = STRLEN("RESULT/VALUE");
+        name_width = strlen("NAME");
+        type_width = strlen("TYPE");
+        signature_width = strlen("SIGNATURE");
+        result_width = strlen("RESULT/VALUE");
 
         sorted = newa(Member*, set_size(members));
 
         SET_FOREACH(m, members, i) {
-
                 if (argv[3] && !streq(argv[3], m->interface))
                         continue;
 
@@ -1123,6 +1130,8 @@ static int introspect(int argc, char **argv, void *userdata) {
 
         typesafe_qsort(sorted, k, member_compare_funcp);
 
+        (void) pager_open(arg_pager_flags);
+
         if (arg_legend) {
                 printf("%-*s %-*s %-*s %-*s %s\n",
                        (int) name_width, "NAME",
@@ -1159,7 +1168,8 @@ static int introspect(int argc, char **argv, void *userdata) {
                 printf("%s%s%-*s%s %-*s %-*s %-*s%s%s%s%s%s%s\n",
                        is_interface ? ansi_highlight() : "",
                        is_interface ? "" : ".",
-                       - !is_interface + (int) name_width, empty_to_dash(streq_ptr(m->type, "interface") ? m->interface : m->name),
+                       - !is_interface + (int) name_width,
+                       empty_to_dash(streq_ptr(m->type, "interface") ? m->interface : m->name),
                        is_interface ? ansi_normal() : "",
                        (int) type_width, empty_to_dash(m->type),
                        (int) signature_width, empty_to_dash(m->signature),
@@ -1196,23 +1206,22 @@ static int message_json(sd_bus_message *m, FILE *f) {
         e[1] = 0;
 
         r = json_build(&w, JSON_BUILD_OBJECT(
-                                       JSON_BUILD_PAIR("type", JSON_BUILD_STRING(bus_message_type_to_string(m->header->type))),
-                                       JSON_BUILD_PAIR("endian", JSON_BUILD_STRING(e)),
-                                       JSON_BUILD_PAIR("flags", JSON_BUILD_INTEGER(m->header->flags)),
-                                       JSON_BUILD_PAIR("version", JSON_BUILD_INTEGER(m->header->version)),
-                                       JSON_BUILD_PAIR_CONDITION(m->priority != 0, "priority", JSON_BUILD_INTEGER(m->priority)),
-                                       JSON_BUILD_PAIR("cookie", JSON_BUILD_INTEGER(BUS_MESSAGE_COOKIE(m))),
-                                       JSON_BUILD_PAIR_CONDITION(m->reply_cookie != 0, "reply_cookie", JSON_BUILD_INTEGER(m->reply_cookie)),
-                                       JSON_BUILD_PAIR_CONDITION(m->sender, "sender", JSON_BUILD_STRING(m->sender)),
-                                       JSON_BUILD_PAIR_CONDITION(m->destination, "destination", JSON_BUILD_STRING(m->destination)),
-                                       JSON_BUILD_PAIR_CONDITION(m->path, "path", JSON_BUILD_STRING(m->path)),
-                                       JSON_BUILD_PAIR_CONDITION(m->interface, "interface", JSON_BUILD_STRING(m->interface)),
-                                       JSON_BUILD_PAIR_CONDITION(m->member, "member", JSON_BUILD_STRING(m->member)),
-                                       JSON_BUILD_PAIR_CONDITION(m->monotonic != 0, "monotonic", JSON_BUILD_INTEGER(m->monotonic)),
-                                       JSON_BUILD_PAIR_CONDITION(m->realtime != 0, "realtime", JSON_BUILD_INTEGER(m->realtime)),
-                                       JSON_BUILD_PAIR_CONDITION(m->seqnum != 0, "seqnum", JSON_BUILD_INTEGER(m->seqnum)),
-                                       JSON_BUILD_PAIR_CONDITION(m->error.name, "error_name", JSON_BUILD_STRING(m->error.name)),
-                                       JSON_BUILD_PAIR("payload", JSON_BUILD_VARIANT(v))));
+                JSON_BUILD_PAIR("type", JSON_BUILD_STRING(bus_message_type_to_string(m->header->type))),
+                JSON_BUILD_PAIR("endian", JSON_BUILD_STRING(e)),
+                JSON_BUILD_PAIR("flags", JSON_BUILD_INTEGER(m->header->flags)),
+                JSON_BUILD_PAIR("version", JSON_BUILD_INTEGER(m->header->version)),
+                JSON_BUILD_PAIR("cookie", JSON_BUILD_INTEGER(BUS_MESSAGE_COOKIE(m))),
+                JSON_BUILD_PAIR_CONDITION(m->reply_cookie != 0, "reply_cookie", JSON_BUILD_INTEGER(m->reply_cookie)),
+                JSON_BUILD_PAIR_CONDITION(m->sender, "sender", JSON_BUILD_STRING(m->sender)),
+                JSON_BUILD_PAIR_CONDITION(m->destination, "destination", JSON_BUILD_STRING(m->destination)),
+                JSON_BUILD_PAIR_CONDITION(m->path, "path", JSON_BUILD_STRING(m->path)),
+                JSON_BUILD_PAIR_CONDITION(m->interface, "interface", JSON_BUILD_STRING(m->interface)),
+                JSON_BUILD_PAIR_CONDITION(m->member, "member", JSON_BUILD_STRING(m->member)),
+                JSON_BUILD_PAIR_CONDITION(m->monotonic != 0, "monotonic", JSON_BUILD_INTEGER(m->monotonic)),
+                JSON_BUILD_PAIR_CONDITION(m->realtime != 0, "realtime", JSON_BUILD_INTEGER(m->realtime)),
+                JSON_BUILD_PAIR_CONDITION(m->seqnum != 0, "seqnum", JSON_BUILD_INTEGER(m->seqnum)),
+                JSON_BUILD_PAIR_CONDITION(m->error.name, "error_name", JSON_BUILD_STRING(m->error.name)),
+                JSON_BUILD_PAIR("payload", JSON_BUILD_VARIANT(v))));
         if (r < 0)
                 return log_error_errno(r, "Failed to build JSON object: %m");
 
@@ -1251,7 +1260,7 @@ static int monitor(int argc, char **argv, int (*dump)(sd_bus_message *m, FILE *f
         STRV_FOREACH(i, argv+1) {
                 _cleanup_free_ char *m = NULL;
 
-                if (!service_name_is_valid(*i))
+                if (!sd_bus_service_name_is_valid(*i))
                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid service name '%s'", *i);
 
                 m = strjoin("sender='", *i, "'");
@@ -1313,7 +1322,7 @@ static int monitor(int argc, char **argv, int (*dump)(sd_bus_message *m, FILE *f
 
                         r = sd_bus_message_read(m, "s", &name);
                         if (r < 0)
-                                return log_error_errno(r, "Failed to read lost name: %m");
+                                return bus_log_parse_error(r);
 
                         if (streq(name, unique_name))
                                 is_monitor = true;
@@ -1376,6 +1385,8 @@ static int status(int argc, char **argv, void *userdata) {
         if (r < 0)
                 return r;
 
+        (void) pager_open(arg_pager_flags);
+
         if (!isempty(argv[1])) {
                 r = parse_pid(argv[1], &pid);
                 if (r < 0)
@@ -1403,7 +1414,8 @@ static int status(int argc, char **argv, void *userdata) {
 
                 r = sd_bus_get_bus_id(bus, &bus_id);
                 if (r >= 0)
-                        printf("BusID=%s" SD_ID128_FORMAT_STR "%s\n", ansi_highlight(), SD_ID128_FORMAT_VAL(bus_id), ansi_normal());
+                        printf("BusID=%s" SD_ID128_FORMAT_STR "%s\n",
+                               ansi_highlight(), SD_ID128_FORMAT_VAL(bus_id), ansi_normal());
 
                 r = sd_bus_get_owner_creds(
                                 bus,
@@ -1563,7 +1575,6 @@ static int message_append_cmdline(sd_bus_message *m, const char *signature, char
                                 return log_error_errno(r, "Invalid array signature: %m");
 
                         {
-                                unsigned i;
                                 char s[k + 1];
                                 memcpy(s, signature, k);
                                 s[k] = 0;
@@ -1572,7 +1583,7 @@ static int message_append_cmdline(sd_bus_message *m, const char *signature, char
                                 if (r < 0)
                                         return bus_log_create_error(r);
 
-                                for (i = 0; i < n; i++) {
+                                for (unsigned i = 0; i < n; i++) {
                                         r = message_append_cmdline(m, s, &p);
                                         if (r < 0)
                                                 return r;
@@ -1613,7 +1624,9 @@ static int message_append_cmdline(sd_bus_message *m, const char *signature, char
                                 memcpy(s, signature + 1, k - 2);
                                 s[k - 2] = 0;
 
-                                r = sd_bus_message_open_container(m, t == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY, s);
+                                const char ctype = t == SD_BUS_TYPE_STRUCT_BEGIN ?
+                                        SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY;
+                                r = sd_bus_message_open_container(m, ctype, s);
                                 if (r < 0)
                                         return bus_log_create_error(r);
 
@@ -2034,7 +2047,8 @@ static int call(int argc, char **argv, void *userdata) {
                         return r;
 
                 if (*p)
-                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Too many parameters for signature.");
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                               "Too many parameters for signature.");
         }
 
         if (!arg_expect_reply) {
@@ -2122,7 +2136,8 @@ static int emit_signal(int argc, char **argv, void *userdata) {
                         return r;
 
                 if (*p)
-                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Too many parameters for signature.");
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                               "Too many parameters for signature.");
         }
 
         r = sd_bus_send(bus, m, NULL);
@@ -2147,7 +2162,9 @@ static int get_property(int argc, char **argv, void *userdata) {
                 const char *contents = NULL;
                 char type;
 
-                r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Properties", "Get", &error, &reply, "ss", argv[3], *i);
+                r = sd_bus_call_method(bus, argv[1], argv[2],
+                                       "org.freedesktop.DBus.Properties", "Get",
+                                       &error, &reply, "ss", argv[3], *i);
                 if (r < 0)
                         return log_error_errno(r, "Failed to get property %s on interface %s: %s",
                                                *i, argv[3],
@@ -2209,7 +2226,8 @@ static int set_property(int argc, char **argv, void *userdata) {
         if (r < 0)
                 return r;
 
-        r = sd_bus_message_new_method_call(bus, &m, argv[1], argv[2], "org.freedesktop.DBus.Properties", "Set");
+        r = sd_bus_message_new_method_call(bus, &m, argv[1], argv[2],
+                                           "org.freedesktop.DBus.Properties", "Set");
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -2559,7 +2577,6 @@ static int parse_argv(int argc, char *argv[]) {
 }
 
 static int busctl_main(int argc, char *argv[]) {
-
         static const Verb verbs[] = {
                 { "list",         VERB_ANY, 1,        VERB_DEFAULT, list_bus_names },
                 { "status",       VERB_ANY, 2,        0,            status         },
@@ -2581,9 +2598,7 @@ static int busctl_main(int argc, char *argv[]) {
 static int run(int argc, char *argv[]) {
         int r;
 
-        log_show_color(true);
-        log_parse_environment();
-        log_open();
+        log_setup_cli();
 
         r = parse_argv(argc, argv);
         if (r <= 0)
index b55d7299cabf03529f44669af5a8d6578d01c6d2..e09adb8b5b1f875c71ff1d9c403b035dbfd5c2cb 100644 (file)
@@ -164,9 +164,7 @@ static void show_cg_info(const char *controller, const char *path) {
 static int run(int argc, char *argv[]) {
         int r, output_flags;
 
-        log_show_color(true);
-        log_parse_environment();
-        log_open();
+        log_setup_cli();
 
         r = parse_argv(argc, argv);
         if (r <= 0)
@@ -199,7 +197,7 @@ static int run(int argc, char *argv[]) {
                                                                           arg_show_unit == SHOW_UNIT_USER,
                                                                           &bus);
                                         if (r < 0)
-                                                return log_error_errno(r, "Failed to create bus connection: %m");
+                                                return bus_log_connect_error(r);
                                 }
 
                                 q = show_cgroup_get_unit_path_and_warn(bus, *name, &cgroup);
index de25aaae5d1db02df04b08a60596ab30e139eb6d..e6c09d1b38bcc193e3b94af10e1c8bcff32c6abc 100644 (file)
@@ -908,9 +908,7 @@ static int run(int argc, char *argv[]) {
         CGroupMask mask;
         int r;
 
-        log_show_color(true);
-        log_parse_environment();
-        log_open();
+        log_setup_cli();
 
         r = parse_argv(argc, argv);
         if (r <= 0)
diff --git a/src/core/apparmor-setup.c b/src/core/apparmor-setup.c
new file mode 100644 (file)
index 0000000..6cba841
--- /dev/null
@@ -0,0 +1,100 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <errno.h>
+#if HAVE_APPARMOR
+#  include <sys/apparmor.h>
+#endif
+#include <unistd.h>
+
+#include "apparmor-setup.h"
+#include "apparmor-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "log.h"
+#include "macro.h"
+#include "string-util.h"
+#include "strv.h"
+
+#if HAVE_APPARMOR
+DEFINE_TRIVIAL_CLEANUP_FUNC(aa_policy_cache *, aa_policy_cache_unref);
+DEFINE_TRIVIAL_CLEANUP_FUNC(aa_features *, aa_features_unref);
+#endif
+
+int mac_apparmor_setup(void) {
+#if HAVE_APPARMOR
+        int r;
+        _cleanup_(aa_policy_cache_unrefp) aa_policy_cache *policy_cache = NULL;
+        _cleanup_(aa_features_unrefp) aa_features *features = NULL;
+        const char *current_file;
+        _cleanup_free_ char *current_profile = NULL, *cache_dir_path = NULL;
+
+        if (!mac_apparmor_use()) {
+                log_debug("AppArmor either not supported by the kernel or disabled.");
+                return 0;
+        }
+
+        /* To enable LSM stacking a patch to the kernel is proposed to create a
+         * per-LSM subdirectory to distinguish between the LSMs. Therefore, we
+         * read the file from the LSM specific directory first and only if that
+         * fails the one from the generic directory.
+         */
+        FOREACH_STRING(current_file, "/proc/self/attr/apparmor/current", "/proc/self/attr/current") {
+                r = read_one_line_file(current_file, &current_profile);
+                if (r == -ENOENT)
+                        continue;
+                else if (r < 0)
+                        log_warning_errno(r, "Failed to read current AppArmor profile from file %s, ignoring: %m", current_file);
+                else
+                        break;
+        }
+        if (!current_profile) {
+                log_warning("Failed to get the current AppArmor profile of systemd from /proc/self/attr/apparmor/current or /proc/self/attr/current, ignoring.");
+                return 0;
+        }
+        if (!streq(current_profile, "unconfined")) {
+                log_debug("We are already confined in an AppArmor profile.");
+                return 0;
+        }
+
+        r = aa_features_new_from_kernel(&features);
+        if (r < 0) {
+                log_warning_errno(errno, "Failed to get the AppArmor feature set from the kernel, ignoring: %m");
+                return 0;
+        }
+        cache_dir_path = aa_policy_cache_dir_path_preview(features, AT_FDCWD, "/etc/apparmor/earlypolicy");
+        if (!cache_dir_path) {
+                log_debug_errno(errno, "Failed to get the path of the early AppArmor policy cache directory.");
+                return 0;
+        }
+
+        /* aa_policy_cache_new will internally use the same path as aa_policy_cache_dir_path_preview has returned. */
+        r = aa_policy_cache_new(&policy_cache, features, AT_FDCWD, "/etc/apparmor/earlypolicy", 0);
+        if (r < 0) {
+                if (errno == ENOENT) {
+                        log_debug_errno(errno, "The early AppArmor policy cache directory %s does not exist.", cache_dir_path);
+                        return 0;
+                }
+                log_warning_errno(errno, "Failed to create a new AppArmor policy cache, ignoring: %m");
+                return 0;
+        }
+        r = aa_policy_cache_replace_all(policy_cache, NULL);
+        if (r < 0) {
+                log_warning_errno(errno, "Failed to load the profiles from the early AppArmor policy cache directory %s, ignoring: %m", cache_dir_path);
+                return 0;
+        }
+
+        log_info("Successfully loaded all binary profiles from AppArmor early policy cache at %s.", cache_dir_path);
+
+        r = aa_change_profile("systemd");
+        if (r < 0) {
+                if (errno == ENOENT)
+                        log_debug_errno(errno, "Failed to change to AppArmor profile 'systemd'. Please ensure that one of the binary profile files in policy cache directory %s contains a profile with that name.", cache_dir_path);
+                else
+                        log_error_errno(errno, "Failed to change to AppArmor profile 'systemd': %m");
+                return 0;
+        }
+
+        log_info("Changed to AppArmor profile systemd.");
+#endif
+        return 0;
+}
diff --git a/src/core/apparmor-setup.h b/src/core/apparmor-setup.h
new file mode 100644 (file)
index 0000000..100680a
--- /dev/null
@@ -0,0 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+int mac_apparmor_setup(void);
index 68dc5db71861d1e47750f7c329c579703c6c4885..1f0519876653440831df9799e32cb7bdd6ba4be0 100644 (file)
@@ -912,13 +912,7 @@ static int automount_deserialize_item(Unit *u, const char *key, const char *valu
                 if (safe_atou(value, &token) < 0)
                         log_unit_debug(u, "Failed to parse token value: %s", value);
                 else {
-                        r = set_ensure_allocated(&a->tokens, NULL);
-                        if (r < 0) {
-                                log_oom();
-                                return 0;
-                        }
-
-                        r = set_put(a->tokens, UINT_TO_PTR(token));
+                        r = set_ensure_put(&a->tokens, NULL, UINT_TO_PTR(token));
                         if (r < 0)
                                 log_unit_error_errno(u, r, "Failed to add token to set: %m");
                 }
@@ -928,13 +922,7 @@ static int automount_deserialize_item(Unit *u, const char *key, const char *valu
                 if (safe_atou(value, &token) < 0)
                         log_unit_debug(u, "Failed to parse token value: %s", value);
                 else {
-                        r = set_ensure_allocated(&a->expire_tokens, NULL);
-                        if (r < 0) {
-                                log_oom();
-                                return 0;
-                        }
-
-                        r = set_put(a->expire_tokens, UINT_TO_PTR(token));
+                        r = set_ensure_put(&a->expire_tokens, NULL, UINT_TO_PTR(token));
                         if (r < 0)
                                 log_unit_error_errno(u, r, "Failed to add expire token to set: %m");
                 }
@@ -1010,13 +998,7 @@ static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, vo
                 } else
                         log_unit_debug(UNIT(a), "Got direct mount request on %s", a->where);
 
-                r = set_ensure_allocated(&a->tokens, NULL);
-                if (r < 0) {
-                        log_unit_error(UNIT(a), "Failed to allocate token set.");
-                        goto fail;
-                }
-
-                r = set_put(a->tokens, UINT_TO_PTR(packet.v5_packet.wait_queue_token));
+                r = set_ensure_put(&a->tokens, NULL, UINT_TO_PTR(packet.v5_packet.wait_queue_token));
                 if (r < 0) {
                         log_unit_error_errno(UNIT(a), r, "Failed to remember token: %m");
                         goto fail;
@@ -1030,13 +1012,7 @@ static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, vo
 
                 automount_stop_expire(a);
 
-                r = set_ensure_allocated(&a->expire_tokens, NULL);
-                if (r < 0) {
-                        log_unit_error(UNIT(a), "Failed to allocate token set.");
-                        goto fail;
-                }
-
-                r = set_put(a->expire_tokens, UINT_TO_PTR(packet.v5_packet.wait_queue_token));
+                r = set_ensure_put(&a->expire_tokens, NULL, UINT_TO_PTR(packet.v5_packet.wait_queue_token));
                 if (r < 0) {
                         log_unit_error_errno(UNIT(a), r, "Failed to remember token: %m");
                         goto fail;
@@ -1138,7 +1114,6 @@ const UnitVTable automount_vtable = {
 
         .reset_failed = automount_reset_failed,
 
-        .bus_vtable = bus_automount_vtable,
         .bus_set_property = bus_automount_set_property,
 
         .shutdown = automount_shutdown,
index 07ef9f67771876f5b6addce3afd4e03218fbb9ba..34320e88fb2e701381c6dcb3a0e794f2d0a46576 100644 (file)
@@ -38,7 +38,7 @@ static int bpf_access_type(const char *acc) {
         return r;
 }
 
-static int bpf_prog_whitelist_device(
+static int bpf_prog_allow_list_device(
                 BPFProgram *prog,
                 char type,
                 int major,
@@ -80,7 +80,7 @@ static int bpf_prog_whitelist_device(
         return r;
 }
 
-static int bpf_prog_whitelist_major(
+static int bpf_prog_allow_list_major(
                 BPFProgram *prog,
                 char type,
                 int major,
@@ -120,7 +120,7 @@ static int bpf_prog_whitelist_major(
         return r;
 }
 
-static int bpf_prog_whitelist_class(
+static int bpf_prog_allow_list_class(
                 BPFProgram *prog,
                 char type,
                 const char *acc) {
@@ -161,7 +161,7 @@ static int bpf_prog_whitelist_class(
 int bpf_devices_cgroup_init(
                 BPFProgram **ret,
                 CGroupDevicePolicy policy,
-                bool whitelist) {
+                bool allow_list) {
 
         const struct bpf_insn pre_insn[] = {
                 /* load device type to r2 */
@@ -188,14 +188,14 @@ int bpf_devices_cgroup_init(
 
         assert(ret);
 
-        if (policy == CGROUP_DEVICE_POLICY_AUTO && !whitelist)
+        if (policy == CGROUP_DEVICE_POLICY_AUTO && !allow_list)
                 return 0;
 
         r = bpf_program_new(BPF_PROG_TYPE_CGROUP_DEVICE, &prog);
         if (r < 0)
                 return log_error_errno(r, "Loading device control BPF program failed: %m");
 
-        if (policy == CGROUP_DEVICE_POLICY_CLOSED || whitelist) {
+        if (policy == CGROUP_DEVICE_POLICY_CLOSED || allow_list) {
                 r = bpf_program_add_instructions(prog, pre_insn, ELEMENTSOF(pre_insn));
                 if (r < 0)
                         return log_error_errno(r, "Extending device control BPF program failed: %m");
@@ -209,7 +209,7 @@ int bpf_devices_cgroup_init(
 int bpf_devices_apply_policy(
                 BPFProgram *prog,
                 CGroupDevicePolicy policy,
-                bool whitelist,
+                bool allow_list,
                 const char *cgroup_path,
                 BPFProgram **prog_installed) {
 
@@ -221,7 +221,7 @@ int bpf_devices_apply_policy(
         if (!prog)
                 goto finish;
 
-        const bool deny_everything = policy == CGROUP_DEVICE_POLICY_STRICT && !whitelist;
+        const bool deny_everything = policy == CGROUP_DEVICE_POLICY_STRICT && !allow_list;
 
         const struct bpf_insn post_insn[] = {
                 /* return DENY */
@@ -325,7 +325,7 @@ int bpf_devices_supported(void) {
         return supported = 1;
 }
 
-static int whitelist_device_pattern(
+static int allow_list_device_pattern(
                 BPFProgram *prog,
                 const char *path,
                 char type,
@@ -340,11 +340,11 @@ static int whitelist_device_pattern(
                         return 0;
 
                 if (maj && min)
-                        return bpf_prog_whitelist_device(prog, type, *maj, *min, acc);
+                        return bpf_prog_allow_list_device(prog, type, *maj, *min, acc);
                 else if (maj)
-                        return bpf_prog_whitelist_major(prog, type, *maj, acc);
+                        return bpf_prog_allow_list_major(prog, type, *maj, acc);
                 else
-                        return bpf_prog_whitelist_class(prog, type, acc);
+                        return bpf_prog_allow_list_class(prog, type, acc);
 
         } else {
                 char buf[2+DECIMAL_STR_MAX(unsigned)*2+2+4];
@@ -369,7 +369,7 @@ static int whitelist_device_pattern(
         }
 }
 
-int bpf_devices_whitelist_device(
+int bpf_devices_allow_list_device(
                 BPFProgram *prog,
                 const char *path,
                 const char *node,
@@ -405,10 +405,10 @@ int bpf_devices_whitelist_device(
         }
 
         unsigned maj = major(rdev), min = minor(rdev);
-        return whitelist_device_pattern(prog, path, S_ISCHR(mode) ? 'c' : 'b', &maj, &min, acc);
+        return allow_list_device_pattern(prog, path, S_ISCHR(mode) ? 'c' : 'b', &maj, &min, acc);
 }
 
-int bpf_devices_whitelist_major(
+int bpf_devices_allow_list_major(
                 BPFProgram *prog,
                 const char *path,
                 const char *name,
@@ -424,12 +424,12 @@ int bpf_devices_whitelist_major(
 
         if (streq(name, "*"))
                 /* If the name is a wildcard, then apply this list to all devices of this type */
-                return whitelist_device_pattern(prog, path, type, NULL, NULL, acc);
+                return allow_list_device_pattern(prog, path, type, NULL, NULL, acc);
 
         if (safe_atou(name, &maj) >= 0 && DEVICE_MAJOR_VALID(maj))
                 /* The name is numeric and suitable as major. In that case, let's take its major, and create
                  * the entry directly. */
-                return whitelist_device_pattern(prog, path, type, &maj, NULL, acc);
+                return allow_list_device_pattern(prog, path, type, &maj, NULL, acc);
 
         _cleanup_fclose_ FILE *f = NULL;
         bool good = false, any = false;
@@ -486,17 +486,17 @@ int bpf_devices_whitelist_major(
                         continue;
 
                 any = true;
-                (void) whitelist_device_pattern(prog, path, type, &maj, NULL, acc);
+                (void) allow_list_device_pattern(prog, path, type, &maj, NULL, acc);
         }
 
         if (!any)
                 return log_debug_errno(SYNTHETIC_ERRNO(ENOENT),
-                                       "Device whitelist pattern \"%s\" did not match anything.", name);
+                                       "Device allow list pattern \"%s\" did not match anything.", name);
 
         return 0;
 }
 
-int bpf_devices_whitelist_static(
+int bpf_devices_allow_list_static(
                 BPFProgram *prog,
                 const char *path) {
 
@@ -515,13 +515,13 @@ int bpf_devices_whitelist_static(
 
         const char *node, *acc;
         NULSTR_FOREACH_PAIR(node, acc, auto_devices) {
-                k = bpf_devices_whitelist_device(prog, path, node, acc);
+                k = bpf_devices_allow_list_device(prog, path, node, acc);
                 if (r >= 0 && k < 0)
                         r = k;
         }
 
         /* PTS (/dev/pts) devices may not be duplicated, but accessed */
-        k = bpf_devices_whitelist_major(prog, path, "pts", 'c', "rw");
+        k = bpf_devices_allow_list_major(prog, path, "pts", 'c', "rw");
         if (r >= 0 && k < 0)
                 r = k;
 
index 4a5f4b1fb1895f57eba0e2ac017c1a71a9802c06..e2a08016e374e094355d830a39d1eaf0965355a9 100644 (file)
@@ -7,15 +7,15 @@
 
 typedef struct BPFProgram BPFProgram;
 
-int bpf_devices_cgroup_init(BPFProgram **ret, CGroupDevicePolicy policy, bool whitelist);
+int bpf_devices_cgroup_init(BPFProgram **ret, CGroupDevicePolicy policy, bool allow_list);
 int bpf_devices_apply_policy(
                 BPFProgram *prog,
                 CGroupDevicePolicy policy,
-                bool whitelist,
+                bool allow_list,
                 const char *cgroup_path,
                 BPFProgram **prog_installed);
 
 int bpf_devices_supported(void);
-int bpf_devices_whitelist_device(BPFProgram *prog, const char *path, const char *node, const char *acc);
-int bpf_devices_whitelist_major(BPFProgram *prog, const char *path, const char *name, char type, const char *acc);
-int bpf_devices_whitelist_static(BPFProgram *prog, const char *path);
+int bpf_devices_allow_list_device(BPFProgram *prog, const char *path, const char *node, const char *acc);
+int bpf_devices_allow_list_major(BPFProgram *prog, const char *path, const char *name, char type, const char *acc);
+int bpf_devices_allow_list_static(BPFProgram *prog, const char *path);
index 96c1a28b4fd02adb70e939b564afec32c54c11fb..bceb049b58b6604d206000d6aafec58bf329ca9e 100644 (file)
@@ -544,7 +544,7 @@ int bpf_firewall_compile(Unit *u) {
                                             "BPF_F_ALLOW_MULTI is not supported on this manager, not doing BPF firewall on slice units.");
 
         /* Note that when we compile a new firewall we first flush out the access maps and the BPF programs themselves,
-         * but we reuse the the accounting maps. That way the firewall in effect always maps to the actual
+         * but we reuse the accounting maps. That way the firewall in effect always maps to the actual
          * configuration, but we don't flush out the accounting unnecessarily */
 
         u->ip_bpf_ingress = bpf_program_unref(u->ip_bpf_ingress);
@@ -595,7 +595,7 @@ static int load_bpf_progs_from_fs_to_set(Unit *u, char **filter_paths, Set **set
         set_clear(*set);
 
         STRV_FOREACH(bpf_fs_path, filter_paths) {
-                _cleanup_free_ BPFProgram *prog = NULL;
+                _cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL;
                 int r;
 
                 r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, &prog);
@@ -606,14 +606,9 @@ static int load_bpf_progs_from_fs_to_set(Unit *u, char **filter_paths, Set **set
                 if (r < 0)
                         return log_unit_error_errno(u, r, "Loading of ingress BPF program %s failed: %m", *bpf_fs_path);
 
-                r = set_ensure_allocated(set, &filter_prog_hash_ops);
-                if (r < 0)
-                        return log_unit_error_errno(u, r, "Can't allocate BPF program set: %m");
-
-                r = set_put(*set, prog);
+                r = set_ensure_consume(set, &filter_prog_hash_ops, TAKE_PTR(prog));
                 if (r < 0)
                         return log_unit_error_errno(u, r, "Can't add program to BPF program set: %m");
-                TAKE_PTR(prog);
         }
 
         return 0;
@@ -662,12 +657,9 @@ static int attach_custom_bpf_progs(Unit *u, const char *path, int attach_type, S
                 r = bpf_program_cgroup_attach(prog, attach_type, path, BPF_F_ALLOW_MULTI);
                 if (r < 0)
                         return log_unit_error_errno(u, r, "Attaching custom egress BPF program to cgroup %s failed: %m", path);
-                /* Remember that these BPF programs are installed now. */
-                r = set_ensure_allocated(set_installed, &filter_prog_hash_ops);
-                if (r < 0)
-                        return log_unit_error_errno(u, r, "Can't allocate BPF program set: %m");
 
-                r = set_put(*set_installed, prog);
+                /* Remember that these BPF programs are installed now. */
+                r = set_ensure_put(set_installed, &filter_prog_hash_ops, prog);
                 if (r < 0)
                         return log_unit_error_errno(u, r, "Can't add program to BPF program set: %m");
                 bpf_program_ref(prog);
index ddd3f408174482f97c17d56261c204f5ada7883e..031b28a6846d89c3d896ef91166b5f52b3f2aca6 100644 (file)
@@ -16,7 +16,9 @@
 #include "fd-util.h"
 #include "fileio.h"
 #include "fs-util.h"
+#include "io-util.h"
 #include "limits-util.h"
+#include "nulstr-util.h"
 #include "parse-util.h"
 #include "path-util.h"
 #include "process-util.h"
@@ -215,31 +217,12 @@ void cgroup_context_done(CGroupContext *c) {
 }
 
 static int unit_get_kernel_memory_limit(Unit *u, const char *file, uint64_t *ret) {
-        _cleanup_free_ char *raw_kval = NULL;
-        uint64_t kval;
-        int r;
-
         assert(u);
 
         if (!u->cgroup_realized)
                 return -EOWNERDEAD;
 
-        r = cg_get_attribute("memory", u->cgroup_path, file, &raw_kval);
-        if (r < 0)
-                return r;
-
-        if (streq(raw_kval, "max")) {
-                *ret = CGROUP_LIMIT_MAX;
-                return 0;
-        }
-
-        r = safe_atou64(raw_kval, &kval);
-        if (r < 0)
-                return r;
-
-        *ret = kval;
-
-        return 0;
+        return cg_get_attribute_as_uint64("memory", u->cgroup_path, file, ret);
 }
 
 static int unit_compare_memory_limit(Unit *u, const char *property_name, uint64_t *ret_unit_value, uint64_t *ret_kernel_value) {
@@ -1007,12 +990,12 @@ static int cgroup_apply_devices(Unit *u) {
                                       "Failed to reset devices.allow/devices.deny: %m");
         }
 
-        bool whitelist_static = policy == CGROUP_DEVICE_POLICY_CLOSED ||
+        bool allow_list_static = policy == CGROUP_DEVICE_POLICY_CLOSED ||
                 (policy == CGROUP_DEVICE_POLICY_AUTO && c->device_allow);
-        if (whitelist_static)
-                (void) bpf_devices_whitelist_static(prog, path);
+        if (allow_list_static)
+                (void) bpf_devices_allow_list_static(prog, path);
 
-        bool any = whitelist_static;
+        bool any = allow_list_static;
         LIST_FOREACH(device_allow, a, c->device_allow) {
                 char acc[4], *val;
                 unsigned k = 0;
@@ -1028,11 +1011,11 @@ static int cgroup_apply_devices(Unit *u) {
                 acc[k++] = 0;
 
                 if (path_startswith(a->path, "/dev/"))
-                        r = bpf_devices_whitelist_device(prog, path, a->path, acc);
+                        r = bpf_devices_allow_list_device(prog, path, a->path, acc);
                 else if ((val = startswith(a->path, "block-")))
-                        r = bpf_devices_whitelist_major(prog, path, val, 'b', acc);
+                        r = bpf_devices_allow_list_major(prog, path, val, 'b', acc);
                 else if ((val = startswith(a->path, "char-")))
-                        r = bpf_devices_whitelist_major(prog, path, val, 'c', acc);
+                        r = bpf_devices_allow_list_major(prog, path, val, 'c', acc);
                 else {
                         log_unit_debug(u, "Ignoring device '%s' while writing cgroup attribute.", a->path);
                         continue;
@@ -1046,7 +1029,7 @@ static int cgroup_apply_devices(Unit *u) {
                 log_unit_warning_errno(u, SYNTHETIC_ERRNO(ENODEV), "No devices matched by device filter.");
 
                 /* The kernel verifier would reject a program we would build with the normal intro and outro
-                   but no whitelisting rules (outro would contain an unreachable instruction for successful
+                   but no allow-listing rules (outro would contain an unreachable instruction for successful
                    return). */
                 policy = CGROUP_DEVICE_POLICY_STRICT;
         }
@@ -2680,6 +2663,16 @@ void unit_add_to_cgroup_empty_queue(Unit *u) {
                 log_debug_errno(r, "Failed to enable cgroup empty event source: %m");
 }
 
+static void unit_remove_from_cgroup_empty_queue(Unit *u) {
+        assert(u);
+
+        if (!u->in_cgroup_empty_queue)
+                return;
+
+        LIST_REMOVE(cgroup_empty_queue, u->manager->cgroup_empty_queue, u);
+        u->in_cgroup_empty_queue = false;
+}
+
 int unit_check_oom(Unit *u) {
         _cleanup_free_ char *oom_kill = NULL;
         bool increased;
@@ -2780,6 +2773,41 @@ static void unit_add_to_cgroup_oom_queue(Unit *u) {
                 log_error_errno(r, "Failed to enable cgroup oom event source: %m");
 }
 
+static int unit_check_cgroup_events(Unit *u) {
+        char *values[2] = {};
+        int r;
+
+        assert(u);
+
+        r = cg_get_keyed_attribute_graceful(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, "cgroup.events",
+                                            STRV_MAKE("populated", "frozen"), values);
+        if (r < 0)
+                return r;
+
+        /* The cgroup.events notifications can be merged together so act as we saw the given state for the
+         * first time. The functions we call to handle given state are idempotent, which makes them
+         * effectively remember the previous state. */
+        if (values[0]) {
+                if (streq(values[0], "1"))
+                        unit_remove_from_cgroup_empty_queue(u);
+                else
+                        unit_add_to_cgroup_empty_queue(u);
+        }
+
+        /* Disregard freezer state changes due to operations not initiated by us */
+        if (values[1] && IN_SET(u->freezer_state, FREEZER_FREEZING, FREEZER_THAWING)) {
+                if (streq(values[1], "0"))
+                        unit_thawed(u);
+                else
+                        unit_frozen(u);
+        }
+
+        free(values[0]);
+        free(values[1]);
+
+        return 0;
+}
+
 static int on_cgroup_inotify_event(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
         Manager *m = userdata;
 
@@ -2816,7 +2844,7 @@ static int on_cgroup_inotify_event(sd_event_source *s, int fd, uint32_t revents,
 
                         u = hashmap_get(m->cgroup_control_inotify_wd_unit, INT_TO_PTR(e->wd));
                         if (u)
-                                unit_add_to_cgroup_empty_queue(u);
+                                unit_check_cgroup_events(u);
 
                         u = hashmap_get(m->cgroup_memory_inotify_wd_unit, INT_TO_PTR(e->wd));
                         if (u)
@@ -3112,7 +3140,6 @@ int manager_notify_cgroup_empty(Manager *m, const char *cgroup) {
 }
 
 int unit_get_memory_current(Unit *u, uint64_t *ret) {
-        _cleanup_free_ char *v = NULL;
         int r;
 
         assert(u);
@@ -3134,22 +3161,11 @@ int unit_get_memory_current(Unit *u, uint64_t *ret) {
         r = cg_all_unified();
         if (r < 0)
                 return r;
-        if (r > 0)
-                r = cg_get_attribute("memory", u->cgroup_path, "memory.current", &v);
-        else
-                r = cg_get_attribute("memory", u->cgroup_path, "memory.usage_in_bytes", &v);
-        if (r == -ENOENT)
-                return -ENODATA;
-        if (r < 0)
-                return r;
 
-        return safe_atou64(v, ret);
+        return cg_get_attribute_as_uint64("memory", u->cgroup_path, r > 0 ? "memory.current" : "memory.usage_in_bytes", ret);
 }
 
 int unit_get_tasks_current(Unit *u, uint64_t *ret) {
-        _cleanup_free_ char *v = NULL;
-        int r;
-
         assert(u);
         assert(ret);
 
@@ -3166,17 +3182,10 @@ int unit_get_tasks_current(Unit *u, uint64_t *ret) {
         if ((u->cgroup_realized_mask & CGROUP_MASK_PIDS) == 0)
                 return -ENODATA;
 
-        r = cg_get_attribute("pids", u->cgroup_path, "pids.current", &v);
-        if (r == -ENOENT)
-                return -ENODATA;
-        if (r < 0)
-                return r;
-
-        return safe_atou64(v, ret);
+        return cg_get_attribute_as_uint64("pids", u->cgroup_path, "pids.current", ret);
 }
 
 static int unit_get_cpu_usage_raw(Unit *u, nsec_t *ret) {
-        _cleanup_free_ char *v = NULL;
         uint64_t ns;
         int r;
 
@@ -3212,17 +3221,8 @@ static int unit_get_cpu_usage_raw(Unit *u, nsec_t *ret) {
                         return r;
 
                 ns = us * NSEC_PER_USEC;
-        } else {
-                r = cg_get_attribute("cpuacct", u->cgroup_path, "cpuacct.usage", &v);
-                if (r == -ENOENT)
-                        return -ENODATA;
-                if (r < 0)
-                        return r;
-
-                r = safe_atou64(v, &ns);
-                if (r < 0)
-                        return r;
-        }
+        } else
+                return cg_get_attribute_as_uint64("cpuacct", u->cgroup_path, "cpuacct.usage", ret);
 
         *ret = ns;
         return 0;
@@ -3597,6 +3597,46 @@ int compare_job_priority(const void *a, const void *b) {
         return strcmp(x->unit->id, y->unit->id);
 }
 
+int unit_cgroup_freezer_action(Unit *u, FreezerAction action) {
+        _cleanup_free_ char *path = NULL;
+        FreezerState target, kernel = _FREEZER_STATE_INVALID;
+        int r;
+
+        assert(u);
+        assert(IN_SET(action, FREEZER_FREEZE, FREEZER_THAW));
+
+        if (!u->cgroup_realized)
+                return -EBUSY;
+
+        target = action == FREEZER_FREEZE ? FREEZER_FROZEN : FREEZER_RUNNING;
+
+        r = unit_freezer_state_kernel(u, &kernel);
+        if (r < 0)
+                log_unit_debug_errno(u, r, "Failed to obtain cgroup freezer state: %m");
+
+        if (target == kernel) {
+                u->freezer_state = target;
+                return 0;
+        }
+
+        r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, "cgroup.freeze", &path);
+        if (r < 0)
+                return r;
+
+        log_unit_debug(u, "%s unit.", action == FREEZER_FREEZE ? "Freezing" : "Thawing");
+
+        if (action == FREEZER_FREEZE)
+                u->freezer_state = FREEZER_FREEZING;
+        else
+                u->freezer_state = FREEZER_THAWING;
+
+        r = write_string_file(path, one_zero(action == FREEZER_FREEZE), WRITE_STRING_FILE_DISABLE_BUFFER);
+        if (r < 0)
+                return r;
+
+        return 1;
+}
+
 static const char* const cgroup_device_policy_table[_CGROUP_DEVICE_POLICY_MAX] = {
         [CGROUP_DEVICE_POLICY_AUTO]   = "auto",
         [CGROUP_DEVICE_POLICY_CLOSED] = "closed",
@@ -3632,3 +3672,10 @@ int unit_get_cpuset(Unit *u, CPUSet *cpus, const char *name) {
 }
 
 DEFINE_STRING_TABLE_LOOKUP(cgroup_device_policy, CGroupDevicePolicy);
+
+static const char* const freezer_action_table[_FREEZER_ACTION_MAX] = {
+        [FREEZER_FREEZE] = "freeze",
+        [FREEZER_THAW] = "thaw",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(freezer_action, FreezerAction);
index b6bd4e0de5312d067204d8305d5ded5c0f75da93..52d028e740ff38e7c29a07be98f32c1e8c15adca 100644 (file)
@@ -47,6 +47,14 @@ typedef enum CGroupDevicePolicy {
         _CGROUP_DEVICE_POLICY_INVALID = -1
 } CGroupDevicePolicy;
 
+typedef enum FreezerAction {
+        FREEZER_FREEZE,
+        FREEZER_THAW,
+
+        _FREEZER_ACTION_MAX,
+        _FREEZER_ACTION_INVALID = -1,
+} FreezerAction;
+
 struct CGroupDeviceAllow {
         LIST_FIELDS(CGroupDeviceAllow, device_allow);
         char *path;
@@ -274,3 +282,7 @@ bool unit_cgroup_delegate(Unit *u);
 int compare_job_priority(const void *a, const void *b);
 
 int unit_get_cpuset(Unit *u, CPUSet *cpus, const char *name);
+int unit_cgroup_freezer_action(Unit *u, FreezerAction action);
+
+const char* freezer_action_to_string(FreezerAction a) _const_;
+FreezerAction freezer_action_from_string(const char *s) _pure_;
index bd6e6a9dde15ea73625272f6fe2988f2ac7a9f16..70b85d8023ff0bf0593b7908693d8be8ee3a3d77 100644 (file)
@@ -1,7 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
 #include "automount.h"
-#include "bus-util.h"
+#include "bus-get-properties.h"
 #include "dbus-automount.h"
 #include "dbus-util.h"
 #include "string-util.h"
index 27dc9e43c3e202adc441616cd1607cc4f76d95ad..b7d2e32639a51699b5df2404c3260e73bb672a9c 100644 (file)
@@ -5,7 +5,7 @@
 #include "af-list.h"
 #include "alloc-util.h"
 #include "bpf-firewall.h"
-#include "bus-util.h"
+#include "bus-get-properties.h"
 #include "cgroup-util.h"
 #include "cgroup.h"
 #include "dbus-cgroup.h"
@@ -707,8 +707,7 @@ static int bus_cgroup_set_boolean(
                 return 1;                                               \
         }
 
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wtype-limits"
+DISABLE_WARNING_TYPE_LIMITS;
 BUS_DEFINE_SET_CGROUP_WEIGHT(cpu_weight, CGROUP_MASK_CPU, CGROUP_WEIGHT_IS_OK, CGROUP_WEIGHT_INVALID);
 BUS_DEFINE_SET_CGROUP_WEIGHT(cpu_shares, CGROUP_MASK_CPU, CGROUP_CPU_SHARES_IS_OK, CGROUP_CPU_SHARES_INVALID);
 BUS_DEFINE_SET_CGROUP_WEIGHT(io_weight, CGROUP_MASK_IO, CGROUP_WEIGHT_IS_OK, CGROUP_WEIGHT_INVALID);
@@ -716,7 +715,7 @@ BUS_DEFINE_SET_CGROUP_WEIGHT(blockio_weight, CGROUP_MASK_BLKIO, CGROUP_BLKIO_WEI
 BUS_DEFINE_SET_CGROUP_LIMIT(memory, CGROUP_MASK_MEMORY, physical_memory_scale, 1);
 BUS_DEFINE_SET_CGROUP_LIMIT(memory_protection, CGROUP_MASK_MEMORY, physical_memory_scale, 0);
 BUS_DEFINE_SET_CGROUP_LIMIT(swap, CGROUP_MASK_MEMORY, physical_memory_scale, 0);
-#pragma GCC diagnostic pop
+REENABLE_WARNING;
 
 static int bus_cgroup_set_tasks_max(
                 Unit *u,
index 93857436b4efea57c8bedb42f168fbc5fd2e15bd..50f7ada8cefb2f32efe790c5698bdcbbc44ff578 100644 (file)
@@ -9,7 +9,7 @@
 
 #include "af-list.h"
 #include "alloc-util.h"
-#include "bus-util.h"
+#include "bus-get-properties.h"
 #include "cap-list.h"
 #include "capability-util.h"
 #include "cpu-set-util.h"
@@ -58,7 +58,6 @@ static BUS_DEFINE_PROPERTY_GET_REF(property_get_syslog_level, "i", int, LOG_PRI)
 static BUS_DEFINE_PROPERTY_GET_REF(property_get_syslog_facility, "i", int, LOG_FAC);
 static BUS_DEFINE_PROPERTY_GET(property_get_cpu_affinity_from_numa, "b", ExecContext, exec_context_get_cpu_affinity_from_numa);
 
-
 static int property_get_environment_files(
                 sd_bus *bus,
                 const char *path,
@@ -102,6 +101,7 @@ static int property_get_oom_score_adjust(
 
         ExecContext *c = userdata;
         int32_t n;
+        int r;
 
         assert(bus);
         assert(reply);
@@ -113,13 +113,55 @@ static int property_get_oom_score_adjust(
                 _cleanup_free_ char *t = NULL;
 
                 n = 0;
-                if (read_one_line_file("/proc/self/oom_score_adj", &t) >= 0)
-                        safe_atoi32(t, &n);
+                r = read_one_line_file("/proc/self/oom_score_adj", &t);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to read /proc/self/oom_score_adj, ignoring: %m");
+                else {
+                        r = safe_atoi32(t, &n);
+                        if (r < 0)
+                                log_debug_errno(r, "Failed to parse \"%s\" from /proc/self/oom_score_adj, ignoring: %m", t);
+                }
         }
 
         return sd_bus_message_append(reply, "i", n);
 }
 
+static int property_get_coredump_filter(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        ExecContext *c = userdata;
+        uint64_t n;
+        int r;
+
+        assert(bus);
+        assert(reply);
+        assert(c);
+
+        if (c->coredump_filter_set)
+                n = c->coredump_filter;
+        else {
+                _cleanup_free_ char *t = NULL;
+
+                n = COREDUMP_FILTER_MASK_DEFAULT;
+                r = read_one_line_file("/proc/self/coredump_filter", &t);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to read /proc/self/coredump_filter, ignoring: %m");
+                else {
+                        r = safe_atoux64(t, &n);
+                        if (r < 0)
+                                log_debug_errno(r, "Failed to parse \"%s\" from /proc/self/coredump_filter, ignoring: %m", t);
+                }
+        }
+
+        return sd_bus_message_append(reply, "t", n);
+}
+
 static int property_get_nice(
                 sd_bus *bus,
                 const char *path,
@@ -328,7 +370,7 @@ static int property_get_syscall_filter(
         if (r < 0)
                 return r;
 
-        r = sd_bus_message_append(reply, "b", c->syscall_whitelist);
+        r = sd_bus_message_append(reply, "b", c->syscall_allow_list);
         if (r < 0)
                 return r;
 
@@ -494,7 +536,7 @@ static int property_get_address_families(
         if (r < 0)
                 return r;
 
-        r = sd_bus_message_append(reply, "b", c->address_families_whitelist);
+        r = sd_bus_message_append(reply, "b", c->address_families_allow_list);
         if (r < 0)
                 return r;
 
@@ -704,6 +746,44 @@ static int property_get_log_extra_fields(
         return sd_bus_message_close_container(reply);
 }
 
+static int property_get_root_hash(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        ExecContext *c = userdata;
+
+        assert(bus);
+        assert(c);
+        assert(property);
+        assert(reply);
+
+        return sd_bus_message_append_array(reply, 'y', c->root_hash, c->root_hash_size);
+}
+
+static int property_get_root_hash_sig(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        ExecContext *c = userdata;
+
+        assert(bus);
+        assert(c);
+        assert(property);
+        assert(reply);
+
+        return sd_bus_message_append_array(reply, 'y', c->root_hash_sig, c->root_hash_sig_size);
+}
+
 const sd_bus_vtable bus_exec_vtable[] = {
         SD_BUS_VTABLE_START(0),
         SD_BUS_PROPERTY("Environment", "as", NULL, offsetof(ExecContext, environment), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -746,7 +826,13 @@ const sd_bus_vtable bus_exec_vtable[] = {
         SD_BUS_PROPERTY("WorkingDirectory", "s", property_get_working_directory, 0, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(ExecContext, root_directory), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("RootImage", "s", NULL, offsetof(ExecContext, root_image), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("RootHash", "ay", property_get_root_hash, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("RootHashPath", "s", NULL, offsetof(ExecContext, root_hash_path), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("RootHashSignature", "ay", property_get_root_hash_sig, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("RootHashSignaturePath", "s", NULL, offsetof(ExecContext, root_hash_sig_path), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("RootVerity", "s", NULL, offsetof(ExecContext, root_verity), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("OOMScoreAdjust", "i", property_get_oom_score_adjust, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("CoredumpFilter", "t", property_get_coredump_filter, 0, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("Nice", "i", property_get_nice, 0, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("IOSchedulingClass", "i", property_get_ioprio_class, 0, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("IOSchedulingPriority", "i", property_get_ioprio_priority, 0, SD_BUS_VTABLE_PROPERTY_CONST),
@@ -1215,6 +1301,102 @@ int bus_exec_context_set_transient_property(
         if (streq(name, "RootImage"))
                 return bus_set_transient_path(u, name, &c->root_image, message, flags, error);
 
+        if (streq(name, "RootHash")) {
+                const void *roothash_decoded;
+                size_t roothash_decoded_size;
+
+                r = sd_bus_message_read_array(message, 'y', &roothash_decoded, &roothash_decoded_size);
+                if (r < 0)
+                        return r;
+
+                if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+                        _cleanup_free_ char *encoded = NULL;
+
+                        if (roothash_decoded_size == 0) {
+                                c->root_hash_path = mfree(c->root_hash_path);
+                                c->root_hash = mfree(c->root_hash);
+                                c->root_hash_size = 0;
+
+                                unit_write_settingf(u, flags, name, "RootHash=");
+                        } else {
+                                _cleanup_free_ void *p;
+
+                                encoded = hexmem(roothash_decoded, roothash_decoded_size);
+                                if (!encoded)
+                                        return -ENOMEM;
+
+                                p = memdup(roothash_decoded, roothash_decoded_size);
+                                if (!p)
+                                        return -ENOMEM;
+
+                                free_and_replace(c->root_hash, p);
+                                c->root_hash_size = roothash_decoded_size;
+                                c->root_hash_path = mfree(c->root_hash_path);
+
+                                unit_write_settingf(u, flags, name, "RootHash=%s", encoded);
+                        }
+                }
+
+                return 1;
+        }
+
+        if (streq(name, "RootHashPath")) {
+                c->root_hash_size = 0;
+                c->root_hash = mfree(c->root_hash);
+
+                return bus_set_transient_path(u, "RootHash", &c->root_hash_path, message, flags, error);
+        }
+
+        if (streq(name, "RootHashSignature")) {
+                const void *roothash_sig_decoded;
+                size_t roothash_sig_decoded_size;
+
+                r = sd_bus_message_read_array(message, 'y', &roothash_sig_decoded, &roothash_sig_decoded_size);
+                if (r < 0)
+                        return r;
+
+                if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+                        _cleanup_free_ char *encoded = NULL;
+
+                        if (roothash_sig_decoded_size == 0) {
+                                c->root_hash_sig_path = mfree(c->root_hash_sig_path);
+                                c->root_hash_sig = mfree(c->root_hash_sig);
+                                c->root_hash_sig_size = 0;
+
+                                unit_write_settingf(u, flags, name, "RootHashSignature=");
+                        } else {
+                                _cleanup_free_ void *p;
+                                ssize_t len;
+
+                                len = base64mem(roothash_sig_decoded, roothash_sig_decoded_size, &encoded);
+                                if (len < 0)
+                                        return -ENOMEM;
+
+                                p = memdup(roothash_sig_decoded, roothash_sig_decoded_size);
+                                if (!p)
+                                        return -ENOMEM;
+
+                                free_and_replace(c->root_hash_sig, p);
+                                c->root_hash_sig_size = roothash_sig_decoded_size;
+                                c->root_hash_sig_path = mfree(c->root_hash_sig_path);
+
+                                unit_write_settingf(u, flags, name, "RootHashSignature=base64:%s", encoded);
+                        }
+                }
+
+                return 1;
+        }
+
+        if (streq(name, "RootHashSignaturePath")) {
+                c->root_hash_sig_size = 0;
+                c->root_hash_sig = mfree(c->root_hash_sig);
+
+                return bus_set_transient_path(u, "RootHashSignature", &c->root_hash_sig_path, message, flags, error);
+        }
+
+        if (streq(name, "RootVerity"))
+                return bus_set_transient_path(u, name, &c->root_verity, message, flags, error);
+
         if (streq(name, "RootDirectory"))
                 return bus_set_transient_path(u, name, &c->root_directory, message, flags, error);
 
@@ -1558,14 +1740,14 @@ int bus_exec_context_set_transient_property(
                 return bus_set_transient_errno(u, name, &c->syscall_errno, message, flags, error);
 
         if (streq(name, "SystemCallFilter")) {
-                int whitelist;
+                int allow_list;
                 _cleanup_strv_free_ char **l = NULL;
 
                 r = sd_bus_message_enter_container(message, 'r', "bas");
                 if (r < 0)
                         return r;
 
-                r = sd_bus_message_read(message, "b", &whitelist);
+                r = sd_bus_message_read(message, "b", &allow_list);
                 if (r < 0)
                         return r;
 
@@ -1579,11 +1761,11 @@ int bus_exec_context_set_transient_property(
 
                 if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
                         _cleanup_free_ char *joined = NULL;
-                        SeccompParseFlags invert_flag = whitelist ? 0 : SECCOMP_PARSE_INVERT;
+                        SeccompParseFlags invert_flag = allow_list ? 0 : SECCOMP_PARSE_INVERT;
                         char **s;
 
                         if (strv_isempty(l)) {
-                                c->syscall_whitelist = false;
+                                c->syscall_allow_list = false;
                                 c->syscall_filter = hashmap_free(c->syscall_filter);
 
                                 unit_write_settingf(u, flags, name, "SystemCallFilter=");
@@ -1595,14 +1777,14 @@ int bus_exec_context_set_transient_property(
                                 if (!c->syscall_filter)
                                         return log_oom();
 
-                                c->syscall_whitelist = whitelist;
+                                c->syscall_allow_list = allow_list;
 
-                                if (c->syscall_whitelist) {
+                                if (c->syscall_allow_list) {
                                         r = seccomp_parse_syscall_filter("@default",
                                                                          -1,
                                                                          c->syscall_filter,
                                                                          SECCOMP_PARSE_PERMISSIVE |
-                                                                         SECCOMP_PARSE_WHITELIST | invert_flag,
+                                                                         SECCOMP_PARSE_ALLOW_LIST | invert_flag,
                                                                          u->id,
                                                                          NULL, 0);
                                         if (r < 0)
@@ -1623,7 +1805,7 @@ int bus_exec_context_set_transient_property(
                                                                  c->syscall_filter,
                                                                  SECCOMP_PARSE_LOG | SECCOMP_PARSE_PERMISSIVE |
                                                                  invert_flag |
-                                                                 (c->syscall_whitelist ? SECCOMP_PARSE_WHITELIST : 0),
+                                                                 (c->syscall_allow_list ? SECCOMP_PARSE_ALLOW_LIST : 0),
                                                                  u->id,
                                                                  NULL, 0);
                                 if (r < 0)
@@ -1634,7 +1816,7 @@ int bus_exec_context_set_transient_property(
                         if (!joined)
                                 return -ENOMEM;
 
-                        unit_write_settingf(u, flags, name, "SystemCallFilter=%s%s", whitelist ? "" : "~", joined);
+                        unit_write_settingf(u, flags, name, "SystemCallFilter=%s%s", allow_list ? "" : "~", joined);
                 }
 
                 return 1;
@@ -1654,10 +1836,6 @@ int bus_exec_context_set_transient_property(
                         else {
                                 char **s;
 
-                                r = set_ensure_allocated(&c->syscall_archs, NULL);
-                                if (r < 0)
-                                        return r;
-
                                 STRV_FOREACH(s, l) {
                                         uint32_t a;
 
@@ -1665,7 +1843,7 @@ int bus_exec_context_set_transient_property(
                                         if (r < 0)
                                                 return r;
 
-                                        r = set_put(c->syscall_archs, UINT32_TO_PTR(a + 1));
+                                        r = set_ensure_put(&c->syscall_archs, NULL, UINT32_TO_PTR(a + 1));
                                         if (r < 0)
                                                 return r;
                                 }
@@ -1682,14 +1860,14 @@ int bus_exec_context_set_transient_property(
                 return 1;
 
         } else if (streq(name, "RestrictAddressFamilies")) {
-                int whitelist;
+                int allow_list;
                 _cleanup_strv_free_ char **l = NULL;
 
                 r = sd_bus_message_enter_container(message, 'r', "bas");
                 if (r < 0)
                         return r;
 
-                r = sd_bus_message_read(message, "b", &whitelist);
+                r = sd_bus_message_read(message, "b", &allow_list);
                 if (r < 0)
                         return r;
 
@@ -1706,7 +1884,7 @@ int bus_exec_context_set_transient_property(
                         char **s;
 
                         if (strv_isempty(l)) {
-                                c->address_families_whitelist = false;
+                                c->address_families_allow_list = false;
                                 c->address_families = set_free(c->address_families);
 
                                 unit_write_settingf(u, flags, name, "RestrictAddressFamilies=");
@@ -1718,7 +1896,7 @@ int bus_exec_context_set_transient_property(
                                 if (!c->address_families)
                                         return log_oom();
 
-                                c->address_families_whitelist = whitelist;
+                                c->address_families_allow_list = allow_list;
                         }
 
                         STRV_FOREACH(s, l) {
@@ -1728,7 +1906,7 @@ int bus_exec_context_set_transient_property(
                                 if (af < 0)
                                         return af;
 
-                                if (whitelist == c->address_families_whitelist) {
+                                if (allow_list == c->address_families_allow_list) {
                                         r = set_put(c->address_families, INT_TO_PTR(af));
                                         if (r < 0)
                                                 return r;
@@ -1740,7 +1918,7 @@ int bus_exec_context_set_transient_property(
                         if (!joined)
                                 return -ENOMEM;
 
-                        unit_write_settingf(u, flags, name, "RestrictAddressFamilies=%s%s", whitelist ? "" : "~", joined);
+                        unit_write_settingf(u, flags, name, "RestrictAddressFamilies=%s%s", allow_list ? "" : "~", joined);
                 }
 
                 return 1;
@@ -2190,6 +2368,21 @@ int bus_exec_context_set_transient_property(
 
                 return 1;
 
+        } else if (streq(name, "CoredumpFilter")) {
+                uint64_t f;
+
+                r = sd_bus_message_read(message, "t", &f);
+                if (r < 0)
+                        return r;
+
+                if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+                        c->coredump_filter = f;
+                        c->coredump_filter_set = true;
+                        unit_write_settingf(u, flags, name, "CoredumpFilter=0x%"PRIx64, f);
+                }
+
+                return 1;
+
         } else if (streq(name, "EnvironmentFiles")) {
 
                 _cleanup_free_ char *joined = NULL;
index 6151326f7243e4aa7b631363e752e0fd9a6af0ff..33e4128909bbe2e7721e1808106b8488a44cf90a 100644 (file)
@@ -3,6 +3,7 @@
 #include "sd-bus.h"
 
 #include "alloc-util.h"
+#include "bus-get-properties.h"
 #include "bus-util.h"
 #include "dbus-job.h"
 #include "dbus-unit.h"
@@ -118,9 +119,21 @@ int bus_job_method_get_waiting_jobs(sd_bus_message *message, void *userdata, sd_
 
 const sd_bus_vtable bus_job_vtable[] = {
         SD_BUS_VTABLE_START(0),
+
         SD_BUS_METHOD("Cancel", NULL, NULL, bus_job_method_cancel, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("GetAfter", NULL, "a(usssoo)", bus_job_method_get_waiting_jobs, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("GetBefore", NULL, "a(usssoo)", bus_job_method_get_waiting_jobs, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("GetAfter",
+                                 NULL,,
+                                 "a(usssoo)",
+                                 SD_BUS_PARAM(jobs),
+                                 bus_job_method_get_waiting_jobs,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("GetBefore",
+                                 NULL,,
+                                 "a(usssoo)",
+                                 SD_BUS_PARAM(jobs),
+                                 bus_job_method_get_waiting_jobs,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+
         SD_BUS_PROPERTY("Id", "u", NULL, offsetof(Job, id), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("Unit", "(so)", property_get_unit, 0, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("JobType", "s", property_get_type, offsetof(Job, type), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -128,6 +141,58 @@ const sd_bus_vtable bus_job_vtable[] = {
         SD_BUS_VTABLE_END
 };
 
+static int bus_job_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
+        Manager *m = userdata;
+        Job *j;
+        int r;
+
+        assert(bus);
+        assert(path);
+        assert(interface);
+        assert(found);
+        assert(m);
+
+        r = manager_get_job_from_dbus_path(m, path, &j);
+        if (r < 0)
+                return 0;
+
+        *found = j;
+        return 1;
+}
+
+static int bus_job_enumerate(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
+        _cleanup_strv_free_ char **l = NULL;
+        Manager *m = userdata;
+        unsigned k = 0;
+        Iterator i;
+        Job *j;
+
+        l = new0(char*, hashmap_size(m->jobs)+1);
+        if (!l)
+                return -ENOMEM;
+
+        HASHMAP_FOREACH(j, m->jobs, i) {
+                l[k] = job_dbus_path(j);
+                if (!l[k])
+                        return -ENOMEM;
+
+                k++;
+        }
+
+        assert(hashmap_size(m->jobs) == k);
+
+        *nodes = TAKE_PTR(l);
+
+        return k;
+}
+
+const BusObjectImplementation job_object = {
+        "/org/freedesktop/systemd1/job",
+        "org.freedesktop.systemd1.Job",
+        .fallback_vtables = BUS_FALLBACK_VTABLES({bus_job_vtable, bus_job_find}),
+        .node_enumerator = bus_job_enumerate,
+};
+
 static int send_new_signal(sd_bus *bus, void *userdata) {
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
         _cleanup_free_ char *p = NULL;
index 380e117fca9141418c1ee14cd4f1f352a5a38ede..96c5b66309a9696aeba1bfd3ac10b36dabada339 100644 (file)
@@ -2,11 +2,12 @@
 #pragma once
 
 #include "sd-bus.h"
-#include "sd-bus-vtable.h"
 
 #include "unit.h"
+#include "bus-object.h"
 
 extern const sd_bus_vtable bus_job_vtable[];
+extern const BusObjectImplementation job_object;
 
 int bus_job_method_cancel(sd_bus_message *message, void *job, sd_bus_error *error);
 int bus_job_method_get_waiting_jobs(sd_bus_message *message, void *userdata, sd_bus_error *error);
index 30597e86f0d1dabb6c2087fb2dfa29deafd8463d..eda3410375a30362a98a83632627a4c0e5b0b791 100644 (file)
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
-#include "bus-util.h"
+#include "bus-get-properties.h"
 #include "dbus-kill.h"
 #include "dbus-util.h"
 #include "kill.h"
index ef4bb316ccef73f0f77b79cdb4f89bb1c927c526..07e139c5ad265680de5bf1a9b1b849b97637e816 100644 (file)
@@ -9,7 +9,8 @@
 #include "architecture.h"
 #include "build.h"
 #include "bus-common-errors.h"
-#include "bus-util.h"
+#include "bus-get-properties.h"
+#include "bus-log-control-api.h"
 #include "dbus-cgroup.h"
 #include "dbus-execute.h"
 #include "dbus-job.h"
@@ -50,7 +51,6 @@ BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_oom_policy, oom_policy, OOMPolicy)
 static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_version, "s", GIT_VERSION);
 static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_features, "s", SYSTEMD_FEATURES);
 static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_architecture, "s", architecture_to_string(uname_architecture()));
-static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_log_target, "s", log_target_to_string(log_get_target()));
 static BUS_DEFINE_PROPERTY_GET2(property_get_system_state, "s", Manager, manager_state, manager_state_to_string);
 static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_timer_slack_nsec, "t", (uint64_t) prctl(PR_GET_TIMERSLACK));
 static BUS_DEFINE_PROPERTY_GET_REF(property_get_hashmap_size, "u", Hashmap *, hashmap_size);
@@ -141,28 +141,6 @@ static int property_set_log_target(
         return 0;
 }
 
-static int property_get_log_level(
-                sd_bus *bus,
-                const char *path,
-                const char *interface,
-                const char *property,
-                sd_bus_message *reply,
-                void *userdata,
-                sd_bus_error *error) {
-
-        _cleanup_free_ char *t = NULL;
-        int r;
-
-        assert(bus);
-        assert(reply);
-
-        r = log_level_to_string_alloc(log_get_max_level(), &t);
-        if (r < 0)
-                return r;
-
-        return sd_bus_message_append(reply, "s", t);
-}
-
 static int property_set_log_level(
                 sd_bus *bus,
                 const char *path,
@@ -256,38 +234,124 @@ static int property_get_show_status(
                 sd_bus_error *error) {
 
         Manager *m = userdata;
-        int b;
 
+        assert(m);
         assert(bus);
         assert(reply);
+
+        return sd_bus_message_append(reply, "b", manager_get_show_status_on(m));
+}
+
+static int property_get_runtime_watchdog(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        Manager *m = userdata;
+
         assert(m);
+        assert(bus);
+        assert(reply);
 
-        b = IN_SET(m->show_status, SHOW_STATUS_TEMPORARY, SHOW_STATUS_YES);
-        return sd_bus_message_append_basic(reply, 'b', &b);
+        return sd_bus_message_append(reply, "t", manager_get_watchdog(m, WATCHDOG_RUNTIME));
 }
 
-static int property_set_runtime_watchdog(
+static int property_get_reboot_watchdog(
                 sd_bus *bus,
                 const char *path,
                 const char *interface,
                 const char *property,
-                sd_bus_message *value,
+                sd_bus_message *reply,
                 void *userdata,
                 sd_bus_error *error) {
 
-        usec_t *t = userdata;
-        int r;
+        Manager *m = userdata;
 
+        assert(m);
         assert(bus);
+        assert(reply);
+
+        return sd_bus_message_append(reply, "t", manager_get_watchdog(m, WATCHDOG_REBOOT));
+}
+
+static int property_get_kexec_watchdog(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        Manager *m = userdata;
+
+        assert(m);
+        assert(bus);
+        assert(reply);
+
+        return sd_bus_message_append(reply, "t", manager_get_watchdog(m, WATCHDOG_KEXEC));
+}
+
+static int property_set_watchdog(Manager *m, WatchdogType type, sd_bus_message *value) {
+        usec_t timeout;
+        int r;
+
+        assert(m);
         assert(value);
 
         assert_cc(sizeof(usec_t) == sizeof(uint64_t));
 
-        r = sd_bus_message_read(value, "t", t);
+        r = sd_bus_message_read(value, "t", &timeout);
         if (r < 0)
                 return r;
 
-        return watchdog_set_timeout(t);
+        return manager_override_watchdog(m, type, timeout);
+}
+
+static int property_set_runtime_watchdog(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *value,
+                void *userdata,
+                sd_bus_error *error) {
+
+        return property_set_watchdog(userdata, WATCHDOG_RUNTIME, value);
+}
+
+static int property_set_reboot_watchdog(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *value,
+                void *userdata,
+                sd_bus_error *error) {
+
+        return property_set_watchdog(userdata, WATCHDOG_REBOOT, value);
+}
+
+static int property_set_kexec_watchdog(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *value,
+                void *userdata,
+                sd_bus_error *error) {
+
+        _unused_ Manager *m = userdata;
+
+        assert(m);
+        assert(bus);
+        assert(value);
+
+        return property_set_watchdog(userdata, WATCHDOG_KEXEC, value);
 }
 
 static int bus_get_unit_by_name(Manager *m, sd_bus_message *message, const char *name, Unit **ret_unit, sd_bus_error *error) {
@@ -642,6 +706,14 @@ static int method_clean_unit(sd_bus_message *message, void *userdata, sd_bus_err
         return method_generic_unit_operation(message, userdata, error, bus_unit_method_clean, GENERIC_UNIT_LOAD|GENERIC_UNIT_VALIDATE_LOADED);
 }
 
+static int method_freeze_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        return method_generic_unit_operation(message, userdata, error, bus_unit_method_freeze, 0);
+}
+
+static int method_thaw_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        return method_generic_unit_operation(message, userdata, error, bus_unit_method_thaw, 0);
+}
+
 static int method_reset_failed_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         /* Don't load the unit (because unloaded units can't be in failed state), and don't insist on the
          * unit to be loaded properly (since a failed unit might have its unit file disappeared) */
@@ -2378,6 +2450,30 @@ static int method_abandon_scope(sd_bus_message *message, void *userdata, sd_bus_
         return bus_scope_method_abandon(message, u, error);
 }
 
+static int method_set_show_status(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        Manager *m = userdata;
+        ShowStatus mode = _SHOW_STATUS_INVALID;
+        const char *t;
+        int r;
+
+        assert(m);
+        assert(message);
+
+        r = sd_bus_message_read(message, "s", &t);
+        if (r < 0)
+                return r;
+
+        if (!isempty(t)) {
+                mode = show_status_from_string(t);
+                if (mode < 0)
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid show status '%s'", t);
+        }
+
+        manager_override_show_status(m, mode, "bus");
+
+        return sd_bus_reply_method_return(message, NULL);
+}
+
 const sd_bus_vtable bus_manager_vtable[] = {
         SD_BUS_VTABLE_START(0),
 
@@ -2404,8 +2500,8 @@ const sd_bus_vtable bus_manager_vtable[] = {
         BUS_PROPERTY_DUAL_TIMESTAMP("InitRDGeneratorsFinishTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_INITRD_GENERATORS_FINISH]), SD_BUS_VTABLE_PROPERTY_CONST),
         BUS_PROPERTY_DUAL_TIMESTAMP("InitRDUnitsLoadStartTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_INITRD_UNITS_LOAD_START]), SD_BUS_VTABLE_PROPERTY_CONST),
         BUS_PROPERTY_DUAL_TIMESTAMP("InitRDUnitsLoadFinishTimestamp", offsetof(Manager, timestamps[MANAGER_TIMESTAMP_INITRD_UNITS_LOAD_FINISH]), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_WRITABLE_PROPERTY("LogLevel", "s", property_get_log_level, property_set_log_level, 0, 0),
-        SD_BUS_WRITABLE_PROPERTY("LogTarget", "s", property_get_log_target, property_set_log_target, 0, 0),
+        SD_BUS_WRITABLE_PROPERTY("LogLevel", "s", bus_property_get_log_level, property_set_log_level, 0, 0),
+        SD_BUS_WRITABLE_PROPERTY("LogTarget", "s", bus_property_get_log_target, property_set_log_target, 0, 0),
         SD_BUS_PROPERTY("NNames", "u", property_get_hashmap_size, offsetof(Manager, units), 0),
         SD_BUS_PROPERTY("NFailedUnits", "u", property_get_set_size, offsetof(Manager, failed_units), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("NJobs", "u", property_get_hashmap_size, offsetof(Manager, jobs), 0),
@@ -2418,11 +2514,11 @@ const sd_bus_vtable bus_manager_vtable[] = {
         SD_BUS_PROPERTY("UnitPath", "as", NULL, offsetof(Manager, lookup_paths.search_path), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("DefaultStandardOutput", "s", bus_property_get_exec_output, offsetof(Manager, default_std_output), SD_BUS_VTABLE_PROPERTY_CONST),
         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("RebootWatchdogUSec", "t", bus_property_get_usec, bus_property_set_usec, offsetof(Manager, reboot_watchdog), 0),
+        SD_BUS_WRITABLE_PROPERTY("RuntimeWatchdogUSec", "t", property_get_runtime_watchdog, property_set_runtime_watchdog, 0, 0),
+        SD_BUS_WRITABLE_PROPERTY("RebootWatchdogUSec", "t", property_get_reboot_watchdog, property_set_reboot_watchdog, 0, 0),
         /* The following item is an obsolete alias */
-        SD_BUS_WRITABLE_PROPERTY("ShutdownWatchdogUSec", "t", bus_property_get_usec, bus_property_set_usec, offsetof(Manager, reboot_watchdog), SD_BUS_VTABLE_HIDDEN),
-        SD_BUS_WRITABLE_PROPERTY("KExecWatchdogUSec", "t", bus_property_get_usec, bus_property_set_usec, offsetof(Manager, kexec_watchdog), 0),
+        SD_BUS_WRITABLE_PROPERTY("ShutdownWatchdogUSec", "t", property_get_reboot_watchdog, property_set_reboot_watchdog, 0, SD_BUS_VTABLE_HIDDEN),
+        SD_BUS_WRITABLE_PROPERTY("KExecWatchdogUSec", "t", property_get_kexec_watchdog, property_set_kexec_watchdog, 0, 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),
@@ -2477,91 +2573,606 @@ const sd_bus_vtable bus_manager_vtable[] = {
         SD_BUS_PROPERTY("TimerSlackNSec", "t", property_get_timer_slack_nsec, 0, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("DefaultOOMPolicy", "s", bus_property_get_oom_policy, offsetof(Manager, default_oom_policy), SD_BUS_VTABLE_PROPERTY_CONST),
 
-        SD_BUS_METHOD("GetUnit", "s", "o", method_get_unit, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("GetUnitByPID", "u", "o", method_get_unit_by_pid, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("GetUnitByInvocationID", "ay", "o", method_get_unit_by_invocation_id, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("GetUnitByControlGroup", "s", "o", method_get_unit_by_control_group, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("LoadUnit", "s", "o", method_load_unit, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("StartUnit", "ss", "o", method_start_unit, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("StartUnitReplace", "sss", "o", method_start_unit_replace, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("StopUnit", "ss", "o", method_stop_unit, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("ReloadUnit", "ss", "o", method_reload_unit, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("RestartUnit", "ss", "o", method_restart_unit, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("TryRestartUnit", "ss", "o", method_try_restart_unit, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("ReloadOrRestartUnit", "ss", "o", method_reload_or_restart_unit, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("ReloadOrTryRestartUnit", "ss", "o", method_reload_or_try_restart_unit, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("EnqueueUnitJob", "sss", "uososa(uosos)", method_enqueue_unit_job, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("KillUnit", "ssi", NULL, method_kill_unit, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("CleanUnit", "sas", NULL, method_clean_unit, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("ResetFailedUnit", "s", NULL, method_reset_failed_unit, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetUnitProperties", "sba(sv)", NULL, method_set_unit_properties, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("RefUnit", "s", NULL, method_ref_unit, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("UnrefUnit", "s", NULL, method_unref_unit, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("StartTransientUnit", "ssa(sv)a(sa(sv))", "o", method_start_transient_unit, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("GetUnitProcesses", "s", "a(sus)", method_get_unit_processes, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("AttachProcessesToUnit", "ssau", NULL, method_attach_processes_to_unit, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("AbandonScope", "s", NULL, method_abandon_scope, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("GetJob", "u", "o", method_get_job, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("GetJobAfter", "u", "a(usssoo)", method_get_job_waiting, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("GetJobBefore", "u", "a(usssoo)", method_get_job_waiting, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("CancelJob", "u", NULL, method_cancel_job, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("ClearJobs", NULL, NULL, method_clear_jobs, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("ResetFailed", NULL, NULL, method_reset_failed, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("ListUnits", NULL, "a(ssssssouso)", method_list_units, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("ListUnitsFiltered", "as", "a(ssssssouso)", method_list_units_filtered, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("ListUnitsByPatterns", "asas", "a(ssssssouso)", method_list_units_by_patterns, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("ListUnitsByNames", "as", "a(ssssssouso)", method_list_units_by_names, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("ListJobs", NULL, "a(usssoo)", method_list_jobs, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("Subscribe", NULL, NULL, method_subscribe, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("Unsubscribe", NULL, NULL, method_unsubscribe, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("Dump", NULL, "s", method_dump, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("DumpByFileDescriptor", NULL, "h", method_dump_by_fd, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("CreateSnapshot", "sb", "o", method_refuse_snapshot, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_HIDDEN),
-        SD_BUS_METHOD("RemoveSnapshot", "s", NULL, method_refuse_snapshot, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_HIDDEN),
-        SD_BUS_METHOD("Reload", NULL, NULL, method_reload, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("Reexecute", NULL, NULL, method_reexecute, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("Exit", NULL, NULL, method_exit, 0),
-        SD_BUS_METHOD("Reboot", NULL, NULL, method_reboot, SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT)),
-        SD_BUS_METHOD("PowerOff", NULL, NULL, method_poweroff, SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT)),
-        SD_BUS_METHOD("Halt", NULL, NULL, method_halt, SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT)),
-        SD_BUS_METHOD("KExec", NULL, NULL, method_kexec, SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT)),
-        SD_BUS_METHOD("SwitchRoot", "ss", NULL, method_switch_root, SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT)),
-        SD_BUS_METHOD("SetEnvironment", "as", NULL, method_set_environment, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("UnsetEnvironment", "as", NULL, method_unset_environment, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("UnsetAndSetEnvironment", "asas", NULL, method_unset_and_set_environment, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("ListUnitFiles", NULL, "a(ss)", method_list_unit_files, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("ListUnitFilesByPatterns", "asas", "a(ss)", method_list_unit_files_by_patterns, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("GetUnitFileState", "s", "s", method_get_unit_file_state, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("EnableUnitFiles", "asbb", "ba(sss)", method_enable_unit_files, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("DisableUnitFiles", "asb", "a(sss)", method_disable_unit_files, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("ReenableUnitFiles", "asbb", "ba(sss)", method_reenable_unit_files, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("LinkUnitFiles", "asbb", "a(sss)", method_link_unit_files, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("PresetUnitFiles", "asbb", "ba(sss)", method_preset_unit_files, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("PresetUnitFilesWithMode", "assbb", "ba(sss)", method_preset_unit_files_with_mode, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("MaskUnitFiles", "asbb", "a(sss)", method_mask_unit_files, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("UnmaskUnitFiles", "asb", "a(sss)", method_unmask_unit_files, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("RevertUnitFiles", "as", "a(sss)", method_revert_unit_files, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetDefaultTarget", "sb", "a(sss)", method_set_default_target, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("GetDefaultTarget", NULL, "s", method_get_default_target, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("PresetAllUnitFiles", "sbb", "a(sss)", method_preset_all_unit_files, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("AddDependencyUnitFiles", "asssbb", "a(sss)", method_add_dependency_unit_files, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("GetUnitFileLinks", "sb", "as", method_get_unit_file_links, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetExitCode", "y", NULL, method_set_exit_code, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("LookupDynamicUserByName", "s", "u", method_lookup_dynamic_user_by_name, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("LookupDynamicUserByUID", "u", "s", method_lookup_dynamic_user_by_uid, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("GetDynamicUsers", NULL, "a(us)", method_get_dynamic_users, SD_BUS_VTABLE_UNPRIVILEGED),
-
-        SD_BUS_SIGNAL("UnitNew", "so", 0),
-        SD_BUS_SIGNAL("UnitRemoved", "so", 0),
-        SD_BUS_SIGNAL("JobNew", "uos", 0),
-        SD_BUS_SIGNAL("JobRemoved", "uoss", 0),
-        SD_BUS_SIGNAL("StartupFinished", "tttttt", 0),
+        SD_BUS_METHOD_WITH_NAMES("GetUnit",
+                                 "s",
+                                 SD_BUS_PARAM(name),
+                                 "o",
+                                 SD_BUS_PARAM(unit),
+                                 method_get_unit,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("GetUnitByPID",
+                                 "u",
+                                 SD_BUS_PARAM(pid),
+                                 "o",
+                                 SD_BUS_PARAM(unit),
+                                 method_get_unit_by_pid,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("GetUnitByInvocationID",
+                                 "ay",
+                                 SD_BUS_PARAM(invocation_id),
+                                 "o",
+                                 SD_BUS_PARAM(unit),
+                                 method_get_unit_by_invocation_id,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("GetUnitByControlGroup",
+                                 "s",
+                                 SD_BUS_PARAM(cgroup),
+                                 "o",
+                                 SD_BUS_PARAM(unit),
+                                 method_get_unit_by_control_group,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("LoadUnit",
+                                 "s",
+                                 SD_BUS_PARAM(name),
+                                 "o",
+                                 SD_BUS_PARAM(unit),
+                                 method_load_unit,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("StartUnit",
+                                 "ss",
+                                 SD_BUS_PARAM(name)
+                                 SD_BUS_PARAM(mode),
+                                 "o",
+                                 SD_BUS_PARAM(job),
+                                 method_start_unit,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("StartUnitReplace",
+                                 "sss",
+                                 SD_BUS_PARAM(old_unit)
+                                 SD_BUS_PARAM(new_unit)
+                                 SD_BUS_PARAM(mode),
+                                 "o",
+                                 SD_BUS_PARAM(job),
+                                 method_start_unit_replace,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("StopUnit",
+                                 "ss",
+                                 SD_BUS_PARAM(name)
+                                 SD_BUS_PARAM(mode),
+                                 "o",
+                                 SD_BUS_PARAM(job),
+                                 method_stop_unit,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("ReloadUnit",
+                                 "ss",
+                                 SD_BUS_PARAM(name)
+                                 SD_BUS_PARAM(mode),
+                                 "o",
+                                 SD_BUS_PARAM(job),
+                                 method_reload_unit,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("RestartUnit",
+                                 "ss",
+                                 SD_BUS_PARAM(name)
+                                 SD_BUS_PARAM(mode),
+                                 "o",
+                                 SD_BUS_PARAM(job),
+                                 method_restart_unit,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("TryRestartUnit",
+                                 "ss",
+                                 SD_BUS_PARAM(name)
+                                 SD_BUS_PARAM(mode),
+                                 "o",
+                                 SD_BUS_PARAM(job),
+                                 method_try_restart_unit,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("ReloadOrRestartUnit",
+                                 "ss",
+                                 SD_BUS_PARAM(name)
+                                 SD_BUS_PARAM(mode),
+                                 "o",
+                                 SD_BUS_PARAM(job),
+                                 method_reload_or_restart_unit,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("ReloadOrTryRestartUnit",
+                                 "ss",
+                                 SD_BUS_PARAM(name)
+                                 SD_BUS_PARAM(mode),
+                                 "o",
+                                 SD_BUS_PARAM(job),
+                                 method_reload_or_try_restart_unit,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("EnqueueUnitJob",
+                                 "sss",
+                                 SD_BUS_PARAM(name)
+                                 SD_BUS_PARAM(job_type)
+                                 SD_BUS_PARAM(job_mode),
+                                 "uososa(uosos)",
+                                 SD_BUS_PARAM(job_id)
+                                 SD_BUS_PARAM(job_path)
+                                 SD_BUS_PARAM(unit_id)
+                                 SD_BUS_PARAM(unit_path)
+                                 SD_BUS_PARAM(job_type)
+                                 SD_BUS_PARAM(affected_jobs),
+                                 method_enqueue_unit_job,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("KillUnit",
+                                 "ssi",
+                                 SD_BUS_PARAM(name)
+                                 SD_BUS_PARAM(whom)
+                                 SD_BUS_PARAM(signal),
+                                 NULL,,
+                                 method_kill_unit,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("CleanUnit",
+                                 "sas",
+                                 SD_BUS_PARAM(name)
+                                 SD_BUS_PARAM(mask),
+                                 NULL,,
+                                 method_clean_unit,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("FreezeUnit",
+                                 "s",
+                                 SD_BUS_PARAM(name),
+                                 NULL,,
+                                 method_freeze_unit,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("ThawUnit",
+                                 "s",
+                                 SD_BUS_PARAM(name),
+                                 NULL,,
+                                 method_thaw_unit,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("ResetFailedUnit",
+                                 "s",
+                                 SD_BUS_PARAM(name),
+                                 NULL,,
+                                 method_reset_failed_unit,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("SetUnitProperties",
+                                 "sba(sv)",
+                                 SD_BUS_PARAM(name)
+                                 SD_BUS_PARAM(runtime)
+                                 SD_BUS_PARAM(properties),
+                                 NULL,,
+                                 method_set_unit_properties,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("RefUnit",
+                                 "s",
+                                 SD_BUS_PARAM(name),
+                                 NULL,,
+                                 method_ref_unit,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("UnrefUnit",
+                                 "s",
+                                 SD_BUS_PARAM(name),
+                                 NULL,,
+                                 method_unref_unit,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("StartTransientUnit",
+                                 "ssa(sv)a(sa(sv))",
+                                 SD_BUS_PARAM(name)
+                                 SD_BUS_PARAM(mode)
+                                 SD_BUS_PARAM(properties)
+                                 SD_BUS_PARAM(aux),
+                                 "o",
+                                 SD_BUS_PARAM(job),
+                                 method_start_transient_unit,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("GetUnitProcesses",
+                                 "s",
+                                 SD_BUS_PARAM(name),
+                                 "a(sus)",
+                                 SD_BUS_PARAM(processes),
+                                 method_get_unit_processes,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("AttachProcessesToUnit",
+                                 "ssau",
+                                 SD_BUS_PARAM(unit_name)
+                                 SD_BUS_PARAM(subcgroup)
+                                 SD_BUS_PARAM(pids),
+                                 NULL,,
+                                 method_attach_processes_to_unit,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("AbandonScope",
+                                 "s",
+                                 SD_BUS_PARAM(name),
+                                 NULL,,
+                                 method_abandon_scope,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("GetJob",
+                                 "u",
+                                 SD_BUS_PARAM(id),
+                                 "o",
+                                 SD_BUS_PARAM(job),
+                                 method_get_job,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("GetJobAfter",
+                                 "u",
+                                 SD_BUS_PARAM(id),
+                                 "a(usssoo)",
+                                 SD_BUS_PARAM(jobs),
+                                 method_get_job_waiting,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("GetJobBefore",
+                                 "u",
+                                 SD_BUS_PARAM(id),
+                                 "a(usssoo)",
+                                 SD_BUS_PARAM(jobs),
+                                 method_get_job_waiting,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("CancelJob",
+                                 "u",
+                                 SD_BUS_PARAM(id),
+                                 NULL,,
+                                 method_cancel_job,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("ClearJobs",
+                      NULL,
+                      NULL,
+                      method_clear_jobs,
+                      SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("ResetFailed",
+                      NULL,
+                      NULL,
+                      method_reset_failed,
+                      SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("SetShowStatus",
+                                 "s",
+                                 SD_BUS_PARAM(mode),
+                                 NULL,,
+                                 method_set_show_status,
+                                 SD_BUS_VTABLE_CAPABILITY(CAP_SYS_ADMIN)),
+        SD_BUS_METHOD_WITH_NAMES("ListUnits",
+                                 NULL,,
+                                 "a(ssssssouso)",
+                                 SD_BUS_PARAM(units),
+                                 method_list_units,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("ListUnitsFiltered",
+                                 "as",
+                                 SD_BUS_PARAM(states),
+                                 "a(ssssssouso)",
+                                 SD_BUS_PARAM(units),
+                                 method_list_units_filtered,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("ListUnitsByPatterns",
+                                 "asas",
+                                 SD_BUS_PARAM(states)
+                                 SD_BUS_PARAM(patterns),
+                                 "a(ssssssouso)",
+                                 SD_BUS_PARAM(units),
+                                 method_list_units_by_patterns,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("ListUnitsByNames",
+                                 "as",
+                                 SD_BUS_PARAM(names),
+                                 "a(ssssssouso)",
+                                 SD_BUS_PARAM(units),
+                                 method_list_units_by_names,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("ListJobs",
+                                 NULL,,
+                                 "a(usssoo)",
+                                 SD_BUS_PARAM(jobs),
+                                 method_list_jobs,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("Subscribe",
+                      NULL,
+                      NULL,
+                      method_subscribe,
+                      SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("Unsubscribe",
+                      NULL,
+                      NULL,
+                      method_unsubscribe,
+                      SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("Dump",
+                                 NULL,,
+                                 "s",
+                                 SD_BUS_PARAM(output),
+                                 method_dump,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("DumpByFileDescriptor",
+                                 NULL,,
+                                 "h",
+                                 SD_BUS_PARAM(fd),
+                                 method_dump_by_fd,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("CreateSnapshot",
+                                 "sb",
+                                 SD_BUS_PARAM(name)
+                                 SD_BUS_PARAM(cleanup),
+                                 "o",
+                                 SD_BUS_PARAM(unit),
+                                 method_refuse_snapshot,
+                                 SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_HIDDEN),
+        SD_BUS_METHOD_WITH_NAMES("RemoveSnapshot",
+                                 "s",
+                                 SD_BUS_PARAM(name),
+                                 NULL,,
+                                 method_refuse_snapshot,
+                                 SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_HIDDEN),
+        SD_BUS_METHOD("Reload",
+                      NULL,
+                      NULL,
+                      method_reload,
+                      SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("Reexecute",
+                      NULL,
+                      NULL,
+                      method_reexecute,
+                      SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("Exit",
+                      NULL,
+                      NULL,
+                      method_exit,
+                      0),
+        SD_BUS_METHOD("Reboot",
+                      NULL,
+                      NULL,
+                      method_reboot,
+                      SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT)),
+        SD_BUS_METHOD("PowerOff",
+                      NULL,
+                      NULL,
+                      method_poweroff,
+                      SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT)),
+        SD_BUS_METHOD("Halt",
+                      NULL,
+                      NULL,
+                      method_halt,
+                      SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT)),
+        SD_BUS_METHOD("KExec",
+                      NULL,
+                      NULL,
+                      method_kexec,
+                      SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT)),
+        SD_BUS_METHOD_WITH_NAMES("SwitchRoot",
+                                 "ss",
+                                 SD_BUS_PARAM(new_root)
+                                 SD_BUS_PARAM(init),
+                                 NULL,,
+                                 method_switch_root,
+                                 SD_BUS_VTABLE_CAPABILITY(CAP_SYS_BOOT)),
+        SD_BUS_METHOD_WITH_NAMES("SetEnvironment",
+                                 "as",
+                                 SD_BUS_PARAM(assignments),
+                                 NULL,,
+                                 method_set_environment,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("UnsetEnvironment",
+                                 "as",
+                                 SD_BUS_PARAM(names),
+                                 NULL,,
+                                 method_unset_environment,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("UnsetAndSetEnvironment",
+                                 "asas",
+                                 SD_BUS_PARAM(names)
+                                 SD_BUS_PARAM(assignments),
+                                 NULL,,
+                                 method_unset_and_set_environment,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("ListUnitFiles",
+                                 NULL,,
+                                 "a(ss)",
+                                 SD_BUS_PARAM(unit_files),
+                                 method_list_unit_files,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("ListUnitFilesByPatterns",
+                                 "asas",
+                                 SD_BUS_PARAM(states)
+                                 SD_BUS_PARAM(patterns),
+                                 "a(ss)",
+                                 SD_BUS_PARAM(unit_files),
+                                 method_list_unit_files_by_patterns,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("GetUnitFileState",
+                                 "s",
+                                 SD_BUS_PARAM(file),
+                                 "s",
+                                 SD_BUS_PARAM(state),
+                                 method_get_unit_file_state,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("EnableUnitFiles",
+                                 "asbb",
+                                 SD_BUS_PARAM(files)
+                                 SD_BUS_PARAM(runtime)
+                                 SD_BUS_PARAM(force),
+                                 "ba(sss)",
+                                 SD_BUS_PARAM(carries_install_info)
+                                 SD_BUS_PARAM(changes),
+                                 method_enable_unit_files,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("DisableUnitFiles",
+                                 "asb",
+                                 SD_BUS_PARAM(files)
+                                 SD_BUS_PARAM(runtime),
+                                 "a(sss)",
+                                 SD_BUS_PARAM(changes),
+                                 method_disable_unit_files,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("ReenableUnitFiles",
+                                 "asbb",
+                                 SD_BUS_PARAM(files)
+                                 SD_BUS_PARAM(runtime)
+                                 SD_BUS_PARAM(force),
+                                 "ba(sss)",
+                                 SD_BUS_PARAM(carries_install_info)
+                                 SD_BUS_PARAM(changes),
+                                 method_reenable_unit_files,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("LinkUnitFiles",
+                                 "asbb",
+                                 SD_BUS_PARAM(files)
+                                 SD_BUS_PARAM(runtime)
+                                 SD_BUS_PARAM(force),
+                                 "a(sss)",
+                                 SD_BUS_PARAM(changes),
+                                 method_link_unit_files,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("PresetUnitFiles",
+                                 "asbb",
+                                 SD_BUS_PARAM(files)
+                                 SD_BUS_PARAM(runtime)
+                                 SD_BUS_PARAM(force),
+                                 "ba(sss)",
+                                 SD_BUS_PARAM(carries_install_info)
+                                 SD_BUS_PARAM(changes),
+                                 method_preset_unit_files,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("PresetUnitFilesWithMode",
+                                 "assbb",
+                                 SD_BUS_PARAM(files)
+                                 SD_BUS_PARAM(mode)
+                                 SD_BUS_PARAM(runtime)
+                                 SD_BUS_PARAM(force),
+                                 "ba(sss)",
+                                 SD_BUS_PARAM(carries_install_info)
+                                 SD_BUS_PARAM(changes),
+                                 method_preset_unit_files_with_mode,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("MaskUnitFiles",
+                                 "asbb",
+                                 SD_BUS_PARAM(files)
+                                 SD_BUS_PARAM(runtime)
+                                 SD_BUS_PARAM(force),
+                                 "a(sss)",
+                                 SD_BUS_PARAM(changes),
+                                 method_mask_unit_files,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("UnmaskUnitFiles",
+                                 "asb",
+                                 SD_BUS_PARAM(files)
+                                 SD_BUS_PARAM(runtime),
+                                 "a(sss)",
+                                 SD_BUS_PARAM(changes),
+                                 method_unmask_unit_files,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("RevertUnitFiles",
+                                 "as",
+                                 SD_BUS_PARAM(files),
+                                 "a(sss)",
+                                 SD_BUS_PARAM(changes),
+                                 method_revert_unit_files,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("SetDefaultTarget",
+                                 "sb",
+                                 SD_BUS_PARAM(name)
+                                 SD_BUS_PARAM(force),
+                                 "a(sss)",
+                                 SD_BUS_PARAM(changes),
+                                 method_set_default_target,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("GetDefaultTarget",
+                                 NULL,,
+                                 "s",
+                                 SD_BUS_PARAM(name),
+                                 method_get_default_target,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("PresetAllUnitFiles",
+                                 "sbb",
+                                 SD_BUS_PARAM(mode)
+                                 SD_BUS_PARAM(runtime)
+                                 SD_BUS_PARAM(force),
+                                 "a(sss)",
+                                 SD_BUS_PARAM(changes),
+                                 method_preset_all_unit_files,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("AddDependencyUnitFiles",
+                                 "asssbb",
+                                 SD_BUS_PARAM(files)
+                                 SD_BUS_PARAM(target)
+                                 SD_BUS_PARAM(type)
+                                 SD_BUS_PARAM(runtime)
+                                 SD_BUS_PARAM(force),
+                                 "a(sss)",
+                                 SD_BUS_PARAM(changes),
+                                 method_add_dependency_unit_files,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("GetUnitFileLinks",
+                                 "sb",
+                                 SD_BUS_PARAM(name)
+                                 SD_BUS_PARAM(runtime),
+                                 "as",
+                                 SD_BUS_PARAM(links),
+                                 method_get_unit_file_links,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("SetExitCode",
+                                 "y",
+                                 SD_BUS_PARAM(number),
+                                 NULL,,
+                                 method_set_exit_code,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("LookupDynamicUserByName",
+                                 "s",
+                                 SD_BUS_PARAM(name),
+                                 "u",
+                                 SD_BUS_PARAM(uid),
+                                 method_lookup_dynamic_user_by_name,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("LookupDynamicUserByUID",
+                                 "u",
+                                 SD_BUS_PARAM(uid),
+                                 "s",
+                                 SD_BUS_PARAM(name),
+                                 method_lookup_dynamic_user_by_uid,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("GetDynamicUsers",
+                                 NULL,,
+                                 "a(us)",
+                                 SD_BUS_PARAM(users),
+                                 method_get_dynamic_users,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+
+        SD_BUS_SIGNAL_WITH_NAMES("UnitNew",
+                                 "so",
+                                 SD_BUS_PARAM(id)
+                                 SD_BUS_PARAM(unit),
+                                 0),
+        SD_BUS_SIGNAL_WITH_NAMES("UnitRemoved",
+                                 "so",
+                                 SD_BUS_PARAM(id)
+                                 SD_BUS_PARAM(unit),
+                                 0),
+        SD_BUS_SIGNAL_WITH_NAMES("JobNew",
+                                 "uos",
+                                 SD_BUS_PARAM(id)
+                                 SD_BUS_PARAM(job)
+                                 SD_BUS_PARAM(unit),
+                                 0),
+        SD_BUS_SIGNAL_WITH_NAMES("JobRemoved",
+                                 "uoss",
+                                 SD_BUS_PARAM(id)
+                                 SD_BUS_PARAM(job)
+                                 SD_BUS_PARAM(unit)
+                                 SD_BUS_PARAM(result),
+                                 0),
+        SD_BUS_SIGNAL_WITH_NAMES("StartupFinished",
+                                 "tttttt",
+                                 SD_BUS_PARAM(firmware)
+                                 SD_BUS_PARAM(loader)
+                                 SD_BUS_PARAM(kernel)
+                                 SD_BUS_PARAM(initrd)
+                                 SD_BUS_PARAM(userspace)
+                                 SD_BUS_PARAM(total),
+                                 0),
         SD_BUS_SIGNAL("UnitFilesChanged", NULL, 0),
-        SD_BUS_SIGNAL("Reloading", "b", 0),
+        SD_BUS_SIGNAL_WITH_NAMES("Reloading",
+                                 "b",
+                                 SD_BUS_PARAM(active),
+                                 0),
 
         SD_BUS_VTABLE_END
 };
 
+const sd_bus_vtable bus_manager_log_control_vtable[] = {
+        SD_BUS_VTABLE_START(0),
+
+        /* We define a private version of this interface here, since we want slightly different
+         * implementations for the setters. We'll still use the generic getters however, and we share the
+         * setters with the implementations for the Manager interface above (which pre-dates the generic
+         * service API interface). */
+
+        SD_BUS_WRITABLE_PROPERTY("LogLevel", "s", bus_property_get_log_level, property_set_log_level, 0, 0),
+        SD_BUS_WRITABLE_PROPERTY("LogTarget", "s", bus_property_get_log_target, property_set_log_target, 0, 0),
+        SD_BUS_PROPERTY("SyslogIdentifier", "s", bus_property_get_syslog_identifier, 0, 0),
+
+        SD_BUS_VTABLE_END,
+};
+
 static int send_finished(sd_bus *bus, void *userdata) {
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *message = NULL;
         usec_t *times = userdata;
index 10aa2eccee2329010401eb79388793d47377dcc7..83854b0f59be8f019ec2e08effe6b299fa67d9ea 100644 (file)
@@ -6,6 +6,7 @@
 #include "manager.h"
 
 extern const sd_bus_vtable bus_manager_vtable[];
+extern const sd_bus_vtable bus_manager_log_control_vtable[];
 
 void bus_manager_send_finished(Manager *m, usec_t firmware_usec, usec_t loader_usec, usec_t kernel_usec, usec_t initrd_usec, usec_t userspace_usec, usec_t total_usec);
 void bus_manager_send_reloading(Manager *m, bool active);
index b6d61627ebb13b446c269856d42db10aa5acc5c3..bab12cc4ff15fa12242fc144501eab506dd5753c 100644 (file)
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
-#include "bus-util.h"
+#include "bus-get-properties.h"
 #include "dbus-cgroup.h"
 #include "dbus-execute.h"
 #include "dbus-kill.h"
@@ -51,6 +51,7 @@ const sd_bus_vtable bus_mount_vtable[] = {
         SD_BUS_PROPERTY("SloppyOptions", "b", bus_property_get_bool, offsetof(Mount, sloppy_options), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("LazyUnmount", "b", bus_property_get_bool, offsetof(Mount, lazy_unmount), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("ForceUnmount", "b", bus_property_get_bool, offsetof(Mount, force_unmount), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("ReadWriteOnly", "b", bus_property_get_bool, offsetof(Mount, read_write_only), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Mount, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("UID", "u", bus_property_get_uid, offsetof(Unit, ref_uid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("GID", "u", bus_property_get_gid, offsetof(Unit, ref_gid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
@@ -102,6 +103,9 @@ static int bus_mount_set_transient_property(
         if (streq(name, "ForceUnmount"))
                 return bus_set_transient_bool(u, name, &m->force_unmount, message, flags, error);
 
+        if (streq(name, "ReadWriteOnly"))
+                return bus_set_transient_bool(u, name, &m->read_write_only, message, flags, error);
+
         return 0;
 }
 
index 1a97d62486f45f538202a41b3e883a67c5ee4c14..76cd9d32603d4f86db9e61e3d0e454e69078d882 100644 (file)
@@ -1,7 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
 #include "alloc-util.h"
-#include "bus-util.h"
+#include "bus-get-properties.h"
 #include "dbus-path.h"
 #include "dbus-util.h"
 #include "list.h"
index 84d91dcfa3538c14692c0f3de3ccfe4e4534f12e..aecfda65356caa2e5f370b308371270bd3d4b156 100644 (file)
@@ -2,8 +2,7 @@
 
 #include "alloc-util.h"
 #include "bus-common-errors.h"
-#include "bus-internal.h"
-#include "bus-util.h"
+#include "bus-get-properties.h"
 #include "dbus-cgroup.h"
 #include "dbus-kill.h"
 #include "dbus-scope.h"
@@ -140,7 +139,7 @@ static int bus_scope_set_transient_property(
                 if (r < 0)
                         return r;
 
-                if (!isempty(controller) && !service_name_is_valid(controller))
+                if (!isempty(controller) && !sd_bus_service_name_is_valid(controller))
                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Controller '%s' is not a valid bus name.", controller);
 
                 if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
index 5cf9b21890e46d3866a418e87ae74847e9497313..3cc453dff5bbc7bb5a69a1b0742b682f95277e56 100644 (file)
@@ -4,8 +4,7 @@
 
 #include "alloc-util.h"
 #include "async.h"
-#include "bus-internal.h"
-#include "bus-util.h"
+#include "bus-get-properties.h"
 #include "dbus-cgroup.h"
 #include "dbus-execute.h"
 #include "dbus-kill.h"
@@ -29,6 +28,8 @@ 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 BUS_DEFINE_PROPERTY_GET(property_get_timeout_abort_usec, "t", Service, service_timeout_abort_usec);
+static BUS_DEFINE_PROPERTY_GET(property_get_watchdog_usec, "t", Service, service_get_watchdog_usec);
+static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_timeout_failure_mode, service_timeout_failure_mode, ServiceTimeoutFailureMode);
 
 static int property_get_exit_status_set(
                 sd_bus *bus,
@@ -101,8 +102,10 @@ const sd_bus_vtable bus_service_vtable[] = {
         SD_BUS_PROPERTY("TimeoutStartUSec", "t", bus_property_get_usec, offsetof(Service, timeout_start_usec), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("TimeoutStopUSec", "t", bus_property_get_usec, offsetof(Service, timeout_stop_usec), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("TimeoutAbortUSec", "t", property_get_timeout_abort_usec, 0, 0),
+        SD_BUS_PROPERTY("TimeoutStartFailureMode", "s", property_get_timeout_failure_mode, offsetof(Service, timeout_start_failure_mode), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("TimeoutStopFailureMode", "s", property_get_timeout_failure_mode, offsetof(Service, timeout_stop_failure_mode), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("RuntimeMaxUSec", "t", bus_property_get_usec, offsetof(Service, runtime_max_usec), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("WatchdogUSec", "t", bus_property_get_usec, offsetof(Service, watchdog_usec), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("WatchdogUSec", "t", property_get_watchdog_usec, 0, 0),
         BUS_PROPERTY_DUAL_TIMESTAMP("WatchdogTimestamp", offsetof(Service, watchdog_timestamp), 0),
         SD_BUS_PROPERTY("PermissionsStartOnly", "b", bus_property_get_bool, offsetof(Service, permissions_start_only), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), /* 😷 deprecated */
         SD_BUS_PROPERTY("RootDirectoryStartOnly", "b", bus_property_get_bool, offsetof(Service, root_directory_start_only), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -258,7 +261,8 @@ static BUS_DEFINE_SET_TRANSIENT_PARSE(notify_access, NotifyAccess, notify_access
 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_PARSE(oom_policy, OOMPolicy, oom_policy_from_string);
-static BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(bus_name, service_name_is_valid);
+static BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(bus_name, sd_bus_service_name_is_valid);
+static BUS_DEFINE_SET_TRANSIENT_PARSE(timeout_failure_mode, ServiceTimeoutFailureMode, service_timeout_failure_mode_from_string);
 
 static int bus_service_set_transient_property(
                 Service *s,
@@ -316,6 +320,12 @@ static int bus_service_set_transient_property(
                 return r;
         }
 
+        if (streq(name, "TimeoutStartFailureMode"))
+                return bus_set_transient_timeout_failure_mode(u, name, &s->timeout_start_failure_mode, message, flags, error);
+
+        if (streq(name, "TimeoutStopFailureMode"))
+                return bus_set_transient_timeout_failure_mode(u, name, &s->timeout_stop_failure_mode, message, flags, error);
+
         if (streq(name, "RuntimeMaxUSec"))
                 return bus_set_transient_usec(u, name, &s->runtime_max_usec, message, flags, error);
 
index ad7b41a95b25ab2a25a1a06ab33fa732e32ff75a..f01489e29a16258610c5420906b5e13961ac3ddf 100644 (file)
@@ -1,7 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
 #include "alloc-util.h"
-#include "bus-util.h"
+#include "bus-get-properties.h"
 #include "dbus-cgroup.h"
 #include "dbus-execute.h"
 #include "dbus-kill.h"
@@ -104,6 +104,7 @@ const sd_bus_vtable bus_socket_vtable[] = {
         SD_BUS_PROPERTY("Broadcast", "b", bus_property_get_bool, offsetof(Socket, broadcast), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("PassCredentials", "b", bus_property_get_bool, offsetof(Socket, pass_cred), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("PassSecurity", "b", bus_property_get_bool, offsetof(Socket, pass_sec), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("PassPacketInfo", "b", bus_property_get_bool, offsetof(Socket, pass_pktinfo), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("RemoveOnStop", "b", bus_property_get_bool, offsetof(Socket, remove_on_stop), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("Listen", "a(ss)", property_get_listen, 0, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("Symlinks", "as", NULL, offsetof(Socket, symlinks), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -202,6 +203,9 @@ static int bus_socket_set_transient_property(
         if (streq(name, "PassSecurity"))
                 return bus_set_transient_bool(u, name, &s->pass_sec, message, flags, error);
 
+        if (streq(name, "PassPacketInfo"))
+                return bus_set_transient_bool(u, name, &s->pass_pktinfo, message, flags, error);
+
         if (streq(name, "ReusePort"))
                 return bus_set_transient_bool(u, name, &s->reuse_port, message, flags, error);
 
index 57c8c42091fd95750df6684acf8c3e473d3f89c3..cb4824b6bd943aa77f77a8655d23f3f2a3008e8d 100644 (file)
@@ -3,7 +3,7 @@
   Copyright © 2010 Maarten Lankhorst
 ***/
 
-#include "bus-util.h"
+#include "bus-get-properties.h"
 #include "dbus-cgroup.h"
 #include "dbus-execute.h"
 #include "dbus-swap.h"
index 439c276fac90a31bb0132ff5a51330579e3abdf7..da35fa867862ea64d5fc65e61fbda42aa3e8800a 100644 (file)
@@ -1,7 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
 #include "alloc-util.h"
-#include "bus-util.h"
+#include "bus-get-properties.h"
 #include "dbus-timer.h"
 #include "dbus-util.h"
 #include "strv.h"
index 496fd5eb6a5da33fcf802321c23d13af42bbfd1b..9e9d3b101e50df456991068d4e4ef12db71d29f1 100644 (file)
@@ -5,8 +5,8 @@
 #include "alloc-util.h"
 #include "bpf-firewall.h"
 #include "bus-common-errors.h"
+#include "bus-get-properties.h"
 #include "bus-polkit.h"
-#include "bus-util.h"
 #include "cgroup-util.h"
 #include "condition.h"
 #include "dbus-job.h"
@@ -46,12 +46,14 @@ static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_job_mode, job_mode, JobMode);
 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_emergency_action, emergency_action, EmergencyAction);
 static BUS_DEFINE_PROPERTY_GET(property_get_description, "s", Unit, unit_description);
 static BUS_DEFINE_PROPERTY_GET2(property_get_active_state, "s", Unit, unit_active_state, unit_active_state_to_string);
+static BUS_DEFINE_PROPERTY_GET2(property_get_freezer_state, "s", Unit, unit_freezer_state, freezer_state_to_string);
 static BUS_DEFINE_PROPERTY_GET(property_get_sub_state, "s", Unit, unit_sub_state_to_string);
 static BUS_DEFINE_PROPERTY_GET2(property_get_unit_file_state, "s", Unit, unit_get_unit_file_state, unit_file_state_to_string);
 static BUS_DEFINE_PROPERTY_GET(property_get_can_reload, "b", Unit, unit_can_reload);
 static BUS_DEFINE_PROPERTY_GET(property_get_can_start, "b", Unit, unit_can_start_refuse_manual);
 static BUS_DEFINE_PROPERTY_GET(property_get_can_stop, "b", Unit, unit_can_stop_refuse_manual);
 static BUS_DEFINE_PROPERTY_GET(property_get_can_isolate, "b", Unit, unit_can_isolate_refuse_manual);
+static BUS_DEFINE_PROPERTY_GET(property_get_can_freeze, "b", Unit, unit_can_freeze);
 static BUS_DEFINE_PROPERTY_GET(property_get_need_daemon_reload, "b", Unit, unit_need_daemon_reload);
 static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_empty_strv, "as", 0);
 
@@ -100,20 +102,24 @@ static int property_get_names(
                 void *userdata,
                 sd_bus_error *error) {
 
-        Set **s = userdata;
+        Unit *u = userdata;
         Iterator i;
         const char *t;
         int r;
 
         assert(bus);
         assert(reply);
-        assert(s);
+        assert(u);
 
         r = sd_bus_message_open_container(reply, 'a', "s");
         if (r < 0)
                 return r;
 
-        SET_FOREACH(t, *s, i) {
+        r = sd_bus_message_append(reply, "s", u->id);
+        if (r < 0)
+                return r;
+
+        SET_FOREACH(t, u->aliases, i) {
                 r = sd_bus_message_append(reply, "s", t);
                 if (r < 0)
                         return r;
@@ -724,6 +730,79 @@ int bus_unit_method_clean(sd_bus_message *message, void *userdata, sd_bus_error
         return sd_bus_reply_method_return(message, NULL);
 }
 
+static int bus_unit_method_freezer_generic(sd_bus_message *message, void *userdata, sd_bus_error *error, FreezerAction action) {
+        const char* perm;
+        int (*method)(Unit*);
+        Unit *u = userdata;
+        bool reply_no_delay = false;
+        int r;
+
+        assert(message);
+        assert(u);
+        assert(IN_SET(action, FREEZER_FREEZE, FREEZER_THAW));
+
+        if (action == FREEZER_FREEZE) {
+                perm = "stop";
+                method = unit_freeze;
+        } else {
+                perm = "start";
+                method = unit_thaw;
+        }
+
+        r = mac_selinux_unit_access_check(u, message, perm, error);
+        if (r < 0)
+                return r;
+
+        r = bus_verify_manage_units_async_full(
+                        u,
+                        perm,
+                        CAP_SYS_ADMIN,
+                        N_("Authentication is required to freeze or thaw the processes of '$(unit)' unit."),
+                        true,
+                        message,
+                        error);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
+
+        r = method(u);
+        if (r == -EOPNOTSUPP)
+                return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Unit '%s' does not support freezing.", u->id);
+        if (r == -EBUSY)
+                return sd_bus_error_setf(error, BUS_ERROR_UNIT_BUSY, "Unit has a pending job.");
+        if (r == -EHOSTDOWN)
+                return sd_bus_error_setf(error, BUS_ERROR_UNIT_INACTIVE, "Unit is inactive.");
+        if (r == -EALREADY)
+                return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Previously requested freezer operation for unit '%s' is still in progress.", u->id);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                reply_no_delay = true;
+
+        assert(!u->pending_freezer_message);
+
+        r = sd_bus_message_new_method_return(message, &u->pending_freezer_message);
+        if (r < 0)
+                return r;
+
+        if (reply_no_delay) {
+                r = bus_unit_send_pending_freezer_message(u);
+                if (r < 0)
+                        return r;
+        }
+
+        return 1;
+}
+
+int bus_unit_method_thaw(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        return bus_unit_method_freezer_generic(message, userdata, error, FREEZER_THAW);
+}
+
+int bus_unit_method_freeze(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        return bus_unit_method_freezer_generic(message, userdata, error, FREEZER_FREEZE);
+}
+
 static int property_get_refs(
                 sd_bus *bus,
                 const char *path,
@@ -766,7 +845,7 @@ const sd_bus_vtable bus_unit_vtable[] = {
         SD_BUS_VTABLE_START(0),
 
         SD_BUS_PROPERTY("Id", "s", NULL, offsetof(Unit, id), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("Names", "as", property_get_names, offsetof(Unit, names), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("Names", "as", property_get_names, 0, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("Following", "s", property_get_following, 0, 0),
         SD_BUS_PROPERTY("Requires", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUIRES]), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("Requisite", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUISITE]), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -793,6 +872,7 @@ const sd_bus_vtable bus_unit_vtable[] = {
         SD_BUS_PROPERTY("Description", "s", property_get_description, 0, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("LoadState", "s", property_get_load_state, offsetof(Unit, load_state), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("ActiveState", "s", property_get_active_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("FreezerState", "s", property_get_freezer_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("SubState", "s", property_get_sub_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("FragmentPath", "s", NULL, offsetof(Unit, fragment_path), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("SourcePath", "s", NULL, offsetof(Unit, source_path), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -809,6 +889,7 @@ const sd_bus_vtable bus_unit_vtable[] = {
         SD_BUS_PROPERTY("CanReload", "b", property_get_can_reload, 0, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("CanIsolate", "b", property_get_can_isolate, 0, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("CanClean", "as", property_get_can_clean, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("CanFreeze", "b", property_get_can_freeze, 0, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("Job", "(uo)", property_get_job, offsetof(Unit, job), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("StopWhenUnneeded", "b", bus_property_get_bool, offsetof(Unit, stop_when_unneeded), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("RefuseManualStart", "b", bus_property_get_bool, offsetof(Unit, refuse_manual_start), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -843,20 +924,113 @@ const sd_bus_vtable bus_unit_vtable[] = {
         SD_BUS_PROPERTY("CollectMode", "s", property_get_collect_mode, offsetof(Unit, collect_mode), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("Refs", "as", property_get_refs, 0, 0),
 
-        SD_BUS_METHOD("Start", "s", "o", method_start, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("Stop", "s", "o", method_stop, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("Reload", "s", "o", method_reload, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("Restart", "s", "o", method_restart, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("TryRestart", "s", "o", method_try_restart, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("ReloadOrRestart", "s", "o", method_reload_or_restart, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("ReloadOrTryRestart", "s", "o", method_reload_or_try_restart, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("EnqueueJob", "ss", "uososa(uosos)", bus_unit_method_enqueue_job, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("Kill", "si", NULL, bus_unit_method_kill, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("ResetFailed", NULL, NULL, bus_unit_method_reset_failed, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetProperties", "ba(sv)", NULL, bus_unit_method_set_properties, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("Ref", NULL, NULL, bus_unit_method_ref, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("Unref", NULL, NULL, bus_unit_method_unref, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("Clean", "as", NULL, bus_unit_method_clean, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("Start",
+                                 "s",
+                                 SD_BUS_PARAM(mode),
+                                 "o",
+                                 SD_BUS_PARAM(job),
+                                 method_start,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("Stop",
+                                 "s",
+                                 SD_BUS_PARAM(mode),
+                                 "o",
+                                 SD_BUS_PARAM(job),
+                                 method_stop,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("Reload",
+                                 "s",
+                                 SD_BUS_PARAM(mode),
+                                 "o",
+                                 SD_BUS_PARAM(job),
+                                 method_reload,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("Restart",
+                                 "s",
+                                 SD_BUS_PARAM(mode),
+                                 "o",
+                                 SD_BUS_PARAM(job),
+                                 method_restart,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("TryRestart",
+                                 "s",
+                                 SD_BUS_PARAM(mode),
+                                 "o",
+                                 SD_BUS_PARAM(job),
+                                 method_try_restart,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("ReloadOrRestart",
+                                 "s",
+                                 SD_BUS_PARAM(mode),
+                                 "o",
+                                 SD_BUS_PARAM(job),
+                                 method_reload_or_restart,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("ReloadOrTryRestart",
+                                 "s",
+                                 SD_BUS_PARAM(mode),
+                                 "o",
+                                 SD_BUS_PARAM(job),
+                                 method_reload_or_try_restart,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("EnqueueJob",
+                                 "ss",
+                                 SD_BUS_PARAM(job_type)
+                                 SD_BUS_PARAM(job_mode),
+                                 "uososa(uosos)",
+                                 SD_BUS_PARAM(job_id)
+                                 SD_BUS_PARAM(job_path)
+                                 SD_BUS_PARAM(unit_id)
+                                 SD_BUS_PARAM(unit_path)
+                                 SD_BUS_PARAM(job_type)
+                                 SD_BUS_PARAM(affected_jobs),
+                                 bus_unit_method_enqueue_job,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("Kill",
+                                 "si",
+                                 SD_BUS_PARAM(whom)
+                                 SD_BUS_PARAM(signal),
+                                 NULL,,
+                                 bus_unit_method_kill,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("ResetFailed",
+                      NULL,
+                      NULL,
+                      bus_unit_method_reset_failed,
+                      SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("SetProperties",
+                                 "ba(sv)",
+                                 SD_BUS_PARAM(runtime)
+                                 SD_BUS_PARAM(properties),
+                                 NULL,,
+                                 bus_unit_method_set_properties,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("Ref",
+                      NULL,
+                      NULL,
+                      bus_unit_method_ref,
+                      SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("Unref",
+                      NULL,
+                      NULL,
+                      bus_unit_method_unref,
+                      SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("Clean",
+                                 "as",
+                                 SD_BUS_PARAM(mask),
+                                 NULL,,
+                                 bus_unit_method_clean,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("Freeze",
+                      NULL,
+                      NULL,
+                      bus_unit_method_freeze,
+                      SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("Thaw",
+                      NULL,
+                      NULL,
+                      bus_unit_method_thaw,
+                      SD_BUS_VTABLE_UNPRIVILEGED),
 
         /* For dependency types we don't support anymore always return an empty array */
         SD_BUS_PROPERTY("RequiresOverridable", "as", property_get_empty_strv, 0, SD_BUS_VTABLE_HIDDEN),
@@ -866,6 +1040,7 @@ const sd_bus_vtable bus_unit_vtable[] = {
         /* Obsolete alias names */
         SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Unit, start_ratelimit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
         SD_BUS_PROPERTY("StartLimitIntervalSec", "t", bus_property_get_usec, offsetof(Unit, start_ratelimit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
+
         SD_BUS_VTABLE_END
 };
 
@@ -1365,8 +1540,22 @@ const sd_bus_vtable bus_unit_cgroup_vtable[] = {
         SD_BUS_PROPERTY("IOReadOperations", "t", property_get_io_counter, 0, 0),
         SD_BUS_PROPERTY("IOWriteBytes", "t", property_get_io_counter, 0, 0),
         SD_BUS_PROPERTY("IOWriteOperations", "t", property_get_io_counter, 0, 0),
-        SD_BUS_METHOD("GetProcesses", NULL, "a(sus)", bus_unit_method_get_processes, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("AttachProcesses", "sau", NULL, bus_unit_method_attach_processes, SD_BUS_VTABLE_UNPRIVILEGED),
+
+        SD_BUS_METHOD_WITH_NAMES("GetProcesses",
+                                 NULL,,
+                                 "a(sus)",
+                                 SD_BUS_PARAM(processes),
+                                 bus_unit_method_get_processes,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+
+        SD_BUS_METHOD_WITH_NAMES("AttachProcesses",
+                                 "sau",
+                                 SD_BUS_PARAM(subcgroup)
+                                 SD_BUS_PARAM(pids),
+                                 NULL,,
+                                 bus_unit_method_attach_processes,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+
         SD_BUS_VTABLE_END
 };
 
@@ -1468,6 +1657,23 @@ void bus_unit_send_pending_change_signal(Unit *u, bool including_new) {
         bus_unit_send_change_signal(u);
 }
 
+int bus_unit_send_pending_freezer_message(Unit *u) {
+        int r;
+
+        assert(u);
+
+        if (!u->pending_freezer_message)
+                return 0;
+
+        r = sd_bus_send(NULL, u->pending_freezer_message, NULL);
+        if (r < 0)
+                log_warning_errno(r, "Failed to send queued message, ignoring: %m");
+
+        u->pending_freezer_message = sd_bus_message_unref(u->pending_freezer_message);
+
+        return 0;
+}
+
 static int send_removed_signal(sd_bus *bus, void *userdata) {
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
         _cleanup_free_ char *p = NULL;
@@ -1522,7 +1728,7 @@ int bus_unit_queue_job(
 
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
         _cleanup_free_ char *job_path = NULL, *unit_path = NULL;
-        _cleanup_(set_freep) Set *affected = NULL;
+        _cleanup_set_free_ Set *affected = NULL;
         Iterator i;
         Job *j, *a;
         int r;
index 91711311a7836b9f7aae865dd7a798a41b3c0569..f21f23602563aa96e0506786a8d33c9ab7e48d0b 100644 (file)
@@ -2,7 +2,6 @@
 #pragma once
 
 #include "sd-bus.h"
-#include "sd-bus-vtable.h"
 
 #include "unit.h"
 
@@ -11,6 +10,7 @@ extern const sd_bus_vtable bus_unit_cgroup_vtable[];
 
 void bus_unit_send_change_signal(Unit *u);
 void bus_unit_send_pending_change_signal(Unit *u, bool including_new);
+int bus_unit_send_pending_freezer_message(Unit *u);
 void bus_unit_send_removed_signal(Unit *u);
 
 int bus_unit_method_start_generic(sd_bus_message *message, Unit *u, JobType job_type, bool reload_if_possible, sd_bus_error *error);
@@ -25,6 +25,8 @@ int bus_unit_method_attach_processes(sd_bus_message *message, void *userdata, sd
 int bus_unit_method_ref(sd_bus_message *message, void *userdata, sd_bus_error *error);
 int bus_unit_method_unref(sd_bus_message *message, void *userdata, sd_bus_error *error);
 int bus_unit_method_clean(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_unit_method_freeze(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_unit_method_thaw(sd_bus_message *message, void *userdata, sd_bus_error *error);
 
 typedef enum BusUnitQueueFlags {
         BUS_UNIT_QUEUE_RELOAD_IF_POSSIBLE = 1 << 0,
index 50155f22c6725a9543e463c3bf7182dbb4982930..76bb91d0ea1646c23258dc493c6679e06f720e57 100644 (file)
@@ -274,25 +274,6 @@ static int mac_selinux_filter(sd_bus_message *message, void *userdata, sd_bus_er
 }
 #endif
 
-static int bus_job_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
-        Manager *m = userdata;
-        Job *j;
-        int r;
-
-        assert(bus);
-        assert(path);
-        assert(interface);
-        assert(found);
-        assert(m);
-
-        r = manager_get_job_from_dbus_path(m, path, &j);
-        if (r < 0)
-                return 0;
-
-        *found = j;
-        return 1;
-}
-
 static int find_unit(Manager *m, sd_bus *bus, const char *path, Unit **unit, sd_bus_error *error) {
         Unit *u = NULL;  /* just to appease gcc, initialization is not really necessary */
         int r;
@@ -472,32 +453,6 @@ static int bus_kill_context_find(sd_bus *bus, const char *path, const char *inte
         return 1;
 }
 
-static int bus_job_enumerate(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
-        _cleanup_strv_free_ char **l = NULL;
-        Manager *m = userdata;
-        unsigned k = 0;
-        Iterator i;
-        Job *j;
-
-        l = new0(char*, hashmap_size(m->jobs)+1);
-        if (!l)
-                return -ENOMEM;
-
-        HASHMAP_FOREACH(j, m->jobs, i) {
-                l[k] = job_dbus_path(j);
-                if (!l[k])
-                        return -ENOMEM;
-
-                k++;
-        }
-
-        assert(hashmap_size(m->jobs) == k);
-
-        *nodes = TAKE_PTR(l);
-
-        return k;
-}
-
 static int bus_unit_enumerate(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
         _cleanup_strv_free_ char **l = NULL;
         Manager *m = userdata;
@@ -522,8 +477,147 @@ static int bus_unit_enumerate(sd_bus *bus, const char *path, void *userdata, cha
         return k;
 }
 
+static const BusObjectImplementation unit_object = {
+        "/org/freedesktop/systemd1/unit",
+        "org.freedesktop.systemd1.Unit",
+        .fallback_vtables = BUS_FALLBACK_VTABLES(
+                { bus_unit_vtable,        bus_unit_find }),
+        .node_enumerator = bus_unit_enumerate,
+};
+
+static const BusObjectImplementation bus_automount_object = {
+        "/org/freedesktop/systemd1/unit",
+        "org.freedesktop.systemd1.Automount",
+        .fallback_vtables = BUS_FALLBACK_VTABLES(
+                { bus_automount_vtable,   bus_unit_interface_find }),
+};
+
+static const BusObjectImplementation bus_device_object = {
+        "/org/freedesktop/systemd1/unit",
+        "org.freedesktop.systemd1.Device",
+        .fallback_vtables = BUS_FALLBACK_VTABLES(
+                { bus_device_vtable,      bus_unit_interface_find }),
+};
+
+static const BusObjectImplementation bus_mount_object = {
+        "/org/freedesktop/systemd1/unit",
+        "org.freedesktop.systemd1.Mount",
+        .fallback_vtables = BUS_FALLBACK_VTABLES(
+                { bus_mount_vtable,       bus_unit_interface_find },
+                { bus_unit_cgroup_vtable, bus_unit_cgroup_find },
+                { bus_cgroup_vtable,      bus_cgroup_context_find },
+                { bus_exec_vtable,        bus_exec_context_find },
+                { bus_kill_vtable,        bus_kill_context_find }),
+};
+
+static const BusObjectImplementation bus_path_object = {
+        "/org/freedesktop/systemd1/unit",
+        "org.freedesktop.systemd1.Path",
+        .fallback_vtables = BUS_FALLBACK_VTABLES(
+                { bus_path_vtable,        bus_unit_interface_find }),
+};
+
+static const BusObjectImplementation bus_scope_object = {
+        "/org/freedesktop/systemd1/unit",
+        "org.freedesktop.systemd1.Scope",
+        .fallback_vtables = BUS_FALLBACK_VTABLES(
+                { bus_scope_vtable,       bus_unit_interface_find },
+                { bus_unit_cgroup_vtable, bus_unit_cgroup_find },
+                { bus_cgroup_vtable,      bus_cgroup_context_find },
+                { bus_kill_vtable,        bus_kill_context_find }),
+};
+
+static const BusObjectImplementation bus_service_object = {
+        "/org/freedesktop/systemd1/unit",
+        "org.freedesktop.systemd1.Service",
+        .fallback_vtables = BUS_FALLBACK_VTABLES(
+                { bus_service_vtable,     bus_unit_interface_find },
+                { bus_unit_cgroup_vtable, bus_unit_cgroup_find },
+                { bus_cgroup_vtable,      bus_cgroup_context_find },
+                { bus_exec_vtable,        bus_exec_context_find },
+                { bus_kill_vtable,        bus_kill_context_find }),
+};
+
+static const BusObjectImplementation bus_slice_object = {
+        "/org/freedesktop/systemd1/unit",
+        "org.freedesktop.systemd1.Slice",
+        .fallback_vtables = BUS_FALLBACK_VTABLES(
+                { bus_slice_vtable,       bus_unit_interface_find },
+                { bus_unit_cgroup_vtable, bus_unit_cgroup_find },
+                { bus_cgroup_vtable,      bus_cgroup_context_find }),
+};
+
+static const BusObjectImplementation bus_socket_object = {
+        "/org/freedesktop/systemd1/unit",
+        "org.freedesktop.systemd1.Socket",
+        .fallback_vtables = BUS_FALLBACK_VTABLES(
+                { bus_socket_vtable,      bus_unit_interface_find },
+                { bus_unit_cgroup_vtable, bus_unit_cgroup_find },
+                { bus_cgroup_vtable,      bus_cgroup_context_find },
+                { bus_exec_vtable,        bus_exec_context_find },
+                { bus_kill_vtable,        bus_kill_context_find }),
+};
+
+static const BusObjectImplementation bus_swap_object = {
+        "/org/freedesktop/systemd1/unit",
+        "org.freedesktop.systemd1.Swap",
+        .fallback_vtables = BUS_FALLBACK_VTABLES(
+                { bus_swap_vtable,        bus_unit_interface_find },
+                { bus_unit_cgroup_vtable, bus_unit_cgroup_find },
+                { bus_cgroup_vtable,      bus_cgroup_context_find },
+                { bus_exec_vtable,        bus_exec_context_find },
+                { bus_kill_vtable,        bus_kill_context_find }),
+};
+
+static const BusObjectImplementation bus_target_object = {
+        "/org/freedesktop/systemd1/unit",
+        "org.freedesktop.systemd1.Target",
+        .fallback_vtables = BUS_FALLBACK_VTABLES(
+                { bus_target_vtable,      bus_unit_interface_find }),
+};
+
+static const BusObjectImplementation bus_timer_object = {
+        "/org/freedesktop/systemd1/unit",
+        "org.freedesktop.systemd1.Timer",
+        .fallback_vtables = BUS_FALLBACK_VTABLES(
+                { bus_timer_vtable,       bus_unit_interface_find }),
+};
+
+static const BusObjectImplementation bus_manager_object = {
+        "/org/freedesktop/systemd1",
+        "org.freedesktop.systemd1.Manager",
+        .vtables = BUS_VTABLES(bus_manager_vtable),
+        .children = BUS_IMPLEMENTATIONS(
+                        &job_object,
+                        &unit_object,
+                        &bus_automount_object,
+                        &bus_device_object,
+                        &bus_mount_object,
+                        &bus_path_object,
+                        &bus_scope_object,
+                        &bus_service_object,
+                        &bus_slice_object,
+                        &bus_socket_object,
+                        &bus_swap_object,
+                        &bus_target_object,
+                        &bus_timer_object),
+};
+
+static const BusObjectImplementation manager_log_control_object = {
+        "/org/freedesktop/LogControl1",
+        "org.freedesktop.LogControl1",
+        .vtables = BUS_VTABLES(bus_manager_log_control_vtable),
+};
+
+int bus_manager_introspect_implementations(FILE *out, const char *pattern) {
+        return bus_introspect_implementations(
+                        out,
+                        pattern,
+                        BUS_IMPLEMENTATIONS(&bus_manager_object,
+                                            &manager_log_control_object));
+}
+
 static int bus_setup_api_vtables(Manager *m, sd_bus *bus) {
-        UnitType t;
         int r;
 
         assert(m);
@@ -535,59 +629,11 @@ static int bus_setup_api_vtables(Manager *m, sd_bus *bus) {
                 return log_error_errno(r, "Failed to add SELinux access filter: %m");
 #endif
 
-        r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", bus_manager_vtable, m);
-        if (r < 0)
-                return log_error_errno(r, "Failed to register Manager vtable: %m");
-
-        r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/job", "org.freedesktop.systemd1.Job", bus_job_vtable, bus_job_find, m);
-        if (r < 0)
-                return log_error_errno(r, "Failed to register Job vtable: %m");
-
-        r = sd_bus_add_node_enumerator(bus, NULL, "/org/freedesktop/systemd1/job", bus_job_enumerate, m);
-        if (r < 0)
-                return log_error_errno(r, "Failed to add job enumerator: %m");
-
-        r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", "org.freedesktop.systemd1.Unit", bus_unit_vtable, bus_unit_find, m);
-        if (r < 0)
-                return log_error_errno(r, "Failed to register Unit vtable: %m");
-
-        r = sd_bus_add_node_enumerator(bus, NULL, "/org/freedesktop/systemd1/unit", bus_unit_enumerate, m);
+        r = bus_add_implementation(bus, &bus_manager_object, m);
         if (r < 0)
-                return log_error_errno(r, "Failed to add job enumerator: %m");
-
-        for (t = 0; t < _UNIT_TYPE_MAX; t++) {
-                const char *interface;
-
-                assert_se(interface = unit_dbus_interface_from_type(t));
-
-                r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, unit_vtable[t]->bus_vtable, bus_unit_interface_find, m);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to register type specific vtable for %s: %m", interface);
-
-                if (unit_vtable[t]->cgroup_context_offset > 0) {
-                        r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, bus_unit_cgroup_vtable, bus_unit_cgroup_find, m);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to register control group unit vtable for %s: %m", interface);
-
-                        r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, bus_cgroup_vtable, bus_cgroup_context_find, m);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to register control group vtable for %s: %m", interface);
-                }
-
-                if (unit_vtable[t]->exec_context_offset > 0) {
-                        r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, bus_exec_vtable, bus_exec_context_find, m);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to register execute vtable for %s: %m", interface);
-                }
-
-                if (unit_vtable[t]->kill_context_offset > 0) {
-                        r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, bus_kill_vtable, bus_kill_context_find, m);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to register kill vtable for %s: %m", interface);
-                }
-        }
+                return r;
 
-        return 0;
+        return bus_add_implementation(bus, &manager_log_control_object, m);
 }
 
 static int bus_setup_disconnected_match(Manager *m, sd_bus *bus) {
@@ -956,10 +1002,15 @@ static void destroy_bus(Manager *m, sd_bus **bus) {
                 if (j->bus_track && sd_bus_track_get_bus(j->bus_track) == *bus)
                         j->bus_track = sd_bus_track_unref(j->bus_track);
 
-        HASHMAP_FOREACH(u, m->units, i)
+        HASHMAP_FOREACH(u, m->units, i) {
                 if (u->bus_track && sd_bus_track_get_bus(u->bus_track) == *bus)
                         u->bus_track = sd_bus_track_unref(u->bus_track);
 
+                /* Get rid of pending freezer messages on this bus */
+                if (u->pending_freezer_message && sd_bus_message_get_bus(u->pending_freezer_message) == *bus)
+                        u->pending_freezer_message = sd_bus_message_unref(u->pending_freezer_message);
+        }
+
         /* Get rid of queued message on this bus */
         if (m->pending_reload_message && sd_bus_message_get_bus(m->pending_reload_message) == *bus)
                 m->pending_reload_message = sd_bus_message_unref(m->pending_reload_message);
@@ -1061,7 +1112,7 @@ int bus_foreach_bus(
         /* Send to all direct buses, unconditionally */
         SET_FOREACH(b, m->private_buses, i) {
 
-                /* Don't bother with enqueing these messages to clients that haven't started yet */
+                /* Don't bother with enqueuing these messages to clients that haven't started yet */
                 if (sd_bus_is_ready(b) <= 0)
                         continue;
 
index d5ba6537eaf5509947f1f4b684e1e08e8ea0a152..812f56ea2a58ccdfb3e12f766a9532071147f9f3 100644 (file)
@@ -33,3 +33,4 @@ int bus_forward_agent_released(Manager *m, const char *path);
 uint64_t manager_bus_n_queued_write(Manager *m);
 
 void dump_bus_properties(FILE *f);
+int bus_manager_introspect_implementations(FILE *out, const char *pattern);
index 45149e75554c9243b299f93a6ebf8b7d43d2320f..50d55289fa88db5c7c9e1aa102d2a732bb000f9d 100644 (file)
@@ -83,6 +83,8 @@ static int device_set_sysfs(Device *d, const char *sysfs) {
         }
 
         d->sysfs = TAKE_PTR(copy);
+        unit_add_to_dbus_queue(UNIT(d));
+
         return 0;
 }
 
@@ -562,9 +564,6 @@ static int device_setup_unit(Manager *m, sd_device *dev, const char *path, bool
         if (dev && device_is_bound_by_mounts(DEVICE(u), dev))
                 device_upgrade_mount_deps(u);
 
-        /* Note that this won't dispatch the load queue, the caller has to do that if needed and appropriate */
-        unit_add_to_dbus_queue(u);
-
         return 0;
 
 fail:
@@ -1081,8 +1080,6 @@ const UnitVTable device_vtable = {
         .active_state = device_active_state,
         .sub_state_to_string = device_sub_state_to_string,
 
-        .bus_vtable = bus_device_vtable,
-
         .following = device_following,
         .following_set = device_following_set,
 
index c4d25d68e426c5cc2b8425dda2153a818a75936a..b6609e63e548d532c8d1cb6f7433e0371607d7f2 100644 (file)
@@ -1,8 +1,6 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
 #include <fcntl.h>
-#include <linux/random.h>
-#include <sys/ioctl.h>
 #include <unistd.h>
 
 #include "alloc-util.h"
@@ -11,6 +9,7 @@
 #include "efivars.h"
 #include "fd-util.h"
 #include "fs-util.h"
+#include "random-util.h"
 #include "strv.h"
 
 /* If a random seed was passed by the boot loader in the LoaderRandomSeed EFI variable, let's credit it to
@@ -43,7 +42,6 @@ static void lock_down_efi_variables(void) {
 }
 
 int efi_take_random_seed(void) {
-        _cleanup_free_ struct rand_pool_info *info = NULL;
         _cleanup_free_ void *value = NULL;
         _cleanup_close_ int random_fd = -1;
         size_t size;
@@ -79,11 +77,6 @@ int efi_take_random_seed(void) {
         if (size == 0)
                 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Random seed passed from boot loader has zero size? Ignoring.");
 
-        /* The kernel API only accepts "int" as entropy count (which is in bits), let's avoid any chance for
-         * confusion here. */
-        if (size > INT_MAX / 8)
-                size = INT_MAX / 8;
-
         random_fd = open("/dev/urandom", O_WRONLY|O_CLOEXEC|O_NOCTTY);
         if (random_fd < 0)
                 return log_warning_errno(errno, "Failed to open /dev/urandom for writing, ignoring: %m");
@@ -94,15 +87,8 @@ int efi_take_random_seed(void) {
         if (r < 0)
                 return log_warning_errno(r, "Unable to mark EFI random seed as used, not using it: %m");
 
-        info = malloc(offsetof(struct rand_pool_info, buf) + size);
-        if (!info)
-                return log_oom();
-
-        info->entropy_count = size * 8;
-        info->buf_size = size;
-        memcpy(info->buf, value, size);
-
-        if (ioctl(random_fd, RNDADDENTROPY, info) < 0)
+        r = random_write_entropy(random_fd, value, size, true);
+        if (r < 0)
                 return log_warning_errno(errno, "Failed to credit entropy, ignoring: %m");
 
         log_info("Successfully credited entropy passed from boot loader.");
index 4330c0f2c1cd25e367d9f33f57ccdacce14433fd..1565a799270c01c2d988dfb648d4fb024a1c486e 100644 (file)
 #include "terminal-util.h"
 #include "virt.h"
 
+static const char* const emergency_action_table[_EMERGENCY_ACTION_MAX] = {
+        [EMERGENCY_ACTION_NONE] =               "none",
+        [EMERGENCY_ACTION_REBOOT] =             "reboot",
+        [EMERGENCY_ACTION_REBOOT_FORCE] =       "reboot-force",
+        [EMERGENCY_ACTION_REBOOT_IMMEDIATE] =   "reboot-immediate",
+        [EMERGENCY_ACTION_POWEROFF] =           "poweroff",
+        [EMERGENCY_ACTION_POWEROFF_FORCE] =     "poweroff-force",
+        [EMERGENCY_ACTION_POWEROFF_IMMEDIATE] = "poweroff-immediate",
+        [EMERGENCY_ACTION_EXIT] =               "exit",
+        [EMERGENCY_ACTION_EXIT_FORCE] =         "exit-force",
+};
+
 static void log_and_status(Manager *m, bool warn, const char *message, const char *reason) {
         log_full(warn ? LOG_WARNING : LOG_DEBUG, "%s: %s", message, reason);
         if (warn)
@@ -28,10 +40,22 @@ void emergency_action(
                 int exit_status,
                 const char *reason) {
 
+        Unit *u;
+
         assert(m);
         assert(action >= 0);
         assert(action < _EMERGENCY_ACTION_MAX);
 
+        /* Is the special shutdown target active or queued? If so, we are in shutdown state */
+        if (IN_SET(action, EMERGENCY_ACTION_REBOOT, EMERGENCY_ACTION_POWEROFF, EMERGENCY_ACTION_EXIT)) {
+                u = manager_get_unit(m, SPECIAL_SHUTDOWN_TARGET);
+                if (u && unit_active_or_pending(u)) {
+                        log_notice("Shutdown is already active. Skipping emergency action request %s.",
+                                   emergency_action_table[action]);
+                        return;
+                }
+        }
+
         if (action == EMERGENCY_ACTION_NONE)
                 return;
 
@@ -126,17 +150,6 @@ void emergency_action(
         }
 }
 
-static const char* const emergency_action_table[_EMERGENCY_ACTION_MAX] = {
-        [EMERGENCY_ACTION_NONE] = "none",
-        [EMERGENCY_ACTION_REBOOT] = "reboot",
-        [EMERGENCY_ACTION_REBOOT_FORCE] = "reboot-force",
-        [EMERGENCY_ACTION_REBOOT_IMMEDIATE] = "reboot-immediate",
-        [EMERGENCY_ACTION_POWEROFF] = "poweroff",
-        [EMERGENCY_ACTION_POWEROFF_FORCE] = "poweroff-force",
-        [EMERGENCY_ACTION_POWEROFF_IMMEDIATE] = "poweroff-immediate",
-        [EMERGENCY_ACTION_EXIT] = "exit",
-        [EMERGENCY_ACTION_EXIT_FORCE] = "exit-force",
-};
 DEFINE_STRING_TABLE_LOOKUP(emergency_action, EmergencyAction);
 
 int parse_emergency_action(
index c1df37a097ff7260adcfd761010358e61e5e34ba..2a4840a3a9b99c418793a6c6046c68de9097b4ab 100644 (file)
@@ -54,6 +54,7 @@
 #include "format-util.h"
 #include "fs-util.h"
 #include "glob-util.h"
+#include "hexdecoct.h"
 #include "io-util.h"
 #include "ioprio.h"
 #include "label.h"
@@ -219,17 +220,10 @@ static bool is_terminal_input(ExecInput i) {
 static bool is_terminal_output(ExecOutput o) {
         return IN_SET(o,
                       EXEC_OUTPUT_TTY,
-                      EXEC_OUTPUT_SYSLOG_AND_CONSOLE,
                       EXEC_OUTPUT_KMSG_AND_CONSOLE,
                       EXEC_OUTPUT_JOURNAL_AND_CONSOLE);
 }
 
-static bool is_syslog_output(ExecOutput o) {
-        return IN_SET(o,
-                      EXEC_OUTPUT_SYSLOG,
-                      EXEC_OUTPUT_SYSLOG_AND_CONSOLE);
-}
-
 static bool is_kmsg_output(ExecOutput o) {
         return IN_SET(o,
                       EXEC_OUTPUT_KMSG,
@@ -361,7 +355,7 @@ static int connect_logger_as(
                 params->flags & EXEC_PASS_LOG_UNIT ? unit->id : "",
                 context->syslog_priority,
                 !!context->syslog_level_prefix,
-                is_syslog_output(output),
+                false,
                 is_kmsg_output(output),
                 is_terminal_output(output)) < 0)
                 return -errno;
@@ -664,8 +658,6 @@ static int setup_output(
                 /* We don't reset the terminal if this is just about output */
                 return open_terminal_as(exec_context_tty_path(context), O_WRONLY, fileno);
 
-        case EXEC_OUTPUT_SYSLOG:
-        case EXEC_OUTPUT_SYSLOG_AND_CONSOLE:
         case EXEC_OUTPUT_KMSG:
         case EXEC_OUTPUT_KMSG_AND_CONSOLE:
         case EXEC_OUTPUT_JOURNAL:
@@ -1388,14 +1380,14 @@ static void rename_process_from_path(const char *path) {
 static bool context_has_address_families(const ExecContext *c) {
         assert(c);
 
-        return c->address_families_whitelist ||
+        return c->address_families_allow_list ||
                 !set_isempty(c->address_families);
 }
 
 static bool context_has_syscall_filters(const ExecContext *c) {
         assert(c);
 
-        return c->syscall_whitelist ||
+        return c->syscall_allow_list ||
                 !hashmap_isempty(c->syscall_filter);
 }
 
@@ -1451,7 +1443,7 @@ static int apply_syscall_filter(const Unit* u, const ExecContext *c, bool needs_
 
         negative_action = c->syscall_errno == 0 ? scmp_act_kill_process() : SCMP_ACT_ERRNO(c->syscall_errno);
 
-        if (c->syscall_whitelist) {
+        if (c->syscall_allow_list) {
                 default_action = negative_action;
                 action = SCMP_ACT_ALLOW;
         } else {
@@ -1460,7 +1452,7 @@ static int apply_syscall_filter(const Unit* u, const ExecContext *c, bool needs_
         }
 
         if (needs_ambient_hack) {
-                r = seccomp_filter_set_add(c->syscall_filter, c->syscall_whitelist, syscall_filter_sets + SYSCALL_FILTER_SET_SETUID);
+                r = seccomp_filter_set_add(c->syscall_filter, c->syscall_allow_list, syscall_filter_sets + SYSCALL_FILTER_SET_SETUID);
                 if (r < 0)
                         return r;
         }
@@ -1491,7 +1483,7 @@ static int apply_address_families(const Unit* u, const ExecContext *c) {
         if (skip_seccomp_unavailable(u, "RestrictAddressFamilies="))
                 return 0;
 
-        return seccomp_restrict_address_families(c->address_families, c->address_families_whitelist);
+        return seccomp_restrict_address_families(c->address_families, c->address_families_allow_list);
 }
 
 static int apply_memory_deny_write_execute(const Unit* u, const ExecContext *c) {
@@ -2569,7 +2561,7 @@ static bool insist_on_sandboxing(
         assert(n_bind_mounts == 0 || bind_mounts);
 
         /* Checks whether we need to insist on fs namespacing. i.e. whether we have settings configured that
-         * would alter the view on the file system beyond making things read-only or invisble, i.e. would
+         * would alter the view on the file system beyond making things read-only or invisible, i.e. would
          * rearrange stuff in a way we cannot ignore gracefully. */
 
         if (context->n_temporary_filesystems > 0)
@@ -2602,7 +2594,7 @@ static int apply_mount_namespace(
                 char **error_path) {
 
         _cleanup_strv_free_ char **empty_directories = NULL;
-        char *tmp = NULL, *var = NULL;
+        const char *tmp_dir = NULL, *var_tmp_dir = NULL;
         const char *root_dir = NULL, *root_image = NULL;
         NamespaceInfo ns_info;
         bool needs_sandboxing;
@@ -2627,13 +2619,19 @@ static int apply_mount_namespace(
         if (needs_sandboxing) {
                 /* The runtime struct only contains the parent of the private /tmp,
                  * which is non-accessible to world users. Inside of it there's a /tmp
-                 * that is sticky, and that's the one we want to use here. */
+                 * that is sticky, and that's the one we want to use here.
+                 * This does not apply when we are using /run/systemd/empty as fallback. */
 
                 if (context->private_tmp && runtime) {
-                        if (runtime->tmp_dir)
-                                tmp = strjoina(runtime->tmp_dir, "/tmp");
-                        if (runtime->var_tmp_dir)
-                                var = strjoina(runtime->var_tmp_dir, "/tmp");
+                        if (streq_ptr(runtime->tmp_dir, RUN_SYSTEMD_EMPTY))
+                                tmp_dir = runtime->tmp_dir;
+                        else if (runtime->tmp_dir)
+                                tmp_dir = strjoina(runtime->tmp_dir, "/tmp");
+
+                        if (streq_ptr(runtime->var_tmp_dir, RUN_SYSTEMD_EMPTY))
+                                var_tmp_dir = runtime->var_tmp_dir;
+                        else if (runtime->var_tmp_dir)
+                                var_tmp_dir = strjoina(runtime->var_tmp_dir, "/tmp");
                 }
 
                 ns_info = (NamespaceInfo) {
@@ -2671,12 +2669,15 @@ static int apply_mount_namespace(
                             n_bind_mounts,
                             context->temporary_filesystems,
                             context->n_temporary_filesystems,
-                            tmp,
-                            var,
+                            tmp_dir,
+                            var_tmp_dir,
                             context->log_namespace,
                             needs_sandboxing ? context->protect_home : PROTECT_HOME_NO,
                             needs_sandboxing ? context->protect_system : PROTECT_SYSTEM_NO,
                             context->mount_flags,
+                            context->root_hash, context->root_hash_size, context->root_hash_path,
+                            context->root_hash_sig, context->root_hash_sig_size, context->root_hash_sig_path,
+                            context->root_verity,
                             DISSECT_IMAGE_DISCARD_ON_LOOP|DISSECT_IMAGE_RELAX_VAR_CHECK|DISSECT_IMAGE_FSCK,
                             error_path);
 
@@ -2862,7 +2863,7 @@ static int setup_keyring(
         }
 
 out:
-        /* Revert back uid & gid for the the last time, and exit */
+        /* Revert back uid & gid for the last time, and exit */
         /* no extra logging, as only the first already reported error matters */
         if (getuid() != saved_uid)
                 (void) setreuid(saved_uid, -1);
@@ -3361,6 +3362,14 @@ static int exec_child(
                 }
         }
 
+        if (context->coredump_filter_set) {
+                r = set_coredump_filter(context->coredump_filter);
+                if (ERRNO_IS_PRIVILEGE(r))
+                        log_unit_debug_errno(unit, r, "Failed to adjust coredump_filter, ignoring: %m");
+                else if (r < 0)
+                        return log_unit_error_errno(unit, r, "Failed to adjust coredump_filter: %m");
+        }
+
         if (context->nice_set) {
                 r = setpriority_closest(context->nice);
                 if (r < 0)
@@ -4198,6 +4207,13 @@ void exec_context_done(ExecContext *c) {
         c->working_directory = mfree(c->working_directory);
         c->root_directory = mfree(c->root_directory);
         c->root_image = mfree(c->root_image);
+        c->root_hash = mfree(c->root_hash);
+        c->root_hash_size = 0;
+        c->root_hash_path = mfree(c->root_hash_path);
+        c->root_hash_sig = mfree(c->root_hash_sig);
+        c->root_hash_sig_size = 0;
+        c->root_hash_sig_path = mfree(c->root_hash_sig_path);
+        c->root_verity = mfree(c->root_verity);
         c->tty_path = mfree(c->tty_path);
         c->syslog_identifier = mfree(c->syslog_identifier);
         c->user = mfree(c->user);
@@ -4602,6 +4618,30 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
         if (c->root_image)
                 fprintf(f, "%sRootImage: %s\n", prefix, c->root_image);
 
+        if (c->root_hash) {
+                _cleanup_free_ char *encoded = NULL;
+                encoded = hexmem(c->root_hash, c->root_hash_size);
+                if (encoded)
+                        fprintf(f, "%sRootHash: %s\n", prefix, encoded);
+        }
+
+        if (c->root_hash_path)
+                fprintf(f, "%sRootHash: %s\n", prefix, c->root_hash_path);
+
+        if (c->root_hash_sig) {
+                _cleanup_free_ char *encoded = NULL;
+                ssize_t len;
+                len = base64mem(c->root_hash_sig, c->root_hash_sig_size, &encoded);
+                if (len)
+                        fprintf(f, "%sRootHashSignature: base64:%s\n", prefix, encoded);
+        }
+
+        if (c->root_hash_sig_path)
+                fprintf(f, "%sRootHashSignature: %s\n", prefix, c->root_hash_sig_path);
+
+        if (c->root_verity)
+                fprintf(f, "%sRootVerity: %s\n", prefix, c->root_verity);
+
         STRV_FOREACH(e, c->environment)
                 fprintf(f, "%sEnvironment: %s\n", prefix, *e);
 
@@ -4637,6 +4677,11 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
                         "%sOOMScoreAdjust: %i\n",
                         prefix, c->oom_score_adjust);
 
+        if (c->coredump_filter_set)
+                fprintf(f,
+                        "%sCoredumpFilter: 0x%"PRIx64"\n",
+                        prefix, c->coredump_filter);
+
         for (i = 0; i < RLIM_NLIMITS; i++)
                 if (c->rlimit[i]) {
                         fprintf(f, "%sLimit%s: " RLIM_FMT "\n",
@@ -4725,17 +4770,13 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
                         prefix, yes_no(c->tty_vt_disallocate));
 
         if (IN_SET(c->std_output,
-                   EXEC_OUTPUT_SYSLOG,
                    EXEC_OUTPUT_KMSG,
                    EXEC_OUTPUT_JOURNAL,
-                   EXEC_OUTPUT_SYSLOG_AND_CONSOLE,
                    EXEC_OUTPUT_KMSG_AND_CONSOLE,
                    EXEC_OUTPUT_JOURNAL_AND_CONSOLE) ||
             IN_SET(c->std_error,
-                   EXEC_OUTPUT_SYSLOG,
                    EXEC_OUTPUT_KMSG,
                    EXEC_OUTPUT_JOURNAL,
-                   EXEC_OUTPUT_SYSLOG_AND_CONSOLE,
                    EXEC_OUTPUT_KMSG_AND_CONSOLE,
                    EXEC_OUTPUT_JOURNAL_AND_CONSOLE)) {
 
@@ -4901,7 +4942,7 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
                         "%sSystemCallFilter: ",
                         prefix);
 
-                if (!c->syscall_whitelist)
+                if (!c->syscall_allow_list)
                         fputc('~', f);
 
 #if HAVE_SECCOMP
@@ -5314,28 +5355,25 @@ static ExecRuntime* exec_runtime_free(ExecRuntime *rt, bool destroy) {
                 (void) hashmap_remove(rt->manager->exec_runtime_by_id, rt->id);
 
         /* When destroy is true, then rm_rf tmp_dir and var_tmp_dir. */
-        if (destroy && rt->tmp_dir) {
+
+        if (destroy && rt->tmp_dir && !streq(rt->tmp_dir, RUN_SYSTEMD_EMPTY)) {
                 log_debug("Spawning thread to nuke %s", rt->tmp_dir);
 
                 r = asynchronous_job(remove_tmpdir_thread, rt->tmp_dir);
-                if (r < 0) {
+                if (r < 0)
                         log_warning_errno(r, "Failed to nuke %s: %m", rt->tmp_dir);
-                        free(rt->tmp_dir);
-                }
-
-                rt->tmp_dir = NULL;
+                else
+                        rt->tmp_dir = NULL;
         }
 
-        if (destroy && rt->var_tmp_dir) {
+        if (destroy && rt->var_tmp_dir && !streq(rt->var_tmp_dir, RUN_SYSTEMD_EMPTY)) {
                 log_debug("Spawning thread to nuke %s", rt->var_tmp_dir);
 
                 r = asynchronous_job(remove_tmpdir_thread, rt->var_tmp_dir);
-                if (r < 0) {
+                if (r < 0)
                         log_warning_errno(r, "Failed to nuke %s: %m", rt->var_tmp_dir);
-                        free(rt->var_tmp_dir);
-                }
-
-                rt->var_tmp_dir = NULL;
+                else
+                        rt->var_tmp_dir = NULL;
         }
 
         rt->id = mfree(rt->id);
@@ -5349,16 +5387,22 @@ static void exec_runtime_freep(ExecRuntime **rt) {
         (void) exec_runtime_free(*rt, false);
 }
 
-static int exec_runtime_allocate(ExecRuntime **ret) {
+static int exec_runtime_allocate(ExecRuntime **ret, const char *id) {
+        _cleanup_free_ char *id_copy = NULL;
         ExecRuntime *n;
 
         assert(ret);
 
+        id_copy = strdup(id);
+        if (!id_copy)
+                return -ENOMEM;
+
         n = new(ExecRuntime, 1);
         if (!n)
                 return -ENOMEM;
 
         *n = (ExecRuntime) {
+                .id = TAKE_PTR(id_copy),
                 .netns_storage_socket = { -1, -1 },
         };
 
@@ -5369,9 +5413,9 @@ static int exec_runtime_allocate(ExecRuntime **ret) {
 static int exec_runtime_add(
                 Manager *m,
                 const char *id,
-                const char *tmp_dir,
-                const char *var_tmp_dir,
-                const int netns_storage_socket[2],
+                char **tmp_dir,
+                char **var_tmp_dir,
+                int netns_storage_socket[2],
                 ExecRuntime **ret) {
 
         _cleanup_(exec_runtime_freep) ExecRuntime *rt = NULL;
@@ -5380,51 +5424,40 @@ static int exec_runtime_add(
         assert(m);
         assert(id);
 
+        /* tmp_dir, var_tmp_dir, netns_storage_socket fds are donated on success */
+
         r = hashmap_ensure_allocated(&m->exec_runtime_by_id, &string_hash_ops);
         if (r < 0)
                 return r;
 
-        r = exec_runtime_allocate(&rt);
+        r = exec_runtime_allocate(&rt, id);
         if (r < 0)
                 return r;
 
-        rt->id = strdup(id);
-        if (!rt->id)
-                return -ENOMEM;
-
-        if (tmp_dir) {
-                rt->tmp_dir = strdup(tmp_dir);
-                if (!rt->tmp_dir)
-                        return -ENOMEM;
+        r = hashmap_put(m->exec_runtime_by_id, rt->id, rt);
+        if (r < 0)
+                return r;
 
-                /* When tmp_dir is set, then we require var_tmp_dir is also set. */
-                assert(var_tmp_dir);
-                rt->var_tmp_dir = strdup(var_tmp_dir);
-                if (!rt->var_tmp_dir)
-                        return -ENOMEM;
-        }
+        assert(!!rt->tmp_dir == !!rt->var_tmp_dir); /* We require both to be set together */
+        rt->tmp_dir = TAKE_PTR(*tmp_dir);
+        rt->var_tmp_dir = TAKE_PTR(*var_tmp_dir);
 
         if (netns_storage_socket) {
-                rt->netns_storage_socket[0] = netns_storage_socket[0];
-                rt->netns_storage_socket[1] = netns_storage_socket[1];
+                rt->netns_storage_socket[0] = TAKE_FD(netns_storage_socket[0]);
+                rt->netns_storage_socket[1] = TAKE_FD(netns_storage_socket[1]);
         }
 
-        r = hashmap_put(m->exec_runtime_by_id, rt->id, rt);
-        if (r < 0)
-                return r;
-
         rt->manager = m;
 
         if (ret)
                 *ret = rt;
-
         /* do not remove created ExecRuntime object when the operation succeeds. */
-        rt = NULL;
+        TAKE_PTR(rt);
         return 0;
 }
 
 static int exec_runtime_make(Manager *m, const ExecContext *c, const char *id, ExecRuntime **ret) {
-        _cleanup_free_ char *tmp_dir = NULL, *var_tmp_dir = NULL;
+        _cleanup_(namespace_cleanup_tmpdirp) char *tmp_dir = NULL, *var_tmp_dir = NULL;
         _cleanup_close_pair_ int netns_storage_socket[2] = { -1, -1 };
         int r;
 
@@ -5436,7 +5469,10 @@ static int exec_runtime_make(Manager *m, const ExecContext *c, const char *id, E
         if (!c->private_network && !c->private_tmp && !c->network_namespace_path)
                 return 0;
 
-        if (c->private_tmp) {
+        if (c->private_tmp &&
+            !(prefixed_path_strv_contains(c->inaccessible_paths, "/tmp") &&
+              (prefixed_path_strv_contains(c->inaccessible_paths, "/var/tmp") ||
+               prefixed_path_strv_contains(c->inaccessible_paths, "/var")))) {
                 r = setup_tmp_dirs(id, &tmp_dir, &var_tmp_dir);
                 if (r < 0)
                         return r;
@@ -5447,12 +5483,10 @@ static int exec_runtime_make(Manager *m, const ExecContext *c, const char *id, E
                         return -errno;
         }
 
-        r = exec_runtime_add(m, id, tmp_dir, var_tmp_dir, netns_storage_socket, ret);
+        r = exec_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_storage_socket, ret);
         if (r < 0)
                 return r;
 
-        /* Avoid cleanup */
-        netns_storage_socket[0] = netns_storage_socket[1] = -1;
         return 1;
 }
 
@@ -5570,14 +5604,10 @@ int exec_runtime_deserialize_compat(Unit *u, const char *key, const char *value,
 
         rt = hashmap_get(u->manager->exec_runtime_by_id, u->id);
         if (!rt) {
-                r = exec_runtime_allocate(&rt_create);
+                r = exec_runtime_allocate(&rt_create, u->id);
                 if (r < 0)
                         return log_oom();
 
-                rt_create->id = strdup(u->id);
-                if (!rt_create->id)
-                        return log_oom();
-
                 rt = rt_create;
         }
 
@@ -5634,15 +5664,16 @@ int exec_runtime_deserialize_compat(Unit *u, const char *key, const char *value,
                 rt_create->manager = u->manager;
 
                 /* Avoid cleanup */
-                rt_create = NULL;
+                TAKE_PTR(rt_create);
         }
 
         return 1;
 }
 
-void exec_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) {
-        char *id = NULL, *tmp_dir = NULL, *var_tmp_dir = NULL;
-        int r, fd0 = -1, fd1 = -1;
+int exec_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) {
+        _cleanup_free_ char *tmp_dir = NULL, *var_tmp_dir = NULL;
+        char *id = NULL;
+        int r, fdpair[] = {-1, -1};
         const char *p, *v = value;
         size_t n;
 
@@ -5659,7 +5690,9 @@ void exec_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) {
         v = startswith(p, "tmp-dir=");
         if (v) {
                 n = strcspn(v, " ");
-                tmp_dir = strndupa(v, n);
+                tmp_dir = strndup(v, n);
+                if (!tmp_dir)
+                        return log_oom();
                 if (v[n] != ' ')
                         goto finalize;
                 p = v + n + 1;
@@ -5668,7 +5701,9 @@ void exec_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) {
         v = startswith(p, "var-tmp-dir=");
         if (v) {
                 n = strcspn(v, " ");
-                var_tmp_dir = strndupa(v, n);
+                var_tmp_dir = strndup(v, n);
+                if (!var_tmp_dir)
+                        return log_oom();
                 if (v[n] != ' ')
                         goto finalize;
                 p = v + n + 1;
@@ -5680,11 +5715,9 @@ void exec_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) {
 
                 n = strcspn(v, " ");
                 buf = strndupa(v, n);
-                if (safe_atoi(buf, &fd0) < 0 || !fdset_contains(fds, fd0)) {
-                        log_debug("Unable to process exec-runtime netns fd specification.");
-                        return;
-                }
-                fd0 = fdset_remove(fds, fd0);
+                if (safe_atoi(buf, &fdpair[0]) < 0 || !fdset_contains(fds, fdpair[0]))
+                        return log_debug("Unable to process exec-runtime netns fd specification.");
+                fdpair[0] = fdset_remove(fds, fdpair[0]);
                 if (v[n] != ' ')
                         goto finalize;
                 p = v + n + 1;
@@ -5696,18 +5729,16 @@ void exec_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) {
 
                 n = strcspn(v, " ");
                 buf = strndupa(v, n);
-                if (safe_atoi(buf, &fd1) < 0 || !fdset_contains(fds, fd1)) {
-                        log_debug("Unable to process exec-runtime netns fd specification.");
-                        return;
-                }
-                fd1 = fdset_remove(fds, fd1);
+                if (safe_atoi(buf, &fdpair[1]) < 0 || !fdset_contains(fds, fdpair[1]))
+                        return log_debug("Unable to process exec-runtime netns fd specification.");
+                fdpair[1] = fdset_remove(fds, fdpair[1]);
         }
 
 finalize:
-
-        r = exec_runtime_add(m, id, tmp_dir, var_tmp_dir, (int[]) { fd0, fd1 }, NULL);
+        r = exec_runtime_add(m, id, &tmp_dir, &var_tmp_dir, fdpair, NULL);
         if (r < 0)
-                log_debug_errno(r, "Failed to add exec-runtime: %m");
+                return log_debug_errno(r, "Failed to add exec-runtime: %m");
+        return 0;
 }
 
 void exec_runtime_vacuum(Manager *m) {
@@ -5730,7 +5761,10 @@ void exec_params_clear(ExecParameters *p) {
         if (!p)
                 return;
 
-        strv_free(p->environment);
+        p->environment = strv_free(p->environment);
+        p->fd_names = strv_free(p->fd_names);
+        p->fds = mfree(p->fds);
+        p->exec_fd = safe_close(p->exec_fd);
 }
 
 static const char* const exec_input_table[_EXEC_INPUT_MAX] = {
@@ -5750,8 +5784,6 @@ static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = {
         [EXEC_OUTPUT_INHERIT] = "inherit",
         [EXEC_OUTPUT_NULL] = "null",
         [EXEC_OUTPUT_TTY] = "tty",
-        [EXEC_OUTPUT_SYSLOG] = "syslog",
-        [EXEC_OUTPUT_SYSLOG_AND_CONSOLE] = "syslog+console",
         [EXEC_OUTPUT_KMSG] = "kmsg",
         [EXEC_OUTPUT_KMSG_AND_CONSOLE] = "kmsg+console",
         [EXEC_OUTPUT_JOURNAL] = "journal",
index 4baf5b1a405f942d7d00de64ab4a6eedd30b03a5..fc7bc5c24b2a89d0b98b0f182644264fa841117b 100644 (file)
@@ -14,6 +14,7 @@ typedef struct Manager Manager;
 #include <sys/capability.h>
 
 #include "cgroup-util.h"
+#include "coredump-util.h"
 #include "cpu-set-util.h"
 #include "exec-util.h"
 #include "fdset.h"
@@ -51,8 +52,6 @@ typedef enum ExecOutput {
         EXEC_OUTPUT_INHERIT,
         EXEC_OUTPUT_NULL,
         EXEC_OUTPUT_TTY,
-        EXEC_OUTPUT_SYSLOG,
-        EXEC_OUTPUT_SYSLOG_AND_CONSOLE,
         EXEC_OUTPUT_KMSG,
         EXEC_OUTPUT_KMSG_AND_CONSOLE,
         EXEC_OUTPUT_JOURNAL,
@@ -156,11 +155,14 @@ struct ExecContext {
         char **unset_environment;
 
         struct rlimit *rlimit[_RLIMIT_MAX];
-        char *working_directory, *root_directory, *root_image;
+        char *working_directory, *root_directory, *root_image, *root_verity, *root_hash_path, *root_hash_sig_path;
+        void *root_hash, *root_hash_sig;
+        size_t root_hash_size, root_hash_sig_size;
         bool working_directory_missing_ok:1;
         bool working_directory_home:1;
 
         bool oom_score_adjust_set:1;
+        bool coredump_filter_set:1;
         bool nice_set:1;
         bool ioprio_set:1;
         bool cpu_sched_set:1;
@@ -179,6 +181,7 @@ struct ExecContext {
         int ioprio;
         int cpu_sched_policy;
         int cpu_sched_priority;
+        uint64_t coredump_filter;
 
         CPUSet cpu_set;
         NUMAPolicy numa_policy;
@@ -284,9 +287,9 @@ struct ExecContext {
         Hashmap *syscall_filter;
         Set *syscall_archs;
         int syscall_errno;
-        bool syscall_whitelist:1;
+        bool syscall_allow_list:1;
 
-        bool address_families_whitelist:1;
+        bool address_families_allow_list:1;
         Set *address_families;
 
         char *network_namespace_path;
@@ -402,7 +405,7 @@ ExecRuntime *exec_runtime_unref(ExecRuntime *r, bool destroy);
 
 int exec_runtime_serialize(const Manager *m, FILE *f, FDSet *fds);
 int exec_runtime_deserialize_compat(Unit *u, const char *key, const char *value, FDSet *fds);
-void exec_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds);
+int exec_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds);
 void exec_runtime_vacuum(Manager *m);
 
 void exec_params_clear(ExecParameters *p);
diff --git a/src/core/generator-setup.c b/src/core/generator-setup.c
new file mode 100644 (file)
index 0000000..78ff590
--- /dev/null
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <unistd.h>
+
+#include "generator-setup.h"
+#include "macro.h"
+#include "mkdir.h"
+#include "rm-rf.h"
+
+int lookup_paths_mkdir_generator(LookupPaths *p) {
+        int r, q;
+
+        assert(p);
+
+        if (!p->generator || !p->generator_early || !p->generator_late)
+                return -EINVAL;
+
+        r = mkdir_p_label(p->generator, 0755);
+
+        q = mkdir_p_label(p->generator_early, 0755);
+        if (q < 0 && r >= 0)
+                r = q;
+
+        q = mkdir_p_label(p->generator_late, 0755);
+        if (q < 0 && r >= 0)
+                r = q;
+
+        return r;
+}
+
+void lookup_paths_trim_generator(LookupPaths *p) {
+        assert(p);
+
+        /* Trim empty dirs */
+
+        if (p->generator)
+                (void) rmdir(p->generator);
+        if (p->generator_early)
+                (void) rmdir(p->generator_early);
+        if (p->generator_late)
+                (void) rmdir(p->generator_late);
+}
+
+void lookup_paths_flush_generator(LookupPaths *p) {
+        assert(p);
+
+        /* Flush the generated unit files in full */
+
+        if (p->generator)
+                (void) rm_rf(p->generator, REMOVE_ROOT|REMOVE_PHYSICAL);
+        if (p->generator_early)
+                (void) rm_rf(p->generator_early, REMOVE_ROOT|REMOVE_PHYSICAL);
+        if (p->generator_late)
+                (void) rm_rf(p->generator_late, REMOVE_ROOT|REMOVE_PHYSICAL);
+
+        if (p->temporary_dir)
+                (void) rm_rf(p->temporary_dir, REMOVE_ROOT|REMOVE_PHYSICAL);
+}
diff --git a/src/core/generator-setup.h b/src/core/generator-setup.h
new file mode 100644 (file)
index 0000000..9688601
--- /dev/null
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "path-lookup.h"
+
+int lookup_paths_mkdir_generator(LookupPaths *p);
+void lookup_paths_trim_generator(LookupPaths *p);
+void lookup_paths_flush_generator(LookupPaths *p);
index 83cce88131eb656396c2ef86216060ce600b68db..6d047db8388c8e94b3947f75e8c1a4fac35e6ba1 100644 (file)
 #include "hostname-util.h"
 #include "log.h"
 #include "macro.h"
+#include "proc-cmdline.h"
 #include "string-util.h"
 #include "util.h"
 
 int hostname_setup(void) {
         _cleanup_free_ char *b = NULL;
+        const char *hn = NULL;
         bool enoent = false;
-        const char *hn;
         int r;
 
-        r = read_etc_hostname(NULL, &b);
-        if (r < 0) {
-                if (r == -ENOENT)
-                        enoent = true;
-                else
-                        log_warning_errno(r, "Failed to read configured hostname: %m");
+        r = proc_cmdline_get_key("systemd.hostname", 0, &b);
+        if (r < 0)
+                log_warning_errno(r, "Failed to retrieve system hostname from kernel command line, ignoring: %m");
+        else if (r > 0) {
+                if (hostname_is_valid(b, true))
+                        hn = b;
+                else  {
+                        log_warning("Hostname specified on kernel command line is invalid, ignoring: %s", b);
+                        b = mfree(b);
+                }
+        }
 
-                hn = NULL;
-        } else
-                hn = b;
+        if (!hn) {
+                r = read_etc_hostname(NULL, &b);
+                if (r < 0) {
+                        if (r == -ENOENT)
+                                enoent = true;
+                        else
+                                log_warning_errno(r, "Failed to read configured hostname: %m");
+                } else
+                        hn = b;
+        }
 
         if (isempty(hn)) {
-                /* Don't override the hostname if it is already set
-                 * and not explicitly configured */
+                /* Don't override the hostname if it is already set and not explicitly configured */
                 if (hostname_is_set())
                         return 0;
 
index 4f9336fcd4444f9691849a785ed5250b79f62377..d97cb64d384ec09caa761c0b88d1d90e60d7b3a8 100644 (file)
@@ -263,6 +263,10 @@ int job_install_deserialized(Job *j) {
                 return log_unit_debug_errno(j->unit, SYNTHETIC_ERRNO(EEXIST),
                                             "Unit already has a job installed. Not installing deserialized job.");
 
+        r = hashmap_ensure_allocated(&j->manager->jobs, NULL);
+        if (r < 0)
+                return r;
+
         r = hashmap_put(j->manager->jobs, UINT32_TO_PTR(j->id), j);
         if (r == -EEXIST)
                 return log_unit_debug_errno(j->unit, r, "Job ID %" PRIu32 " already used, cannot deserialize job.", j->id);
@@ -986,9 +990,10 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool alr
 
         j->result = result;
 
-        log_unit_debug(u, "Job %" PRIu32 " %s/%s finished, result=%s", j->id, u->id, job_type_to_string(t), job_result_to_string(result));
+        log_unit_debug(u, "Job %" PRIu32 " %s/%s finished, result=%s",
+                       j->id, u->id, job_type_to_string(t), job_result_to_string(result));
 
-        /* If this job did nothing to respective unit we don't log the status message */
+        /* If this job did nothing to the respective unit we don't log the status message */
         if (!already)
                 job_emit_done_status_message(u, j->id, t, result);
 
@@ -1025,7 +1030,7 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool alr
          * aren't active. This is when the verify-active job merges with a
          * satisfying job type, and then loses it's invalidation effect, as the
          * result there is JOB_DONE for the start job we merged into, while we
-         * should be failing the depending job if the said unit isn't infact
+         * should be failing the depending job if the said unit isn't in fact
          * active. Oneshots are an example of this, where going directly from
          * activating to inactive is success.
          *
index f61e9da6f281d2deaad55f11ba869424ed555a09..fb3f68561b38bf629ce793ceacdf14a596bf5c1c 100644 (file)
@@ -19,9 +19,8 @@ static int process_deps(Unit *u, UnitDependency dependency, const char *dir_suff
         r = unit_file_find_dropin_paths(NULL,
                                         u->manager->lookup_paths.search_path,
                                         u->manager->unit_path_cache,
-                                        dir_suffix,
-                                        NULL,
-                                        u->names,
+                                        dir_suffix, NULL,
+                                        u->id, u->aliases,
                                         &paths);
         if (r < 0)
                 return r;
@@ -114,12 +113,13 @@ int unit_load_dropin(Unit *u) {
         }
 
         STRV_FOREACH(f, u->dropin_paths)
-                (void) config_parse(u->id, *f, NULL,
-                                    UNIT_VTABLE(u)->sections,
-                                    config_item_perf_lookup, load_fragment_gperf_lookup,
-                                    0, u);
-
-        u->dropin_mtime = now(CLOCK_REALTIME);
+                (void) config_parse(
+                                u->id, *f, NULL,
+                                UNIT_VTABLE(u)->sections,
+                                config_item_perf_lookup, load_fragment_gperf_lookup,
+                                0,
+                                u,
+                                &u->dropin_mtime);
 
         return 0;
 }
index ea15554d88b9475891d4497e7000086cf51c43cb..5e2ec0d80abc2e966e38cf2923a14af96ca53415 100644 (file)
@@ -13,7 +13,7 @@ static inline int unit_find_dropin_paths(Unit *u, char ***paths) {
                                            u->manager->lookup_paths.search_path,
                                            u->manager->unit_path_cache,
                                            ".d", ".conf",
-                                           u->names,
+                                           u->id, u->aliases,
                                            paths);
 }
 
index 69abdb65ba704d018e37e8aa6afc32bf20ac6898..12ae78eb7dc5d4552665ac3d1916faa3f8bb1244 100644 (file)
@@ -23,11 +23,15 @@ m4_define(`EXEC_CONTEXT_CONFIG_ITEMS',
 `$1.WorkingDirectory,            config_parse_working_directory,     0,                             offsetof($1, exec_context)
 $1.RootDirectory,                config_parse_unit_path_printf,      true,                          offsetof($1, exec_context.root_directory)
 $1.RootImage,                    config_parse_unit_path_printf,      true,                          offsetof($1, exec_context.root_image)
+$1.RootHash,                     config_parse_exec_root_hash,        0,                             offsetof($1, exec_context)
+$1.RootHashSignature,            config_parse_exec_root_hash_sig,    0,                             offsetof($1, exec_context)
+$1.RootVerity,                   config_parse_unit_path_printf,      true,                          offsetof($1, exec_context.root_verity)
 $1.User,                         config_parse_user_group_compat,     0,                             offsetof($1, exec_context.user)
 $1.Group,                        config_parse_user_group_compat,     0,                             offsetof($1, exec_context.group)
 $1.SupplementaryGroups,          config_parse_user_group_strv_compat, 0,                            offsetof($1, exec_context.supplementary_groups)
 $1.Nice,                         config_parse_exec_nice,             0,                             offsetof($1, exec_context)
 $1.OOMScoreAdjust,               config_parse_exec_oom_score_adjust, 0,                             offsetof($1, exec_context)
+$1.CoredumpFilter,               config_parse_exec_coredump_filter,  0,                             offsetof($1, exec_context)
 $1.IOSchedulingClass,            config_parse_exec_io_class,         0,                             offsetof($1, exec_context)
 $1.IOSchedulingPriority,         config_parse_exec_io_priority,      0,                             offsetof($1, exec_context)
 $1.CPUSchedulingPolicy,          config_parse_exec_cpu_sched_policy, 0,                             offsetof($1, exec_context)
@@ -321,6 +325,8 @@ Service.TimeoutSec,              config_parse_service_timeout,       0,
 Service.TimeoutStartSec,         config_parse_service_timeout,       0,                             0
 Service.TimeoutStopSec,          config_parse_sec_fix_0,             0,                             offsetof(Service, timeout_stop_usec)
 Service.TimeoutAbortSec,         config_parse_service_timeout_abort, 0,                             0
+Service.TimeoutStartFailureMode, config_parse_service_timeout_failure_mode,  0,                     offsetof(Service, timeout_start_failure_mode)
+Service.TimeoutStopFailureMode,  config_parse_service_timeout_failure_mode,  0,                     offsetof(Service, timeout_stop_failure_mode)
 Service.RuntimeMaxSec,           config_parse_sec,                   0,                             offsetof(Service, runtime_max_usec)
 Service.WatchdogSec,             config_parse_sec,                   0,                             offsetof(Service, watchdog_usec)
 m4_dnl The following five only exist for compatibility, they moved into Unit, see above
@@ -395,6 +401,7 @@ Socket.Transparent,              config_parse_bool,                  0,
 Socket.Broadcast,                config_parse_bool,                  0,                             offsetof(Socket, broadcast)
 Socket.PassCredentials,          config_parse_bool,                  0,                             offsetof(Socket, pass_cred)
 Socket.PassSecurity,             config_parse_bool,                  0,                             offsetof(Socket, pass_sec)
+Socket.PassPacketInfo,           config_parse_bool,                  0,                             offsetof(Socket, pass_pktinfo)
 Socket.TCPCongestion,            config_parse_string,                0,                             offsetof(Socket, tcp_congestion)
 Socket.ReusePort,                config_parse_bool,                  0,                             offsetof(Socket, reuse_port)
 Socket.MessageQueueMaxMessages,  config_parse_long,                  0,                             offsetof(Socket, mq_maxmsg)
@@ -428,6 +435,7 @@ Mount.DirectoryMode,             config_parse_mode,                  0,
 Mount.SloppyOptions,             config_parse_bool,                  0,                             offsetof(Mount, sloppy_options)
 Mount.LazyUnmount,               config_parse_bool,                  0,                             offsetof(Mount, lazy_unmount)
 Mount.ForceUnmount,              config_parse_bool,                  0,                             offsetof(Mount, force_unmount)
+Mount.ReadWriteOnly,             config_parse_bool,                  0,                             offsetof(Mount, read_write_only)
 EXEC_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl
 CGROUP_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl
 KILL_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl
index 01448fa88edbd287f1f1a1a183af73ef6249be02..3036aa8ba4432c934fe36f0d9f68a7c574dbc8da 100644 (file)
@@ -13,6 +13,8 @@
 #include <sched.h>
 #include <sys/resource.h>
 
+#include "sd-messages.h"
+
 #include "af-list.h"
 #include "alloc-util.h"
 #include "all-units.h"
@@ -29,6 +31,7 @@
 #include "errno-list.h"
 #include "escape.h"
 #include "fd-util.h"
+#include "fileio.h"
 #include "fs-util.h"
 #include "hexdecoct.h"
 #include "io-util.h"
@@ -117,13 +120,13 @@ DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGrou
 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_keyring_mode, exec_keyring_mode, ExecKeyringMode, "Failed to parse keyring mode");
 DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode, "Failed to parse utmp mode");
 DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode");
-DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode");
 DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
 DEFINE_CONFIG_PARSE_ENUM(config_parse_protect_home, protect_home, ProtectHome, "Failed to parse protect home value");
 DEFINE_CONFIG_PARSE_ENUM(config_parse_protect_system, protect_system, ProtectSystem, "Failed to parse protect system value");
 DEFINE_CONFIG_PARSE_ENUM(config_parse_runtime_preserve_mode, exec_preserve_mode, ExecPreserveMode, "Failed to parse runtime directory preserve mode");
 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type");
 DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier");
+DEFINE_CONFIG_PARSE_ENUM(config_parse_service_timeout_failure_mode, service_timeout_failure_mode, ServiceTimeoutFailureMode, "Failed to parse timeout failure mode");
 DEFINE_CONFIG_PARSE_ENUM(config_parse_socket_bind, socket_address_bind_ipv6_only_or_bool, SocketAddressBindIPv6Only, "Failed to parse bind IPv6 only value");
 DEFINE_CONFIG_PARSE_ENUM(config_parse_oom_policy, oom_policy, OOMPolicy, "Failed to parse OOM policy");
 DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_ip_tos, ip_tos, int, -1, "Failed to parse IP TOS value");
@@ -592,6 +595,87 @@ int config_parse_exec_oom_score_adjust(
         return 0;
 }
 
+int config_parse_exec_coredump_filter(
+                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) {
+
+        ExecContext *c = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if (isempty(rvalue)) {
+                c->coredump_filter = 0;
+                c->coredump_filter_set = false;
+                return 0;
+        }
+
+        uint64_t f;
+        r = coredump_filter_mask_from_string(rvalue, &f);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse the CoredumpFilter=%s, ignoring: %m", rvalue);
+                return 0;
+        }
+
+        c->coredump_filter |= f;
+        c->oom_score_adjust_set = true;
+        return 0;
+}
+
+int config_parse_kill_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) {
+
+        KillMode *k = data, m;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if (isempty(rvalue)) {
+                *k = KILL_CONTROL_GROUP;
+                return 0;
+        }
+
+        m = kill_mode_from_string(rvalue);
+        if (m < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "Failed to parse kill mode specification, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        if (m == KILL_NONE)
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "Unit configured to use KillMode=none. "
+                           "This is unsafe, as it disables systemd's process lifecycle management for the service. "
+                           "Please update your service to use a safer KillMode=, such as 'mixed' or 'control-group'. "
+                           "Support for KillMode=none is deprecated and will eventually be removed.");
+
+        *k = m;
+        return 0;
+}
+
 int config_parse_exec(
                 const char *unit,
                 const char *filename,
@@ -1060,6 +1144,7 @@ int config_parse_exec_output(
         const char *n;
         ExecContext *c = data;
         const Unit *u = userdata;
+        bool obsolete = false;
         ExecOutput eo;
         int r;
 
@@ -1084,6 +1169,14 @@ int config_parse_exec_output(
 
                 eo = EXEC_OUTPUT_NAMED_FD;
 
+        } else if (streq(rvalue, "syslog")) {
+                eo = EXEC_OUTPUT_JOURNAL;
+                obsolete = true;
+
+        } else if (streq(rvalue, "syslog+console")) {
+                eo = EXEC_OUTPUT_JOURNAL_AND_CONSOLE;
+                obsolete = true;
+
         } else if ((n = startswith(rvalue, "file:"))) {
 
                 r = unit_full_printf(u, n, &resolved);
@@ -1115,6 +1208,11 @@ int config_parse_exec_output(
                 }
         }
 
+        if (obsolete)
+                log_syntax(unit, LOG_NOTICE, filename, line, 0,
+                           "Standard output type %s is obsolete, automatically updating to %s. Please update your unit file, and consider removing the setting altogether.",
+                           rvalue, exec_output_to_string(eo));
+
         if (streq(lvalue, "StandardOutput")) {
                 if (eo == EXEC_OUTPUT_NAMED_FD)
                         free_and_replace(c->stdio_fdname[STDOUT_FILENO], resolved);
@@ -1318,6 +1416,124 @@ int config_parse_exec_cpu_sched_prio(const char *unit,
         return 0;
 }
 
+int config_parse_exec_root_hash(
+                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 *roothash_decoded = NULL;
+        ExecContext *c = data;
+        size_t roothash_decoded_size = 0;
+        int r;
+
+        assert(data);
+        assert(filename);
+        assert(line);
+        assert(rvalue);
+
+        if (isempty(rvalue)) {
+                /* Reset if the empty string is assigned */
+                c->root_hash_path = mfree(c->root_hash_path);
+                c->root_hash = mfree(c->root_hash);
+                c->root_hash_size = 0;
+                return 0;
+        }
+
+        if (path_is_absolute(rvalue)) {
+                /* We have the path to a roothash to load and decode, eg: RootHash=/foo/bar.roothash */
+                _cleanup_free_ char *p = NULL;
+
+                p = strdup(rvalue);
+                if (!p)
+                        return -ENOMEM;
+
+                free_and_replace(c->root_hash_path, p);
+                c->root_hash = mfree(c->root_hash);
+                c->root_hash_size = 0;
+                return 0;
+        }
+
+        /* We have a roothash to decode, eg: RootHash=012345789abcdef */
+        r = unhexmem(rvalue, strlen(rvalue), &roothash_decoded, &roothash_decoded_size);
+        if (r < 0)
+                return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to decode RootHash=, ignoring: %s", rvalue);
+        if (roothash_decoded_size < sizeof(sd_id128_t))
+                return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), "RootHash= is too short, ignoring: %s", rvalue);
+
+        free_and_replace(c->root_hash, roothash_decoded);
+        c->root_hash_size = roothash_decoded_size;
+        c->root_hash_path = mfree(c->root_hash_path);
+
+        return 0;
+}
+
+int config_parse_exec_root_hash_sig(
+                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 *roothash_sig_decoded = NULL;
+        char *value;
+        ExecContext *c = data;
+        size_t roothash_sig_decoded_size = 0;
+        int r;
+
+        assert(data);
+        assert(filename);
+        assert(line);
+        assert(rvalue);
+
+        if (isempty(rvalue)) {
+                /* Reset if the empty string is assigned */
+                c->root_hash_sig_path = mfree(c->root_hash_sig_path);
+                c->root_hash_sig = mfree(c->root_hash_sig);
+                c->root_hash_sig_size = 0;
+                return 0;
+        }
+
+        if (path_is_absolute(rvalue)) {
+                /* We have the path to a roothash signature to load and decode, eg: RootHashSignature=/foo/bar.roothash.p7s */
+                _cleanup_free_ char *p = NULL;
+
+                p = strdup(rvalue);
+                if (!p)
+                        return -ENOMEM;
+
+                free_and_replace(c->root_hash_sig_path, p);
+                c->root_hash_sig = mfree(c->root_hash_sig);
+                c->root_hash_sig_size = 0;
+                return 0;
+        }
+
+        if (!(value = startswith(rvalue, "base64:")))
+                return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), "Failed to decode RootHashSignature=, not a path but doesn't start with 'base64:', ignoring: %s", rvalue);
+
+        /* We have a roothash signature to decode, eg: RootHashSignature=base64:012345789abcdef */
+        r = unbase64mem(value, strlen(value), &roothash_sig_decoded, &roothash_sig_decoded_size);
+        if (r < 0)
+                return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to decode RootHashSignature=, ignoring: %s", rvalue);
+
+        free_and_replace(c->root_hash_sig, roothash_sig_decoded);
+        c->root_hash_sig_size = roothash_sig_decoded_size;
+        c->root_hash_sig_path = mfree(c->root_hash_sig_path);
+
+        return 0;
+}
+
 int config_parse_exec_cpu_affinity(const char *unit,
                                    const char *filename,
                                    unsigned line,
@@ -1895,7 +2111,7 @@ int config_parse_bus_name(
                 return 0;
         }
 
-        if (!service_name_is_valid(k)) {
+        if (!sd_bus_service_name_is_valid(k)) {
                 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid bus name, ignoring: %s", k);
                 return 0;
         }
@@ -2073,6 +2289,15 @@ int config_parse_user_group_compat(
                 return -ENOEXEC;
         }
 
+        if (strstr(lvalue, "User") && streq(k, NOBODY_USER_NAME))
+                log_struct(LOG_NOTICE,
+                           "MESSAGE=%s:%u: Special user %s configured, this is not safe!", filename, line, k,
+                           "UNIT=%s", unit,
+                           "MESSAGE_ID=" SD_MESSAGE_NOBODY_USER_UNSUITABLE_STR,
+                           "OFFENDING_USER=%s", k,
+                           "CONFIG_FILE=%s", filename,
+                           "CONFIG_LINE=%u", line);
+
         return free_and_replace(*user, k);
 }
 
@@ -2863,7 +3088,7 @@ int config_parse_syscall_filter(
         if (isempty(rvalue)) {
                 /* Empty assignment resets the list */
                 c->syscall_filter = hashmap_free(c->syscall_filter);
-                c->syscall_whitelist = false;
+                c->syscall_allow_list = false;
                 return 0;
         }
 
@@ -2879,15 +3104,15 @@ int config_parse_syscall_filter(
 
                 if (invert)
                         /* Allow everything but the ones listed */
-                        c->syscall_whitelist = false;
+                        c->syscall_allow_list = false;
                 else {
                         /* Allow nothing but the ones listed */
-                        c->syscall_whitelist = true;
+                        c->syscall_allow_list = true;
 
-                        /* Accept default syscalls if we are on a whitelist */
+                        /* Accept default syscalls if we are on a allow_list */
                         r = seccomp_parse_syscall_filter(
                                         "@default", -1, c->syscall_filter,
-                                        SECCOMP_PARSE_PERMISSIVE|SECCOMP_PARSE_WHITELIST,
+                                        SECCOMP_PARSE_PERMISSIVE|SECCOMP_PARSE_ALLOW_LIST,
                                         unit,
                                         NULL, 0);
                         if (r < 0)
@@ -2920,7 +3145,7 @@ int config_parse_syscall_filter(
                                 name, num, c->syscall_filter,
                                 SECCOMP_PARSE_LOG|SECCOMP_PARSE_PERMISSIVE|
                                 (invert ? SECCOMP_PARSE_INVERT : 0)|
-                                (c->syscall_whitelist ? SECCOMP_PARSE_WHITELIST : 0),
+                                (c->syscall_allow_list ? SECCOMP_PARSE_ALLOW_LIST : 0),
                                 unit, filename, line);
                 if (r < 0)
                         return r;
@@ -2948,10 +3173,6 @@ int config_parse_syscall_archs(
                 return 0;
         }
 
-        r = set_ensure_allocated(archs, NULL);
-        if (r < 0)
-                return log_oom();
-
         for (;;) {
                 _cleanup_free_ char *word = NULL;
                 uint32_t a;
@@ -2974,7 +3195,7 @@ int config_parse_syscall_archs(
                         continue;
                 }
 
-                r = set_put(*archs, UINT32_TO_PTR(a + 1));
+                r = set_ensure_put(archs, NULL, UINT32_TO_PTR(a + 1));
                 if (r < 0)
                         return log_oom();
         }
@@ -3039,7 +3260,7 @@ int config_parse_address_families(
         if (isempty(rvalue)) {
                 /* Empty assignment resets the list */
                 c->address_families = set_free(c->address_families);
-                c->address_families_whitelist = false;
+                c->address_families_allow_list = false;
                 return 0;
         }
 
@@ -3053,7 +3274,7 @@ int config_parse_address_families(
                 if (!c->address_families)
                         return log_oom();
 
-                c->address_families_whitelist = !invert;
+                c->address_families_allow_list = !invert;
         }
 
         for (p = rvalue;;) {
@@ -3081,7 +3302,7 @@ int config_parse_address_families(
                 /* If we previously wanted to forbid an address family and now
                  * we want to allow it, then just remove it from the list.
                  */
-                if (!invert == c->address_families_whitelist)  {
+                if (!invert == c->address_families_allow_list)  {
                         r = set_put(c->address_families, INT_TO_PTR(af));
                         if (r < 0)
                                 return log_oom();
@@ -3277,7 +3498,12 @@ int config_parse_memory_limit(
         uint64_t bytes = CGROUP_LIMIT_MAX;
         int r;
 
-        if (!isempty(rvalue) && !streq(rvalue, "infinity")) {
+        if (isempty(rvalue) && STR_IN_SET(lvalue, "DefaultMemoryLow",
+                                                  "DefaultMemoryMin",
+                                                  "MemoryLow",
+                                                  "MemoryMin"))
+                bytes = CGROUP_LIMIT_MIN;
+        else if (!isempty(rvalue) && !streq(rvalue, "infinity")) {
 
                 r = parse_permille(rvalue);
                 if (r < 0) {
@@ -3297,17 +3523,11 @@ int config_parse_memory_limit(
         }
 
         if (streq(lvalue, "DefaultMemoryLow")) {
+                c->default_memory_low = bytes;
                 c->default_memory_low_set = true;
-                if (isempty(rvalue))
-                        c->default_memory_low = CGROUP_LIMIT_MIN;
-                else
-                        c->default_memory_low = bytes;
         } else if (streq(lvalue, "DefaultMemoryMin")) {
+                c->default_memory_min = bytes;
                 c->default_memory_min_set = true;
-                if (isempty(rvalue))
-                        c->default_memory_min = CGROUP_LIMIT_MIN;
-                else
-                        c->default_memory_min = bytes;
         } else if (streq(lvalue, "MemoryMin")) {
                 c->memory_min = bytes;
                 c->memory_min_set = true;
@@ -4770,7 +4990,9 @@ int unit_load_fragment(Unit *u) {
                         r = config_parse(u->id, fragment, f,
                                          UNIT_VTABLE(u)->sections,
                                          config_item_perf_lookup, load_fragment_gperf_lookup,
-                                         CONFIG_PARSE_ALLOW_INCLUDE, u);
+                                         0,
+                                         u,
+                                         NULL);
                         if (r == -ENOEXEC)
                                 log_unit_notice_errno(u, r, "Unit configuration has fatal error, unit will not be started.");
                         if (r < 0)
@@ -4845,6 +5067,7 @@ void unit_dump_config_items(FILE *f) {
                 { config_parse_exec,                  "PATH [ARGUMENT [...]]" },
                 { config_parse_service_type,          "SERVICETYPE" },
                 { config_parse_service_restart,       "SERVICERESTART" },
+                { config_parse_service_timeout_failure_mode, "TIMEOUTMODE" },
                 { config_parse_kill_mode,             "KILLMODE" },
                 { config_parse_signal,                "SIGNAL" },
                 { config_parse_socket_listen,         "SOCKET [...]" },
@@ -5005,23 +5228,37 @@ int config_parse_output_restricted(
                 void *userdata) {
 
         ExecOutput t, *eo = data;
+        bool obsolete = false;
 
         assert(filename);
         assert(lvalue);
         assert(rvalue);
         assert(data);
 
-        t = exec_output_from_string(rvalue);
-        if (t < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse output type, ignoring: %s", rvalue);
-                return 0;
-        }
+        if (streq(rvalue, "syslog")) {
+                t = EXEC_OUTPUT_JOURNAL;
+                obsolete = true;
+        } else if (streq(rvalue, "syslog+console")) {
+                t = EXEC_OUTPUT_JOURNAL_AND_CONSOLE;
+                obsolete = true;
+        } else {
+                t = exec_output_from_string(rvalue);
+                if (t < 0) {
+                        log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse output type, ignoring: %s", rvalue);
+                        return 0;
+                }
 
-        if (IN_SET(t, EXEC_OUTPUT_SOCKET, EXEC_OUTPUT_NAMED_FD, EXEC_OUTPUT_FILE, EXEC_OUTPUT_FILE_APPEND)) {
-                log_syntax(unit, LOG_ERR, filename, line, 0, "Standard output types socket, fd:, file:, append: are not supported as defaults, ignoring: %s", rvalue);
-                return 0;
+                if (IN_SET(t, EXEC_OUTPUT_SOCKET, EXEC_OUTPUT_NAMED_FD, EXEC_OUTPUT_FILE, EXEC_OUTPUT_FILE_APPEND)) {
+                        log_syntax(unit, LOG_ERR, filename, line, 0, "Standard output types socket, fd:, file:, append: are not supported as defaults, ignoring: %s", rvalue);
+                        return 0;
+                }
         }
 
+        if (obsolete)
+                log_syntax(unit, LOG_NOTICE, filename, line, 0,
+                           "Standard output type %s is obsolete, automatically updating to %s. Please update your configuration.",
+                           rvalue, exec_output_to_string(t));
+
         *eo = t;
         return 0;
 }
index b6b46b2449b118f6cad5876de780871991c1e250..ac3940a1b7fc092335fc6e0400020b2e4d14c9ae 100644 (file)
@@ -26,9 +26,11 @@ CONFIG_PARSER_PROTOTYPE(config_parse_socket_protocol);
 CONFIG_PARSER_PROTOTYPE(config_parse_socket_bind);
 CONFIG_PARSER_PROTOTYPE(config_parse_exec_nice);
 CONFIG_PARSER_PROTOTYPE(config_parse_exec_oom_score_adjust);
+CONFIG_PARSER_PROTOTYPE(config_parse_exec_coredump_filter);
 CONFIG_PARSER_PROTOTYPE(config_parse_exec);
 CONFIG_PARSER_PROTOTYPE(config_parse_service_timeout);
 CONFIG_PARSER_PROTOTYPE(config_parse_service_timeout_abort);
+CONFIG_PARSER_PROTOTYPE(config_parse_service_timeout_failure_mode);
 CONFIG_PARSER_PROTOTYPE(config_parse_service_type);
 CONFIG_PARSER_PROTOTYPE(config_parse_service_restart);
 CONFIG_PARSER_PROTOTYPE(config_parse_socket_bindtodevice);
@@ -42,6 +44,8 @@ CONFIG_PARSER_PROTOTYPE(config_parse_exec_cpu_sched_policy);
 CONFIG_PARSER_PROTOTYPE(config_parse_exec_cpu_sched_prio);
 CONFIG_PARSER_PROTOTYPE(config_parse_exec_cpu_affinity);
 CONFIG_PARSER_PROTOTYPE(config_parse_exec_secure_bits);
+CONFIG_PARSER_PROTOTYPE(config_parse_exec_root_hash);
+CONFIG_PARSER_PROTOTYPE(config_parse_exec_root_hash_sig);
 CONFIG_PARSER_PROTOTYPE(config_parse_capability_set);
 CONFIG_PARSER_PROTOTYPE(config_parse_exec_mount_flags);
 CONFIG_PARSER_PROTOTYPE(config_parse_timer);
index 284b77c1fcce9d11b4ca0bbb03d13a4c156f96de..f76b82a8a45a18eb7a1981dcc50e1a1e232810db 100644 (file)
@@ -223,11 +223,9 @@ int machine_id_commit(const char *root) {
                 return log_error_errno(r, "Can't fetch current mount namespace: %m");
 
         /* Switch to a new mount namespace, isolate ourself and unmount etc_machine_id in our new namespace */
-        if (unshare(CLONE_NEWNS) < 0)
-                return log_error_errno(errno, "Failed to enter new namespace: %m");
-
-        if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0)
-                return log_error_errno(errno, "Couldn't make-rslave / mountpoint in our private namespace: %m");
+        r = detach_mount_namespace();
+        if (r < 0)
+                return log_error_errno(r, "Failed to set up new mount namespace: %m");
 
         if (umount(etc_machine_id) < 0)
                 return log_error_errno(errno, "Failed to unmount transient %s file in our private namespace: %m", etc_machine_id);
index 380a8094467f6025c74625b07fd056792ddaf272..4a376976e94f987c416a60fe26e6c3f48bb8d142 100644 (file)
@@ -19,6 +19,7 @@
 #include "sd-messages.h"
 
 #include "alloc-util.h"
+#include "apparmor-setup.h"
 #include "architecture.h"
 #include "build.h"
 #include "bus-error.h"
@@ -41,6 +42,7 @@
 #include "fileio.h"
 #include "format-util.h"
 #include "fs-util.h"
+#include "hexdecoct.h"
 #include "hostname-setup.h"
 #include "ima-setup.h"
 #include "killall.h"
@@ -59,6 +61,7 @@
 #include "pretty-print.h"
 #include "proc-cmdline.h"
 #include "process-util.h"
+#include "random-util.h"
 #include "raw-clone.h"
 #include "rlimit-util.h"
 #if HAVE_SECCOMP
@@ -94,10 +97,13 @@ static enum {
         ACTION_TEST,
         ACTION_DUMP_CONFIGURATION_ITEMS,
         ACTION_DUMP_BUS_PROPERTIES,
+        ACTION_BUS_INTROSPECT,
 } arg_action = ACTION_RUN;
 
-/* Those variables are initalized to 0 automatically, so we avoid uninitialized memory access.
- * Real defaults are assigned in reset_arguments() below. */
+static const char *arg_bus_introspect = NULL;
+
+/* Those variables are initialized to 0 automatically, so we avoid uninitialized memory access.  Real
+ * defaults are assigned in reset_arguments() below. */
 static char *arg_default_unit;
 static bool arg_system;
 static bool arg_dump_core;
@@ -144,6 +150,9 @@ static EmergencyAction arg_cad_burst_action;
 static OOMPolicy arg_default_oom_policy;
 static CPUSet arg_cpu_affinity;
 static NUMAPolicy arg_numa_policy;
+static usec_t arg_clock_usec;
+static void *arg_random_seed;
+static size_t arg_random_seed_size;
 
 /* A copy of the original environment block */
 static char **saved_env = NULL;
@@ -319,7 +328,6 @@ static int set_machine_id(const char *m) {
 }
 
 static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
-
         int r;
 
         assert(key);
@@ -331,10 +339,8 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
 
                 if (!unit_name_is_valid(value, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
                         log_warning("Unit name specified on %s= is not valid, ignoring: %s", key, value);
-                else if (in_initrd() == !!startswith(key, "rd.")) {
-                        if (free_and_strdup(&arg_default_unit, value) < 0)
-                                return log_oom();
-                }
+                else if (in_initrd() == !!startswith(key, "rd."))
+                        return free_and_strdup_warn(&arg_default_unit, value);
 
         } else if (proc_cmdline_key_streq(key, "systemd.dump_core")) {
 
@@ -483,7 +489,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
 
                 r = parse_cpu_set(value, &arg_cpu_affinity);
                 if (r < 0)
-                        log_warning_errno(r, "Faile to parse CPU affinity mask '%s', ignoring: %m", value);
+                        log_warning_errno(r, "Failed to parse CPU affinity mask '%s', ignoring: %m", value);
 
         } else if (proc_cmdline_key_streq(key, "systemd.watchdog_device")) {
 
@@ -492,6 +498,30 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
 
                 (void) parse_path_argument_and_warn(value, false, &arg_watchdog_device);
 
+        } else if (proc_cmdline_key_streq(key, "systemd.clock_usec")) {
+
+                if (proc_cmdline_value_missing(key, value))
+                        return 0;
+
+                r = safe_atou64(value, &arg_clock_usec);
+                if (r < 0)
+                        log_warning_errno(r, "Failed to parse systemd.clock_usec= argument, ignoring: %s", value);
+
+        } else if (proc_cmdline_key_streq(key, "systemd.random_seed")) {
+                void *p;
+                size_t sz;
+
+                if (proc_cmdline_value_missing(key, value))
+                        return 0;
+
+                r = unbase64mem(value, (size_t) -1, &p, &sz);
+                if (r < 0)
+                        log_warning_errno(r, "Failed to parse systemd.random_seed= argument, ignoring: %s", value);
+
+                free(arg_random_seed);
+                arg_random_seed = sz > 0 ? p : mfree(p);
+                arg_random_seed_size = sz;
+
         } else if (streq(key, "quiet") && !value) {
 
                 if (arg_show_status == _SHOW_STATUS_INVALID)
@@ -508,10 +538,10 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
         } else if (!value) {
                 const char *target;
 
-                /* SysV compatibility */
+                /* Compatible with SysV, but supported independently even if SysV compatibility is disabled. */
                 target = runlevel_to_target(key);
                 if (target)
-                        return free_and_strdup(&arg_default_unit, target);
+                        return free_and_strdup_warn(&arg_default_unit, target);
         }
 
         return 0;
@@ -546,8 +576,9 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
 
 DEFINE_SETTER(config_parse_level2, log_set_max_level_from_string, "log level");
 DEFINE_SETTER(config_parse_target, log_set_target_from_string, "target");
-DEFINE_SETTER(config_parse_color, log_show_color_from_string, "color" );
+DEFINE_SETTER(config_parse_color, log_show_color_from_string, "color");
 DEFINE_SETTER(config_parse_location, log_show_location_from_string, "location");
+DEFINE_SETTER(config_parse_time, log_show_time_from_string, "time");
 
 static int config_parse_default_timeout_abort(
                 const char *unit,
@@ -575,6 +606,7 @@ static int parse_config_file(void) {
                 { "Manager", "LogTarget",                    config_parse_target,                0, NULL                                   },
                 { "Manager", "LogColor",                     config_parse_color,                 0, NULL                                   },
                 { "Manager", "LogLocation",                  config_parse_location,              0, NULL                                   },
+                { "Manager", "LogTime",                      config_parse_time,                  0, NULL                                   },
                 { "Manager", "DumpCore",                     config_parse_bool,                  0, &arg_dump_core                         },
                 { "Manager", "CrashChVT", /* legacy */       config_parse_crash_chvt,            0, &arg_crash_chvt                        },
                 { "Manager", "CrashChangeVT",                config_parse_crash_chvt,            0, &arg_crash_chvt                        },
@@ -646,7 +678,13 @@ static int parse_config_file(void) {
                 CONF_PATHS_NULSTR("systemd/system.conf.d") :
                 CONF_PATHS_NULSTR("systemd/user.conf.d");
 
-        (void) config_parse_many_nulstr(fn, conf_dirs_nulstr, "Manager\0", config_item_table_lookup, items, CONFIG_PARSE_WARN, NULL);
+        (void) config_parse_many_nulstr(
+                        fn, conf_dirs_nulstr,
+                        "Manager\0",
+                        config_item_table_lookup, items,
+                        CONFIG_PARSE_WARN,
+                        NULL,
+                        NULL);
 
         /* Traditionally "0" was used to turn off the default unit timeouts. Fix this up so that we used USEC_INFINITY
          * like everywhere else. */
@@ -702,16 +740,18 @@ static void set_manager_settings(Manager *m) {
 
         assert(m);
 
-        /* Propagates the various manager settings into the manager object, i.e. properties that effect the manager
-         * itself (as opposed to just being inherited into newly allocated units, see set_manager_defaults() above). */
+        /* Propagates the various manager settings into the manager object, i.e. properties that
+         * effect the manager itself (as opposed to just being inherited into newly allocated
+         * units, see set_manager_defaults() above). */
 
         m->confirm_spawn = arg_confirm_spawn;
         m->service_watchdogs = arg_service_watchdogs;
-        m->runtime_watchdog = arg_runtime_watchdog;
-        m->reboot_watchdog = arg_reboot_watchdog;
-        m->kexec_watchdog = arg_kexec_watchdog;
         m->cad_burst_action = arg_cad_burst_action;
 
+        manager_set_watchdog(m, WATCHDOG_RUNTIME, arg_runtime_watchdog);
+        manager_set_watchdog(m, WATCHDOG_REBOOT, arg_reboot_watchdog);
+        manager_set_watchdog(m, WATCHDOG_KEXEC, arg_kexec_watchdog);
+
         manager_set_show_status(m, arg_show_status, "commandline");
         m->status_unit_format = arg_status_unit_format;
 }
@@ -722,6 +762,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_LOG_TARGET,
                 ARG_LOG_COLOR,
                 ARG_LOG_LOCATION,
+                ARG_LOG_TIME,
                 ARG_UNIT,
                 ARG_SYSTEM,
                 ARG_USER,
@@ -730,6 +771,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_VERSION,
                 ARG_DUMP_CONFIGURATION_ITEMS,
                 ARG_DUMP_BUS_PROPERTIES,
+                ARG_BUS_INTROSPECT,
                 ARG_DUMP_CORE,
                 ARG_CRASH_CHVT,
                 ARG_CRASH_SHELL,
@@ -749,6 +791,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "log-target",               required_argument, NULL, ARG_LOG_TARGET               },
                 { "log-color",                optional_argument, NULL, ARG_LOG_COLOR                },
                 { "log-location",             optional_argument, NULL, ARG_LOG_LOCATION             },
+                { "log-time",                 optional_argument, NULL, ARG_LOG_TIME                 },
                 { "unit",                     required_argument, NULL, ARG_UNIT                     },
                 { "system",                   no_argument,       NULL, ARG_SYSTEM                   },
                 { "user",                     no_argument,       NULL, ARG_USER                     },
@@ -758,6 +801,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "version",                  no_argument,       NULL, ARG_VERSION                  },
                 { "dump-configuration-items", no_argument,       NULL, ARG_DUMP_CONFIGURATION_ITEMS },
                 { "dump-bus-properties",      no_argument,       NULL, ARG_DUMP_BUS_PROPERTIES      },
+                { "bus-introspect",           required_argument, NULL, ARG_BUS_INTROSPECT           },
                 { "dump-core",                optional_argument, NULL, ARG_DUMP_CORE                },
                 { "crash-chvt",               required_argument, NULL, ARG_CRASH_CHVT               },
                 { "crash-shell",              optional_argument, NULL, ARG_CRASH_SHELL              },
@@ -822,6 +866,18 @@ static int parse_argv(int argc, char *argv[]) {
 
                         break;
 
+                case ARG_LOG_TIME:
+
+                        if (optarg) {
+                                r = log_show_time_from_string(optarg);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to parse log time setting \"%s\": %m",
+                                                               optarg);
+                        } else
+                                log_show_time(true);
+
+                        break;
+
                 case ARG_DEFAULT_STD_OUTPUT:
                         r = exec_output_from_string(optarg);
                         if (r < 0)
@@ -873,6 +929,11 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_action = ACTION_DUMP_BUS_PROPERTIES;
                         break;
 
+                case ARG_BUS_INTROSPECT:
+                        arg_bus_introspect = optarg;
+                        arg_action = ACTION_BUS_INTROSPECT;
+                        break;
+
                 case ARG_DUMP_CORE:
                         if (!optarg)
                                 arg_dump_core = true;
@@ -988,11 +1049,9 @@ static int parse_argv(int argc, char *argv[]) {
                 case 'b':
                 case 's':
                 case 'z':
-                        /* Just to eat away the sysvinit kernel
-                         * cmdline args without getopt() error
-                         * messages that we'll parse in
-                         * parse_proc_cmdline_word() or ignore. */
-
+                        /* Just to eat away the sysvinit kernel cmdline args that we'll parse in
+                         * parse_proc_cmdline_item() or ignore, without any getopt() error messages.
+                         */
                 case '?':
                         if (getpid_cached() != 1)
                                 return -EINVAL;
@@ -1023,7 +1082,9 @@ static int help(void) {
                 return log_oom();
 
         printf("%s [OPTIONS...]\n\n"
-               "Starts up and maintains the system or user services.\n\n"
+               "%sStarts and monitors system and user services.%s\n\n"
+               "This program takes no positional arguments.\n\n"
+               "%sOptions%s:\n"
                "  -h --help                      Show this help\n"
                "     --version                   Show version\n"
                "     --test                      Determine initial transaction, dump it and exit\n"
@@ -1032,6 +1093,7 @@ static int help(void) {
                "     --no-pager                  Do not pipe output into a pager\n"
                "     --dump-configuration-items  Dump understood unit configuration items\n"
                "     --dump-bus-properties       Dump exposed bus properties\n"
+               "     --bus-introspect=PATH       Write XML introspection data\n"
                "     --unit=UNIT                 Set default unit\n"
                "     --dump-core[=BOOL]          Dump core on crash\n"
                "     --crash-vt=NR               Change to specified VT on crash\n"
@@ -1043,10 +1105,13 @@ static int help(void) {
                "     --log-level=LEVEL           Set log level (debug, info, notice, warning, err, crit, alert, emerg)\n"
                "     --log-color[=BOOL]          Highlight important log messages\n"
                "     --log-location[=BOOL]       Include code location in log messages\n"
+               "     --log-time[=BOOL]           Prefix log messages with current time\n"
                "     --default-standard-output=  Set default standard output for services\n"
                "     --default-standard-error=   Set default standard error output for services\n"
                "\nSee the %s for details.\n"
                , program_invocation_short_name
+               , ansi_highlight(), ansi_normal()
+               , ansi_underline(), ansi_normal()
                , link
         );
 
@@ -1209,7 +1274,7 @@ static int bump_rlimit_memlock(struct rlimit *saved_rlimit) {
         assert_cc(RLIM_INFINITY > 0);
 
         mm = physical_memory() / 8; /* Let's scale how much we allow to be locked by the amount of physical
-                                     * RAM. We allow an eigth to be locked by us, just to pick a value. */
+                                     * RAM. We allow an eighth to be locked by us, just to pick a value. */
 
         new_rlimit = (struct rlimit) {
                 .rlim_cur = MAX3(HIGH_RLIMIT_MEMLOCK, saved_rlimit->rlim_cur, mm),
@@ -1429,6 +1494,9 @@ static int become_shutdown(
         if (log_get_show_location())
                 command_line[pos++] = "--log-location";
 
+        if (log_get_show_time())
+                command_line[pos++] = "--log-time";
+
         if (streq(shutdown_verb, "exit")) {
                 command_line[pos++] = "--exit-code";
                 command_line[pos++] = exit_code;
@@ -1475,6 +1543,9 @@ static int become_shutdown(
 static void initialize_clock(void) {
         int r;
 
+        /* This is called very early on, before we parse the kernel command line or otherwise figure out why
+         * we are running, but only once. */
+
         if (clock_is_localtime(NULL) > 0) {
                 int min;
 
@@ -1513,6 +1584,62 @@ static void initialize_clock(void) {
                 log_info("System time before build time, advancing clock.");
 }
 
+static void apply_clock_update(void) {
+        struct timespec ts;
+
+        /* This is called later than initialize_clock(), i.e. after we parsed configuration files/kernel
+         * command line and such. */
+
+        if (arg_clock_usec == 0)
+                return;
+
+        if (getpid_cached() != 1)
+                return;
+
+        if (clock_settime(CLOCK_REALTIME, timespec_store(&ts, arg_clock_usec)) < 0)
+                log_error_errno(errno, "Failed to set system clock to time specified on kernel command line: %m");
+        else {
+                char buf[FORMAT_TIMESTAMP_MAX];
+
+                log_info("Set system clock to %s, as specified on the kernel command line.",
+                         format_timestamp(buf, sizeof(buf), arg_clock_usec));
+        }
+}
+
+static void cmdline_take_random_seed(void) {
+        _cleanup_close_ int random_fd = -1;
+        size_t suggested;
+        int r;
+
+        if (arg_random_seed_size == 0)
+                return;
+
+        if (getpid_cached() != 1)
+                return;
+
+        assert(arg_random_seed);
+        suggested = random_pool_size();
+
+        if (arg_random_seed_size < suggested)
+                log_warning("Random seed specified on kernel command line has size %zu, but %zu bytes required to fill entropy pool.",
+                            arg_random_seed_size, suggested);
+
+        random_fd = open("/dev/urandom", O_WRONLY|O_CLOEXEC|O_NOCTTY);
+        if (random_fd < 0) {
+                log_warning_errno(errno, "Failed to open /dev/urandom for writing, ignoring: %m");
+                return;
+        }
+
+        r = random_write_entropy(random_fd, arg_random_seed, arg_random_seed_size, true);
+        if (r < 0) {
+                log_warning_errno(r, "Failed to credit entropy specified on kernel command line, ignoring: %m");
+                return;
+        }
+
+        log_notice("Successfully credited entropy passed on kernel command line.\n"
+                   "Note that the seed provided this way is accessible to unprivileged programs. This functionality should not be used outside of testing environments.");
+}
+
 static void initialize_coredump(bool skip_setup) {
 #if ENABLE_COREDUMP
         if (getpid_cached() != 1)
@@ -1752,11 +1879,10 @@ static int invoke_main_loop(
                         saved_log_level = m->log_level_overridden ? log_get_max_level() : -1;
                         saved_log_target = m->log_target_overridden ? log_get_target() : _LOG_TARGET_INVALID;
 
-                        mac_selinux_reload();
-
                         (void) parse_configuration(saved_rlimit_nofile, saved_rlimit_memlock);
 
                         set_manager_defaults(m);
+                        set_manager_settings(m);
 
                         update_cpu_affinity(false);
                         update_numa_policy(false);
@@ -1947,9 +2073,6 @@ static int initialize_runtime(
                         if (r < 0)
                                 log_warning_errno(r, "Failed to set watchdog device to %s, ignoring: %m", arg_watchdog_device);
                 }
-
-                if (timestamp_is_set(arg_runtime_watchdog))
-                        watchdog_set_timeout(&arg_runtime_watchdog);
         }
 
         if (arg_timer_slack_nsec != NSEC_INFINITY)
@@ -2002,21 +2125,21 @@ static int do_queue_default_job(
                 const char **ret_error_message) {
 
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
-        const char* default_unit;
-        Job *default_unit_job;
-        Unit *target = NULL;
+        const char *unit;
+        Job *job;
+        Unit *target;
         int r;
 
         if (arg_default_unit)
-                default_unit = arg_default_unit;
+                unit = arg_default_unit;
         else if (in_initrd())
-                default_unit = SPECIAL_INITRD_TARGET;
+                unit = SPECIAL_INITRD_TARGET;
         else
-                default_unit = SPECIAL_DEFAULT_TARGET;
+                unit = SPECIAL_DEFAULT_TARGET;
 
-        log_debug("Activating default unit: %s", default_unit);
+        log_debug("Activating default unit: %s", unit);
 
-        r = manager_load_startable_unit_or_warn(m, default_unit, NULL, &target);
+        r = manager_load_startable_unit_or_warn(m, unit, NULL, &target);
         if (r < 0 && in_initrd() && !arg_default_unit) {
                 /* Fall back to default.target, which we used to always use by default. Only do this if no
                  * explicit configuration was given. */
@@ -2038,13 +2161,13 @@ static int do_queue_default_job(
 
         assert(target->load_state == UNIT_LOADED);
 
-        r = manager_add_job(m, JOB_START, target, JOB_ISOLATE, NULL, &error, &default_unit_job);
+        r = manager_add_job(m, JOB_START, target, JOB_ISOLATE, NULL, &error, &job);
         if (r == -EPERM) {
                 log_debug_errno(r, "Default target could not be isolated, starting instead: %s", bus_error_message(&error, r));
 
                 sd_bus_error_free(&error);
 
-                r = manager_add_job(m, JOB_START, target, JOB_REPLACE, NULL, &error, &default_unit_job);
+                r = manager_add_job(m, JOB_START, target, JOB_REPLACE, NULL, &error, &job);
                 if (r < 0) {
                         *ret_error_message = "Failed to start default target";
                         return log_emergency_errno(r, "Failed to start default target: %s", bus_error_message(&error, r));
@@ -2053,9 +2176,12 @@ static int do_queue_default_job(
         } else if (r < 0) {
                 *ret_error_message = "Failed to isolate default target";
                 return log_emergency_errno(r, "Failed to isolate default target: %s", bus_error_message(&error, r));
-        }
+        } else
+                log_info("Queued %s job for default target %s.",
+                         job_type_to_string(job->type),
+                         unit_status_string(job->unit));
 
-        m->default_unit_job_id = default_unit_job->id;
+        m->default_unit_job_id = job->id;
 
         return 0;
 }
@@ -2191,6 +2317,10 @@ static void reset_arguments(void) {
 
         cpu_set_reset(&arg_cpu_affinity);
         numa_policy_reset(&arg_numa_policy);
+
+        arg_random_seed = mfree(arg_random_seed);
+        arg_random_seed_size = 0;
+        arg_clock_usec = 0;
 }
 
 static int parse_configuration(const struct rlimit *saved_rlimit_nofile,
@@ -2220,29 +2350,6 @@ static int parse_configuration(const struct rlimit *saved_rlimit_nofile,
         /* Note that this also parses bits from the kernel command line, including "debug". */
         log_parse_environment();
 
-        return 0;
-}
-
-static int load_configuration(
-                int argc,
-                char **argv,
-                const struct rlimit *saved_rlimit_nofile,
-                const struct rlimit *saved_rlimit_memlock,
-                const char **ret_error_message) {
-        int r;
-
-        assert(saved_rlimit_nofile);
-        assert(saved_rlimit_memlock);
-        assert(ret_error_message);
-
-        (void) parse_configuration(saved_rlimit_nofile, saved_rlimit_memlock);
-
-        r = parse_argv(argc, argv);
-        if (r < 0) {
-                *ret_error_message = "Failed to parse commandline arguments";
-                return r;
-        }
-
         /* Initialize the show status setting if it hasn't been set explicitly yet */
         if (arg_show_status == _SHOW_STATUS_INVALID)
                 arg_show_status = SHOW_STATUS_YES;
@@ -2321,6 +2428,12 @@ static int initialize_security(
                 return r;
         }
 
+        r = mac_apparmor_setup();
+        if (r < 0) {
+                *ret_error_message = "Failed to load AppArmor policy";
+                return r;
+        }
+
         r = ima_setup();
         if (r < 0) {
                 *ret_error_message = "Failed to load IMA policy";
@@ -2506,7 +2619,7 @@ int main(int argc, char *argv[]) {
                         }
 
                         if (mac_selinux_init() < 0) {
-                                error_message = "Failed to initialize SELinux policy";
+                                error_message = "Failed to initialize SELinux support";
                                 goto finish;
                         }
 
@@ -2527,8 +2640,7 @@ int main(int argc, char *argv[]) {
                         /* For later on, see above... */
                         log_set_target(LOG_TARGET_JOURNAL);
 
-                        /* clear the kernel timestamp,
-                         * because we are in a container */
+                        /* clear the kernel timestamp, because we are in a container */
                         kernel_timestamp = DUAL_TIMESTAMP_NULL;
                 }
 
@@ -2547,9 +2659,13 @@ int main(int argc, char *argv[]) {
                 log_set_target(LOG_TARGET_AUTO);
                 log_open();
 
-                /* clear the kernel timestamp,
-                 * because we are not PID 1 */
+                /* clear the kernel timestamp, because we are not PID 1 */
                 kernel_timestamp = DUAL_TIMESTAMP_NULL;
+
+                if (mac_selinux_init() < 0) {
+                        error_message = "Failed to initialize SELinux support";
+                        goto finish;
+                }
         }
 
         if (arg_system) {
@@ -2562,15 +2678,14 @@ int main(int argc, char *argv[]) {
                         log_warning_errno(r, "Failed to redirect standard streams to /dev/null, ignoring: %m");
         }
 
-        /* Mount /proc, /sys and friends, so that /proc/cmdline and
-         * /proc/$PID/fd is available. */
+        /* Mount /proc, /sys and friends, so that /proc/cmdline and /proc/$PID/fd is available. */
         if (getpid_cached() == 1) {
 
                 /* Load the kernel modules early. */
                 if (!skip_setup)
                         kmod_setup();
 
-                r = mount_setup(loaded_policy);
+                r = mount_setup(loaded_policy, skip_setup);
                 if (r < 0) {
                         error_message = "Failed to mount API filesystems";
                         goto finish;
@@ -2592,15 +2707,19 @@ int main(int argc, char *argv[]) {
         (void) reset_all_signal_handlers();
         (void) ignore_signals(SIGNALS_IGNORE, -1);
 
-        r = load_configuration(argc, argv, &saved_rlimit_nofile, &saved_rlimit_memlock, &error_message);
-        if (r < 0)
+        (void) parse_configuration(&saved_rlimit_nofile, &saved_rlimit_memlock);
+
+        r = parse_argv(argc, argv);
+        if (r < 0) {
+                error_message = "Failed to parse commandline arguments";
                 goto finish;
+        }
 
         r = safety_checks();
         if (r < 0)
                 goto finish;
 
-        if (IN_SET(arg_action, ACTION_TEST, ACTION_HELP, ACTION_DUMP_CONFIGURATION_ITEMS, ACTION_DUMP_BUS_PROPERTIES))
+        if (IN_SET(arg_action, ACTION_TEST, ACTION_HELP, ACTION_DUMP_CONFIGURATION_ITEMS, ACTION_DUMP_BUS_PROPERTIES, ACTION_BUS_INTROSPECT))
                 (void) pager_open(arg_pager_flags);
 
         if (arg_action != ACTION_RUN)
@@ -2620,6 +2739,10 @@ int main(int argc, char *argv[]) {
                 dump_bus_properties(stdout);
                 retval = EXIT_SUCCESS;
                 goto finish;
+        } else if (arg_action == ACTION_BUS_INTROSPECT) {
+                r = bus_manager_introspect_implementations(stdout, arg_bus_introspect);
+                retval = r >= 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+                goto finish;
         }
 
         assert_se(IN_SET(arg_action, ACTION_RUN, ACTION_TEST));
@@ -2628,6 +2751,13 @@ int main(int argc, char *argv[]) {
         assert_se(chdir("/") == 0);
 
         if (arg_action == ACTION_RUN) {
+                if (!skip_setup) {
+                        /* Apply the systemd.clock_usec= kernel command line switch */
+                        apply_clock_update();
+
+                        /* Apply random seed from kernel command line */
+                        cmdline_take_random_seed();
+                }
 
                 /* A core pattern might have been specified via the cmdline.  */
                 initialize_core_pattern(skip_setup);
@@ -2723,8 +2853,8 @@ finish:
         pager_close();
 
         if (m) {
-                arg_reboot_watchdog = m->reboot_watchdog;
-                arg_kexec_watchdog = m->kexec_watchdog;
+                arg_reboot_watchdog = manager_get_watchdog(m, WATCHDOG_REBOOT);
+                arg_kexec_watchdog = manager_get_watchdog(m, WATCHDOG_KEXEC);
                 m = manager_free(m);
         }
 
index 4412e7a849b03588828b3ee353de3cbfe1728b9a..41e0d73736627cd70bcfd165019416da349f0740 100644 (file)
 #include "fd-util.h"
 #include "fileio.h"
 #include "fs-util.h"
+#include "generator-setup.h"
 #include "hashmap.h"
 #include "install.h"
 #include "io-util.h"
 #include "label.h"
 #include "locale-setup.h"
+#include "load-fragment.h"
 #include "log.h"
 #include "macro.h"
 #include "manager.h"
@@ -109,6 +111,7 @@ static int manager_dispatch_sigchld(sd_event_source *source, void *userdata);
 static int manager_dispatch_timezone_change(sd_event_source *source, const struct inotify_event *event, void *userdata);
 static int manager_run_environment_generators(Manager *m);
 static int manager_run_generators(Manager *m);
+static void manager_vacuum(Manager *m);
 
 static usec_t manager_watch_jobs_next_time(Manager *m) {
         return usec_add(now(CLOCK_MONOTONIC),
@@ -180,7 +183,7 @@ static void draw_cylon(char buffer[], size_t buflen, unsigned width, unsigned po
         }
 }
 
-void manager_flip_auto_status(Manager *m, bool enable, const char *reason) {
+static void manager_flip_auto_status(Manager *m, bool enable, const char *reason) {
         assert(m);
 
         if (enable) {
@@ -586,6 +589,8 @@ static char** sanitize_environment(char **l) {
         /* Let's remove some environment variables that we need ourselves to communicate with our clients */
         strv_env_unset_many(
                         l,
+                        "CACHE_DIRECTORY",
+                        "CONFIGURATION_DIRECTORY",
                         "EXIT_CODE",
                         "EXIT_STATUS",
                         "INVOCATION_ID",
@@ -593,13 +598,16 @@ static char** sanitize_environment(char **l) {
                         "LISTEN_FDNAMES",
                         "LISTEN_FDS",
                         "LISTEN_PID",
+                        "LOGS_DIRECTORY",
                         "MAINPID",
                         "MANAGERPID",
                         "NOTIFY_SOCKET",
                         "PIDFILE",
                         "REMOTE_ADDR",
                         "REMOTE_PORT",
+                        "RUNTIME_DIRECTORY",
                         "SERVICE_RESULT",
+                        "STATE_DIRECTORY",
                         "WATCHDOG_PID",
                         "WATCHDOG_USEC",
                         NULL);
@@ -677,19 +685,13 @@ static int manager_setup_prefix(Manager *m) {
                 [EXEC_DIRECTORY_CONFIGURATION] = { SD_PATH_USER_CONFIGURATION, NULL },
         };
 
-        const struct table_entry *p;
-        ExecDirectoryType i;
-        int r;
-
         assert(m);
 
-        if (MANAGER_IS_SYSTEM(m))
-                p = paths_system;
-        else
-                p = paths_user;
+        const struct table_entry *p = MANAGER_IS_SYSTEM(m) ? paths_system : paths_user;
+        int r;
 
-        for (i = 0; i < _EXEC_DIRECTORY_TYPE_MAX; i++) {
-                r = sd_path_home(p[i].type, p[i].suffix, &m->prefix[i]);
+        for (ExecDirectoryType i = 0; i < _EXEC_DIRECTORY_TYPE_MAX; i++) {
+                r = sd_path_lookup(p[i].type, p[i].suffix, &m->prefix[i]);
                 if (r < 0)
                         return r;
         }
@@ -700,7 +702,7 @@ static int manager_setup_prefix(Manager *m) {
 static void manager_free_unit_name_maps(Manager *m) {
         m->unit_id_map = hashmap_free(m->unit_id_map);
         m->unit_name_map = hashmap_free(m->unit_name_map);
-        m->unit_path_cache = set_free_free(m->unit_path_cache);
+        m->unit_path_cache = set_free(m->unit_path_cache);
         m->unit_cache_mtime =  0;
 }
 
@@ -778,6 +780,12 @@ int manager_new(UnitFileScope scope, ManagerTestRunFlags test_run_flags, Manager
                 .original_log_level = -1,
                 .original_log_target = _LOG_TARGET_INVALID,
 
+                .watchdog_overridden[WATCHDOG_RUNTIME] = USEC_INFINITY,
+                .watchdog_overridden[WATCHDOG_REBOOT] = USEC_INFINITY,
+                .watchdog_overridden[WATCHDOG_KEXEC] = USEC_INFINITY,
+
+                .show_status_overridden = _SHOW_STATUS_INVALID,
+
                 .notify_fd = -1,
                 .cgroups_agent_fd = -1,
                 .signal_fd = -1,
@@ -833,10 +841,6 @@ int manager_new(UnitFileScope scope, ManagerTestRunFlags test_run_flags, Manager
         if (r < 0)
                 return r;
 
-        r = hashmap_ensure_allocated(&m->jobs, NULL);
-        if (r < 0)
-                return r;
-
         r = hashmap_ensure_allocated(&m->cgroup_unit, &path_hash_ops);
         if (r < 0)
                 return r;
@@ -1338,15 +1342,12 @@ static void manager_clear_jobs_and_units(Manager *m) {
 }
 
 Manager* manager_free(Manager *m) {
-        ExecDirectoryType dt;
-        UnitType c;
-
         if (!m)
                 return NULL;
 
         manager_clear_jobs_and_units(m);
 
-        for (c = 0; c < _UNIT_TYPE_MAX; c++)
+        for (UnitType c = 0; c < _UNIT_TYPE_MAX; c++)
                 if (unit_vtable[c]->shutdown)
                         unit_vtable[c]->shutdown(m);
 
@@ -1417,22 +1418,20 @@ Manager* manager_free(Manager *m) {
         hashmap_free(m->uid_refs);
         hashmap_free(m->gid_refs);
 
-        for (dt = 0; dt < _EXEC_DIRECTORY_TYPE_MAX; dt++)
+        for (ExecDirectoryType dt = 0; dt < _EXEC_DIRECTORY_TYPE_MAX; dt++)
                 m->prefix[dt] = mfree(m->prefix[dt]);
 
         return mfree(m);
 }
 
 static void manager_enumerate_perpetual(Manager *m) {
-        UnitType c;
-
         assert(m);
 
         if (m->test_run_flags == MANAGER_TEST_RUN_MINIMAL)
                 return;
 
         /* Let's ask every type to load all units from disk/kernel that it might know */
-        for (c = 0; c < _UNIT_TYPE_MAX; c++) {
+        for (UnitType c = 0; c < _UNIT_TYPE_MAX; c++) {
                 if (!unit_type_supported(c)) {
                         log_debug("Unit type .%s is not supported on this system.", unit_type_to_string(c));
                         continue;
@@ -1444,15 +1443,13 @@ static void manager_enumerate_perpetual(Manager *m) {
 }
 
 static void manager_enumerate(Manager *m) {
-        UnitType c;
-
         assert(m);
 
         if (m->test_run_flags == MANAGER_TEST_RUN_MINIMAL)
                 return;
 
         /* Let's ask every type to load all units from disk/kernel that it might know */
-        for (c = 0; c < _UNIT_TYPE_MAX; c++) {
+        for (UnitType c = 0; c < _UNIT_TYPE_MAX; c++) {
                 if (!unit_type_supported(c)) {
                         log_debug("Unit type .%s is not supported on this system.", unit_type_to_string(c));
                         continue;
@@ -1595,20 +1592,6 @@ static void manager_preset_all(Manager *m) {
                 log_info("Populated /etc with preset unit settings.");
 }
 
-static void manager_vacuum(Manager *m) {
-        assert(m);
-
-        /* Release any dynamic users no longer referenced */
-        dynamic_user_vacuum(m, true);
-
-        /* Release any references to UIDs/GIDs no longer referenced, and destroy any IPC owned by them */
-        manager_vacuum_uid_refs(m);
-        manager_vacuum_gid_refs(m);
-
-        /* Release any runtimes no longer referenced */
-        exec_runtime_vacuum(m);
-}
-
 static void manager_ready(Manager *m) {
         assert(m);
 
@@ -1954,6 +1937,21 @@ unsigned manager_dispatch_load_queue(Manager *m) {
         return n;
 }
 
+bool manager_unit_file_maybe_loadable_from_cache(Unit *u) {
+        assert(u);
+
+        if (u->load_state != UNIT_NOT_FOUND)
+                return false;
+
+        if (u->manager->unit_cache_mtime == 0)
+                return false;
+
+        if (u->manager->unit_cache_mtime > u->fragment_loadtime)
+                return true;
+
+        return !lookup_paths_mtime_good(&u->manager->lookup_paths, u->manager->unit_cache_mtime);
+}
+
 int manager_load_unit_prepare(
                 Manager *m,
                 const char *name,
@@ -1994,18 +1992,31 @@ int manager_load_unit_prepare(
 
         ret = manager_get_unit(m, name);
         if (ret) {
-                *_ret = ret;
-                return 1;
+                /* The time-based cache allows to start new units without daemon-reload,
+                 * but if they are already referenced (because of dependencies or ordering)
+                 * then we have to force a load of the fragment. As an optimization, check
+                 * first if anything in the usual paths was modified since the last time
+                 * the cache was loaded. Also check if the last time an attempt to load the
+                 * unit was made was before the most recent cache refresh, so that we know
+                 * we need to try again - even if the cache is current, it might have been
+                 * updated in a different context before we had a chance to retry loading
+                 * this particular unit. */
+                if (manager_unit_file_maybe_loadable_from_cache(ret))
+                        ret->load_state = UNIT_STUB;
+                else {
+                        *_ret = ret;
+                        return 1;
+                }
+        } else {
+                ret = cleanup_ret = unit_new(m, unit_vtable[t]->object_size);
+                if (!ret)
+                        return -ENOMEM;
         }
 
-        ret = cleanup_ret = unit_new(m, unit_vtable[t]->object_size);
-        if (!ret)
-                return -ENOMEM;
-
         if (path) {
-                ret->fragment_path = strdup(path);
-                if (!ret->fragment_path)
-                        return -ENOMEM;
+                r = free_and_strdup(&ret->fragment_path, path);
+                if (r < 0)
+                        return r;
         }
 
         r = unit_add_name(ret, name);
@@ -2287,37 +2298,48 @@ static int manager_dispatch_cgroups_agent_fd(sd_event_source *source, int fd, ui
         return 0;
 }
 
+static bool manager_process_barrier_fd(char * const *tags, FDSet *fds) {
+
+        /* nothing else must be sent when using BARRIER=1 */
+        if (strv_contains(tags, "BARRIER=1")) {
+                if (strv_length(tags) == 1) {
+                        if (fdset_size(fds) != 1)
+                                log_warning("Got incorrect number of fds with BARRIER=1, closing them.");
+                } else
+                        log_warning("Extra notification messages sent with BARRIER=1, ignoring everything.");
+
+                /* Drop the message if BARRIER=1 was found */
+                return true;
+        }
+
+        return false;
+}
+
 static void manager_invoke_notify_message(
                 Manager *m,
                 Unit *u,
                 const struct ucred *ucred,
-                const char *buf,
+                char * const *tags,
                 FDSet *fds) {
 
         assert(m);
         assert(u);
         assert(ucred);
-        assert(buf);
+        assert(tags);
 
         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;
-
-                tags = strv_split(buf, NEWLINE);
-                if (!tags) {
-                        log_oom();
-                        return;
-                }
-
+        if (UNIT_VTABLE(u)->notify_message)
                 UNIT_VTABLE(u)->notify_message(u, ucred, tags, fds);
 
-        else if (DEBUG_LOGGING) {
-                _cleanup_free_ char *x = NULL, *y = NULL;
+        else if (DEBUG_LOGGING) {
+                _cleanup_free_ char *buf = NULL, *x = NULL, *y = NULL;
 
-                x = ellipsize(buf, 20, 90);
+                buf = strv_join(tags, ", ");
+                if (buf)
+                        x = ellipsize(buf, 20, 90);
                 if (x)
                         y = cescape(x);
 
@@ -2334,11 +2356,8 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t
                 .iov_base = buf,
                 .iov_len = sizeof(buf)-1,
         };
-        union {
-                struct cmsghdr cmsghdr;
-                uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) +
-                            CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX)];
-        } control = {};
+        CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred)) +
+                         CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX)) control;
         struct msghdr msghdr = {
                 .msg_iov = &iovec,
                 .msg_iovlen = 1,
@@ -2349,6 +2368,7 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t
         struct cmsghdr *cmsg;
         struct ucred *ucred = NULL;
         _cleanup_free_ Unit **array_copy = NULL;
+        _cleanup_strv_free_ char **tags = NULL;
         Unit *u1, *u2, **array;
         int r, *fd_array = NULL;
         size_t n_fds = 0;
@@ -2417,8 +2437,17 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t
                 return 0;
         }
 
-        /* Make sure it's NUL-terminated. */
+        /* Make sure it's NUL-terminated, then parse it to obtain the tags list */
         buf[n] = 0;
+        tags = strv_split_newlines(buf);
+        if (!tags) {
+                log_oom();
+                return 0;
+        }
+
+        /* possibly a barrier fd, let's see */
+        if (manager_process_barrier_fd(tags, fds))
+                return 0;
 
         /* Increase the generation counter used for filtering out duplicate unit invocations. */
         m->notifygen++;
@@ -2440,16 +2469,16 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t
         /* 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);
+                manager_invoke_notify_message(m, u1, ucred, tags, fds);
                 found = true;
         }
         if (u2) {
-                manager_invoke_notify_message(m, u2, ucred, buf, fds);
+                manager_invoke_notify_message(m, u2, ucred, tags, 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);
+                        manager_invoke_notify_message(m, array_copy[i], ucred, tags, fds);
                         found = true;
                 }
 
@@ -2750,11 +2779,11 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
                 switch (sfsi.ssi_signo - SIGRTMIN) {
 
                 case 20:
-                        manager_set_show_status(m, SHOW_STATUS_YES, "signal");
+                        manager_override_show_status(m, SHOW_STATUS_YES, "signal");
                         break;
 
                 case 21:
-                        manager_set_show_status(m, SHOW_STATUS_NO, "signal");
+                        manager_override_show_status(m, SHOW_STATUS_NO, "signal");
                         break;
 
                 case 22:
@@ -2903,9 +2932,10 @@ int manager_loop(Manager *m) {
                 return log_error_errno(r, "Failed to enable SIGCHLD event source: %m");
 
         while (m->objective == MANAGER_OK) {
-                usec_t wait_usec;
+                usec_t wait_usec, watchdog_usec;
 
-                if (timestamp_is_set(m->runtime_watchdog) && MANAGER_IS_SYSTEM(m))
+                watchdog_usec = manager_get_watchdog(m, WATCHDOG_RUNTIME);
+                if (timestamp_is_set(watchdog_usec))
                         watchdog_ping();
 
                 if (!ratelimit_below(&rl)) {
@@ -2935,12 +2965,10 @@ int manager_loop(Manager *m) {
                 if (manager_dispatch_dbus_queue(m) > 0)
                         continue;
 
-                /* Sleep for half the watchdog time */
-                if (timestamp_is_set(m->runtime_watchdog) && MANAGER_IS_SYSTEM(m)) {
-                        wait_usec = m->runtime_watchdog / 2;
-                        if (wait_usec <= 0)
-                                wait_usec = 1;
-                } else
+                /* Sleep for watchdog runtime wait time */
+                if (timestamp_is_set(watchdog_usec))
+                        wait_usec = watchdog_runtime_wait();
+                else
                         wait_usec = USEC_INFINITY;
 
                 r = sd_event_run(m->event, wait_usec);
@@ -3111,7 +3139,7 @@ void manager_send_unit_plymouth(Manager *m, Unit *u) {
 }
 
 int manager_open_serialization(Manager *m, FILE **_f) {
-        int fd;
+        _cleanup_close_ int fd = -1;
         FILE *f;
 
         assert(_f);
@@ -3120,11 +3148,9 @@ int manager_open_serialization(Manager *m, FILE **_f) {
         if (fd < 0)
                 return fd;
 
-        f = fdopen(fd, "w+");
-        if (!f) {
-                safe_close(fd);
+        f = take_fdopen(&fd, "w+");
+        if (!f)
                 return -errno;
-        }
 
         *_f = f;
         return 0;
@@ -3143,6 +3169,47 @@ static bool manager_timestamp_shall_serialize(ManagerTimestamp t) {
                        MANAGER_TIMESTAMP_UNITS_LOAD_START, MANAGER_TIMESTAMP_UNITS_LOAD_FINISH);
 }
 
+#define DESTROY_IPC_FLAG (UINT32_C(1) << 31)
+
+static void manager_serialize_uid_refs_internal(
+                Manager *m,
+                FILE *f,
+                Hashmap **uid_refs,
+                const char *field_name) {
+
+        Iterator i;
+        void *p, *k;
+
+        assert(m);
+        assert(f);
+        assert(uid_refs);
+        assert(field_name);
+
+        /* Serialize the UID reference table. Or actually, just the IPC destruction flag of it, as
+         * the actual counter of it is better rebuild after a reload/reexec. */
+
+        HASHMAP_FOREACH_KEY(p, k, *uid_refs, i) {
+                uint32_t c;
+                uid_t uid;
+
+                uid = PTR_TO_UID(k);
+                c = PTR_TO_UINT32(p);
+
+                if (!(c & DESTROY_IPC_FLAG))
+                        continue;
+
+                (void) serialize_item_format(f, field_name, UID_FMT, uid);
+        }
+}
+
+static void manager_serialize_uid_refs(Manager *m, FILE *f) {
+        manager_serialize_uid_refs_internal(m, f, &m->uid_refs, "destroy-ipc-uid");
+}
+
+static void manager_serialize_gid_refs(Manager *m, FILE *f) {
+        manager_serialize_uid_refs_internal(m, f, &m->gid_refs, "destroy-ipc-gid");
+}
+
 int manager_serialize(
                 Manager *m,
                 FILE *f,
@@ -3172,15 +3239,19 @@ int manager_serialize(
         /* After switching root, udevd has not been started yet. So, enumeration results should not be emitted. */
         (void) serialize_bool(f, "honor-device-enumeration", !switching_root);
 
-        t = show_status_to_string(m->show_status);
-        if (t)
-                (void) serialize_item(f, "show-status", t);
+        if (m->show_status_overridden != _SHOW_STATUS_INVALID)
+                (void) serialize_item(f, "show-status-overridden",
+                                      show_status_to_string(m->show_status_overridden));
 
         if (m->log_level_overridden)
                 (void) serialize_item_format(f, "log-level-override", "%i", log_get_max_level());
         if (m->log_target_overridden)
                 (void) serialize_item(f, "log-target-override", log_target_to_string(log_get_target()));
 
+        (void) serialize_usec(f, "runtime-watchdog-overridden", m->watchdog_overridden[WATCHDOG_RUNTIME]);
+        (void) serialize_usec(f, "reboot-watchdog-overridden", m->watchdog_overridden[WATCHDOG_REBOOT]);
+        (void) serialize_usec(f, "kexec-watchdog-overridden", m->watchdog_overridden[WATCHDOG_KEXEC]);
+
         for (q = 0; q < _MANAGER_TIMESTAMP_MAX; q++) {
                 _cleanup_free_ char *joined = NULL;
 
@@ -3313,6 +3384,114 @@ static int manager_deserialize_units(Manager *m, FILE *f, FDSet *fds) {
         return 0;
 }
 
+usec_t manager_get_watchdog(Manager *m, WatchdogType t) {
+        assert(m);
+
+        if (MANAGER_IS_USER(m))
+                return USEC_INFINITY;
+
+        if (timestamp_is_set(m->watchdog_overridden[t]))
+                return m->watchdog_overridden[t];
+
+        return m->watchdog[t];
+}
+
+void manager_set_watchdog(Manager *m, WatchdogType t, usec_t timeout) {
+        int r = 0;
+
+        assert(m);
+
+        if (MANAGER_IS_USER(m))
+                return;
+
+        if (m->watchdog[t] == timeout)
+                return;
+
+        if (t == WATCHDOG_RUNTIME)
+                if (!timestamp_is_set(m->watchdog_overridden[WATCHDOG_RUNTIME])) {
+                        if (timestamp_is_set(timeout))
+                                r = watchdog_set_timeout(&timeout);
+                        else
+                                watchdog_close(true);
+                }
+
+        if (r >= 0)
+                m->watchdog[t] = timeout;
+}
+
+int manager_override_watchdog(Manager *m, WatchdogType t, usec_t timeout) {
+        int r = 0;
+
+        assert(m);
+
+        if (MANAGER_IS_USER(m))
+                return 0;
+
+        if (m->watchdog_overridden[t] == timeout)
+                return 0;
+
+        if (t == WATCHDOG_RUNTIME) {
+                usec_t *p;
+
+                p = timestamp_is_set(timeout) ? &timeout : &m->watchdog[t];
+                if (timestamp_is_set(*p))
+                        r = watchdog_set_timeout(p);
+                else
+                        watchdog_close(true);
+        }
+
+        if (r >= 0)
+                m->watchdog_overridden[t] = timeout;
+
+        return 0;
+}
+
+static void manager_deserialize_uid_refs_one_internal(
+                Manager *m,
+                Hashmap** uid_refs,
+                const char *value) {
+
+        uid_t uid;
+        uint32_t c;
+        int r;
+
+        assert(m);
+        assert(uid_refs);
+        assert(value);
+
+        r = parse_uid(value, &uid);
+        if (r < 0 || uid == 0) {
+                log_debug("Unable to parse UID reference serialization: " UID_FMT, uid);
+                return;
+        }
+
+        r = hashmap_ensure_allocated(uid_refs, &trivial_hash_ops);
+        if (r < 0) {
+                log_oom();
+                return;
+        }
+
+        c = PTR_TO_UINT32(hashmap_get(*uid_refs, UID_TO_PTR(uid)));
+        if (c & DESTROY_IPC_FLAG)
+                return;
+
+        c |= DESTROY_IPC_FLAG;
+
+        r = hashmap_replace(*uid_refs, UID_TO_PTR(uid), UINT32_TO_PTR(c));
+        if (r < 0) {
+                log_debug_errno(r, "Failed to add UID reference entry: %m");
+                return;
+        }
+}
+
+static void manager_deserialize_uid_refs_one(Manager *m, const char *value) {
+        manager_deserialize_uid_refs_one_internal(m, &m->uid_refs, value);
+}
+
+static void manager_deserialize_gid_refs_one(Manager *m, const char *value) {
+        manager_deserialize_uid_refs_one_internal(m, &m->gid_refs, value);
+}
+
 int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
         int r = 0;
 
@@ -3409,14 +3588,14 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
                         else
                                 m->honor_device_enumeration = b;
 
-                } else if ((val = startswith(l, "show-status="))) {
+                } else if ((val = startswith(l, "show-status-overridden="))) {
                         ShowStatus s;
 
                         s = show_status_from_string(val);
                         if (s < 0)
-                                log_notice("Failed to parse show-status flag '%s', ignoring.", val);
+                                log_notice("Failed to parse show-status-overridden flag '%s', ignoring.", val);
                         else
-                                manager_set_show_status(m, s, "deserialization");
+                                manager_override_show_status(m, s, "deserialize");
 
                 } else if ((val = startswith(l, "log-level-override="))) {
                         int level;
@@ -3436,6 +3615,30 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
                         else
                                 manager_override_log_target(m, target);
 
+                } else if ((val = startswith(l, "runtime-watchdog-overridden="))) {
+                        usec_t t;
+
+                        if (deserialize_usec(val, &t) < 0)
+                                log_notice("Failed to parse runtime-watchdog-overridden value '%s', ignoring.", val);
+                        else
+                                manager_override_watchdog(m, WATCHDOG_RUNTIME, t);
+
+                } else if ((val = startswith(l, "reboot-watchdog-overridden="))) {
+                        usec_t t;
+
+                        if (deserialize_usec(val, &t) < 0)
+                                log_notice("Failed to parse reboot-watchdog-overridden value '%s', ignoring.", val);
+                        else
+                                manager_override_watchdog(m, WATCHDOG_REBOOT, t);
+
+                } else if ((val = startswith(l, "kexec-watchdog-overridden="))) {
+                        usec_t t;
+
+                        if (deserialize_usec(val, &t) < 0)
+                                log_notice("Failed to parse kexec-watchdog-overridden value '%s', ignoring.", val);
+                        else
+                                manager_override_watchdog(m, WATCHDOG_KEXEC, t);
+
                 } else if (startswith(l, "env=")) {
                         r = deserialize_environment(l + 4, &m->client_environment);
                         if (r < 0)
@@ -3487,7 +3690,7 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
                 else if ((val = startswith(l, "destroy-ipc-gid=")))
                         manager_deserialize_gid_refs_one(m, val);
                 else if ((val = startswith(l, "exec-runtime=")))
-                        exec_runtime_deserialize_one(m, val, fds);
+                        (void) exec_runtime_deserialize_one(m, val, fds);
                 else if ((val = startswith(l, "subscribed="))) {
 
                         if (strv_extend(&m->deserialized_subscribed, val) < 0)
@@ -3792,6 +3995,11 @@ void manager_check_finished(Manager *m) {
                 return;
         }
 
+        /* The jobs hashmap tends to grow a lot during boot, and then it's not reused until shutdown. Let's
+           kill the hashmap if it is relatively large. */
+        if (hashmap_buckets(m->jobs) > hashmap_size(m->units) / 10)
+                m->jobs = hashmap_free(m->jobs);
+
         manager_flip_auto_status(m, false, "boot finished");
 
         /* Notify Type=idle units that we are done now */
@@ -3831,25 +4039,9 @@ static bool generator_path_any(const char* const* paths) {
         return found;
 }
 
-static const char *const system_env_generator_binary_paths[] = {
-        "/run/systemd/system-environment-generators",
-        "/etc/systemd/system-environment-generators",
-        "/usr/local/lib/systemd/system-environment-generators",
-        SYSTEM_ENV_GENERATOR_PATH,
-        NULL
-};
-
-static const char *const user_env_generator_binary_paths[] = {
-        "/run/systemd/user-environment-generators",
-        "/etc/systemd/user-environment-generators",
-        "/usr/local/lib/systemd/user-environment-generators",
-        USER_ENV_GENERATOR_PATH,
-        NULL
-};
-
 static int manager_run_environment_generators(Manager *m) {
         char **tmp = NULL; /* this is only used in the forked process, no cleanup here */
-        const char *const *paths;
+        _cleanup_strv_free_ char **paths = NULL;
         void* args[] = {
                 [STDOUT_GENERATE] = &tmp,
                 [STDOUT_COLLECT] = &tmp,
@@ -3860,13 +4052,15 @@ static int manager_run_environment_generators(Manager *m) {
         if (MANAGER_IS_TEST_RUN(m) && !(m->test_run_flags & MANAGER_TEST_RUN_ENV_GENERATORS))
                 return 0;
 
-        paths = MANAGER_IS_SYSTEM(m) ? system_env_generator_binary_paths : user_env_generator_binary_paths;
+        paths = env_generator_binary_paths(MANAGER_IS_SYSTEM(m));
+        if (!paths)
+                return log_oom();
 
-        if (!generator_path_any(paths))
+        if (!generator_path_any((const char* const*) paths))
                 return 0;
 
         RUN_WITH_UMASK(0022)
-                r = execute_directories(paths, DEFAULT_TIMEOUT_USEC, gather_environment,
+                r = execute_directories((const char* const*) paths, DEFAULT_TIMEOUT_USEC, gather_environment,
                                         args, NULL, m->transient_environment, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS);
         return r;
 }
@@ -4090,49 +4284,78 @@ void manager_recheck_journal(Manager *m) {
         log_open();
 }
 
+static ShowStatus manager_get_show_status(Manager *m) {
+        assert(m);
+
+        if (MANAGER_IS_USER(m))
+                return _SHOW_STATUS_INVALID;
+
+        if (m->show_status_overridden != _SHOW_STATUS_INVALID)
+                return m->show_status_overridden;
+
+        return m->show_status;
+}
+
+bool manager_get_show_status_on(Manager *m) {
+        assert(m);
+
+        return show_status_on(manager_get_show_status(m));
+}
+
+static void set_show_status_marker(bool b) {
+        if (b)
+                (void) touch("/run/systemd/show-status");
+        else
+                (void) unlink("/run/systemd/show-status");
+}
+
 void manager_set_show_status(Manager *m, ShowStatus mode, const char *reason) {
         assert(m);
+        assert(reason);
         assert(mode >= 0 && mode < _SHOW_STATUS_MAX);
 
-        if (!MANAGER_IS_SYSTEM(m))
+        if (MANAGER_IS_USER(m))
                 return;
 
         if (mode == m->show_status)
                 return;
 
-        bool enabled = IN_SET(mode, SHOW_STATUS_TEMPORARY, SHOW_STATUS_YES);
-        log_debug("%s (%s) showing of status (%s).",
-                  enabled ? "Enabling" : "Disabling",
-                  strna(show_status_to_string(mode)),
-                  reason);
-        m->show_status = mode;
+        if (m->show_status_overridden == _SHOW_STATUS_INVALID) {
+                bool enabled;
 
-        if (enabled)
-                (void) touch("/run/systemd/show-status");
-        else
-                (void) unlink("/run/systemd/show-status");
+                enabled = show_status_on(mode);
+                log_debug("%s (%s) showing of status (%s).",
+                          enabled ? "Enabling" : "Disabling",
+                          strna(show_status_to_string(mode)),
+                          reason);
+
+                set_show_status_marker(enabled);
+        }
+
+        m->show_status = mode;
 }
 
-static bool manager_get_show_status(Manager *m, StatusType type) {
+void manager_override_show_status(Manager *m, ShowStatus mode, const char *reason) {
         assert(m);
+        assert(mode < _SHOW_STATUS_MAX);
 
-        if (!MANAGER_IS_SYSTEM(m))
-                return false;
+        if (MANAGER_IS_USER(m))
+                return;
 
-        if (m->no_console_output)
-                return false;
+        if (mode == m->show_status_overridden)
+                return;
 
-        if (!IN_SET(manager_state(m), MANAGER_INITIALIZING, MANAGER_STARTING, MANAGER_STOPPING))
-                return false;
+        m->show_status_overridden = mode;
 
-        /* If we cannot find out the status properly, just proceed. */
-        if (type != STATUS_TYPE_EMERGENCY && manager_check_ask_password(m) > 0)
-                return false;
+        if (mode == _SHOW_STATUS_INVALID)
+                mode = m->show_status;
 
-        if (type == STATUS_TYPE_NOTICE && m->show_status != SHOW_STATUS_NO)
-                return true;
+        log_debug("%s (%s) showing of status (%s).",
+                  m->show_status_overridden != _SHOW_STATUS_INVALID ? "Overriding" : "Restoring",
+                  strna(show_status_to_string(mode)),
+                  reason);
 
-        return show_status_on(m->show_status);
+        set_show_status_marker(show_status_on(mode));
 }
 
 const char *manager_get_confirm_spawn(Manager *m) {
@@ -4207,12 +4430,34 @@ bool manager_is_confirm_spawn_disabled(Manager *m) {
         return access("/run/systemd/confirm_spawn_disabled", F_OK) >= 0;
 }
 
+static bool manager_should_show_status(Manager *m, StatusType type) {
+        assert(m);
+
+        if (!MANAGER_IS_SYSTEM(m))
+                return false;
+
+        if (m->no_console_output)
+                return false;
+
+        if (!IN_SET(manager_state(m), MANAGER_INITIALIZING, MANAGER_STARTING, MANAGER_STOPPING))
+                return false;
+
+        /* If we cannot find out the status properly, just proceed. */
+        if (type != STATUS_TYPE_EMERGENCY && manager_check_ask_password(m) > 0)
+                return false;
+
+        if (type == STATUS_TYPE_NOTICE && m->show_status != SHOW_STATUS_NO)
+                return true;
+
+        return manager_get_show_status_on(m);
+}
+
 void manager_status_printf(Manager *m, StatusType type, const char *status, const char *format, ...) {
         va_list ap;
 
         /* If m is NULL, assume we're after shutdown and let the messages through. */
 
-        if (m && !manager_get_show_status(m, type))
+        if (m && !manager_should_show_status(m, type))
                 return;
 
         /* XXX We should totally drop the check for ephemeral here
@@ -4247,12 +4492,9 @@ int manager_update_failed_units(Manager *m, Unit *u, bool failed) {
         size = set_size(m->failed_units);
 
         if (failed) {
-                r = set_ensure_allocated(&m->failed_units, NULL);
+                r = set_ensure_put(&m->failed_units, NULL, u);
                 if (r < 0)
                         return log_oom();
-
-                if (set_put(m->failed_units, u) < 0)
-                        return log_oom();
         } else
                 (void) set_remove(m->failed_units, u);
 
@@ -4267,6 +4509,11 @@ ManagerState manager_state(Manager *m) {
 
         assert(m);
 
+        /* Is the special shutdown target active or queued? If so, we are in shutdown state */
+        u = manager_get_unit(m, SPECIAL_SHUTDOWN_TARGET);
+        if (u && unit_active_or_pending(u))
+                return MANAGER_STOPPING;
+
         /* Did we ever finish booting? If not then we are still starting up */
         if (!MANAGER_IS_FINISHED(m)) {
 
@@ -4277,11 +4524,6 @@ ManagerState manager_state(Manager *m) {
                 return MANAGER_STARTING;
         }
 
-        /* Is the special shutdown target active or queued? If so, we are in shutdown state */
-        u = manager_get_unit(m, SPECIAL_SHUTDOWN_TARGET);
-        if (u && unit_active_or_pending(u))
-                return MANAGER_STOPPING;
-
         if (MANAGER_IS_SYSTEM(m)) {
                 /* Are the rescue or emergency targets active or queued? If so we are in maintenance state */
                 u = manager_get_unit(m, SPECIAL_RESCUE_TARGET);
@@ -4300,8 +4542,6 @@ ManagerState manager_state(Manager *m) {
         return MANAGER_RUNNING;
 }
 
-#define DESTROY_IPC_FLAG (UINT32_C(1) << 31)
-
 static void manager_unref_uid_internal(
                 Manager *m,
                 Hashmap **uid_refs,
@@ -4440,97 +4680,26 @@ static void manager_vacuum_uid_refs_internal(
         }
 }
 
-void manager_vacuum_uid_refs(Manager *m) {
+static void manager_vacuum_uid_refs(Manager *m) {
         manager_vacuum_uid_refs_internal(m, &m->uid_refs, clean_ipc_by_uid);
 }
 
-void manager_vacuum_gid_refs(Manager *m) {
+static void manager_vacuum_gid_refs(Manager *m) {
         manager_vacuum_uid_refs_internal(m, &m->gid_refs, clean_ipc_by_gid);
 }
 
-static void manager_serialize_uid_refs_internal(
-                Manager *m,
-                FILE *f,
-                Hashmap **uid_refs,
-                const char *field_name) {
-
-        Iterator i;
-        void *p, *k;
-
-        assert(m);
-        assert(f);
-        assert(uid_refs);
-        assert(field_name);
-
-        /* Serialize the UID reference table. Or actually, just the IPC destruction flag of it, as the actual counter
-         * of it is better rebuild after a reload/reexec. */
-
-        HASHMAP_FOREACH_KEY(p, k, *uid_refs, i) {
-                uint32_t c;
-                uid_t uid;
-
-                uid = PTR_TO_UID(k);
-                c = PTR_TO_UINT32(p);
-
-                if (!(c & DESTROY_IPC_FLAG))
-                        continue;
-
-                (void) serialize_item_format(f, field_name, UID_FMT, uid);
-        }
-}
-
-void manager_serialize_uid_refs(Manager *m, FILE *f) {
-        manager_serialize_uid_refs_internal(m, f, &m->uid_refs, "destroy-ipc-uid");
-}
-
-void manager_serialize_gid_refs(Manager *m, FILE *f) {
-        manager_serialize_uid_refs_internal(m, f, &m->gid_refs, "destroy-ipc-gid");
-}
-
-static void manager_deserialize_uid_refs_one_internal(
-                Manager *m,
-                Hashmap** uid_refs,
-                const char *value) {
-
-        uid_t uid;
-        uint32_t c;
-        int r;
-
+static void manager_vacuum(Manager *m) {
         assert(m);
-        assert(uid_refs);
-        assert(value);
-
-        r = parse_uid(value, &uid);
-        if (r < 0 || uid == 0) {
-                log_debug("Unable to parse UID reference serialization: " UID_FMT, uid);
-                return;
-        }
-
-        r = hashmap_ensure_allocated(uid_refs, &trivial_hash_ops);
-        if (r < 0) {
-                log_oom();
-                return;
-        }
-
-        c = PTR_TO_UINT32(hashmap_get(*uid_refs, UID_TO_PTR(uid)));
-        if (c & DESTROY_IPC_FLAG)
-                return;
-
-        c |= DESTROY_IPC_FLAG;
 
-        r = hashmap_replace(*uid_refs, UID_TO_PTR(uid), UINT32_TO_PTR(c));
-        if (r < 0) {
-                log_debug_errno(r, "Failed to add UID reference entry: %m");
-                return;
-        }
-}
+        /* Release any dynamic users no longer referenced */
+        dynamic_user_vacuum(m, true);
 
-void manager_deserialize_uid_refs_one(Manager *m, const char *value) {
-        manager_deserialize_uid_refs_one_internal(m, &m->uid_refs, value);
-}
+        /* Release any references to UIDs/GIDs no longer referenced, and destroy any IPC owned by them */
+        manager_vacuum_uid_refs(m);
+        manager_vacuum_gid_refs(m);
 
-void manager_deserialize_gid_refs_one(Manager *m, const char *value) {
-        manager_deserialize_uid_refs_one_internal(m, &m->gid_refs, value);
+        /* Release any runtimes no longer referenced */
+        exec_runtime_vacuum(m);
 }
 
 int manager_dispatch_user_lookup_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
index 10c34f95433cfcb0eec1f16c4971dc65b77d6281..81b0c13a955a7bcbbb56cc655ce74eaf24215e0f 100644 (file)
@@ -114,6 +114,13 @@ typedef enum ManagerTimestamp {
         _MANAGER_TIMESTAMP_INVALID = -1,
 } ManagerTimestamp;
 
+typedef enum WatchdogType {
+        WATCHDOG_RUNTIME,
+        WATCHDOG_REBOOT,
+        WATCHDOG_KEXEC,
+        _WATCHDOG_TYPE_MAX,
+} WatchdogType;
+
 #include "execute.h"
 #include "job.h"
 #include "path-lookup.h"
@@ -231,9 +238,8 @@ struct Manager {
         char **transient_environment;  /* The environment, as determined from config files, kernel cmdline and environment generators */
         char **client_environment;     /* Environment variables created by clients through the bus API */
 
-        usec_t runtime_watchdog;
-        usec_t reboot_watchdog;
-        usec_t kexec_watchdog;
+        usec_t watchdog[_WATCHDOG_TYPE_MAX];
+        usec_t watchdog_overridden[_WATCHDOG_TYPE_MAX];
 
         dual_timestamp timestamps[_MANAGER_TIMESTAMP_MAX];
 
@@ -329,6 +335,7 @@ struct Manager {
         uint8_t return_value;
 
         ShowStatus show_status;
+        ShowStatus show_status_overridden;
         StatusUnitFormat status_unit_format;
         char *confirm_spawn;
         bool no_console_output;
@@ -456,6 +463,7 @@ Unit *manager_get_unit(Manager *m, const char *name);
 
 int manager_get_job_from_dbus_path(Manager *m, const char *s, Job **_j);
 
+bool manager_unit_file_maybe_loadable_from_cache(Unit *u);
 int manager_load_unit_prepare(Manager *m, const char *name, const char *path, sd_bus_error *e, Unit **_ret);
 int manager_load_unit(Manager *m, const char *name, const char *path, sd_bus_error *e, Unit **_ret);
 int manager_load_startable_unit_or_warn(Manager *m, const char *name, const char *path, Unit **ret);
@@ -506,11 +514,13 @@ void disable_printk_ratelimit(void);
 void manager_recheck_dbus(Manager *m);
 void manager_recheck_journal(Manager *m);
 
+bool manager_get_show_status_on(Manager *m);
 void manager_set_show_status(Manager *m, ShowStatus mode, const char *reason);
+void manager_override_show_status(Manager *m, ShowStatus mode, const char *reason);
+
 void manager_set_first_boot(Manager *m, bool b);
 
 void manager_status_printf(Manager *m, StatusType type, const char *status, const char *format, ...) _printf_(4,5);
-void manager_flip_auto_status(Manager *m, bool enable, const char *reason);
 
 Set *manager_get_units_requiring_mounts_for(Manager *m, const char *path);
 
@@ -524,15 +534,6 @@ int manager_ref_uid(Manager *m, uid_t uid, bool clean_ipc);
 void manager_unref_gid(Manager *m, gid_t gid, bool destroy_now);
 int manager_ref_gid(Manager *m, gid_t gid, bool destroy_now);
 
-void manager_vacuum_uid_refs(Manager *m);
-void manager_vacuum_gid_refs(Manager *m);
-
-void manager_serialize_uid_refs(Manager *m, FILE *f);
-void manager_deserialize_uid_refs_one(Manager *m, const char *value);
-
-void manager_serialize_gid_refs(Manager *m, FILE *f);
-void manager_deserialize_gid_refs_one(Manager *m, const char *value);
-
 char *manager_taint_string(Manager *m);
 
 void manager_ref_console(Manager *m);
@@ -555,5 +556,9 @@ const char *manager_timestamp_to_string(ManagerTimestamp m) _const_;
 ManagerTimestamp manager_timestamp_from_string(const char *s) _pure_;
 ManagerTimestamp manager_timestamp_initrd_mangle(ManagerTimestamp s);
 
+usec_t manager_get_watchdog(Manager *m, WatchdogType t);
+void manager_set_watchdog(Manager *m, WatchdogType t, usec_t timeout);
+int manager_override_watchdog(Manager *m, WatchdogType t, usec_t timeout);
+
 const char* oom_policy_to_string(OOMPolicy i) _const_;
 OOMPolicy oom_policy_from_string(const char *s) _pure_;
index 3586838f59b3b3a4ed3627399158593327b5bb02..fa951085238f7f3684decbf9b5f2417e3eb8f659 100644 (file)
@@ -12,6 +12,8 @@ libcore_shared_sources = '''
 '''.split()
 
 libcore_sources = '''
+        apparmor-setup.c
+        apparmor-setup.h
         audit-fd.c
         audit-fd.h
         automount.c
@@ -72,6 +74,8 @@ libcore_sources = '''
         emergency-action.h
         execute.c
         execute.h
+        generator-setup.c
+        generator-setup.h
         hostname-setup.c
         hostname-setup.h
         ima-setup.c
index 5dfcb6158a434c1317a22cfdacf283ac68dedb59..feb88f3e6e5f5f1c865a2ecad5d613905ed65017 100644 (file)
@@ -23,6 +23,7 @@
 #include "macro.h"
 #include "mkdir.h"
 #include "mount-setup.h"
+#include "mount-util.h"
 #include "mountpoint-util.h"
 #include "nulstr-util.h"
 #include "path-util.h"
@@ -60,51 +61,51 @@ typedef struct MountPoint {
 #endif
 
 static const MountPoint mount_table[] = {
-        { "sysfs",       "/sys",                      "sysfs",      NULL,                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
+        { "sysfs",       "/sys",                      "sysfs",      NULL,                                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
           NULL,          MNT_FATAL|MNT_IN_CONTAINER },
-        { "proc",        "/proc",                     "proc",       NULL,                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
+        { "proc",        "/proc",                     "proc",       NULL,                                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
           NULL,          MNT_FATAL|MNT_IN_CONTAINER },
-        { "devtmpfs",    "/dev",                      "devtmpfs",   "mode=755",                MS_NOSUID|MS_STRICTATIME,
+        { "devtmpfs",    "/dev",                      "devtmpfs",   "mode=755" TMPFS_LIMITS_DEV,               MS_NOSUID|MS_NOEXEC|MS_STRICTATIME,
           NULL,          MNT_FATAL|MNT_IN_CONTAINER },
-        { "securityfs",  "/sys/kernel/security",      "securityfs", NULL,                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
+        { "securityfs",  "/sys/kernel/security",      "securityfs", NULL,                                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
           NULL,          MNT_NONE                   },
 #if ENABLE_SMACK
-        { "smackfs",     "/sys/fs/smackfs",           "smackfs",    "smackfsdef=*",            MS_NOSUID|MS_NOEXEC|MS_NODEV,
+        { "smackfs",     "/sys/fs/smackfs",           "smackfs",    "smackfsdef=*",                            MS_NOSUID|MS_NOEXEC|MS_NODEV,
           mac_smack_use, MNT_FATAL                  },
-        { "tmpfs",       "/dev/shm",                  "tmpfs",      "mode=1777,smackfsroot=*", MS_NOSUID|MS_NODEV|MS_STRICTATIME,
+        { "tmpfs",       "/dev/shm",                  "tmpfs",      "mode=1777,smackfsroot=*",                 MS_NOSUID|MS_NODEV|MS_STRICTATIME,
           mac_smack_use, MNT_FATAL                  },
 #endif
-        { "tmpfs",       "/dev/shm",                  "tmpfs",      "mode=1777",               MS_NOSUID|MS_NODEV|MS_STRICTATIME,
+        { "tmpfs",       "/dev/shm",                  "tmpfs",      "mode=1777",                               MS_NOSUID|MS_NODEV|MS_STRICTATIME,
           NULL,          MNT_FATAL|MNT_IN_CONTAINER },
-        { "devpts",      "/dev/pts",                  "devpts",     "mode=620,gid=" STRINGIFY(TTY_GID), MS_NOSUID|MS_NOEXEC,
+        { "devpts",      "/dev/pts",                  "devpts",     "mode=620,gid=" STRINGIFY(TTY_GID),        MS_NOSUID|MS_NOEXEC,
           NULL,          MNT_IN_CONTAINER           },
 #if ENABLE_SMACK
-        { "tmpfs",       "/run",                      "tmpfs",      "mode=755,smackfsroot=*" MS_NOSUID|MS_NODEV|MS_STRICTATIME,
+        { "tmpfs",       "/run",                      "tmpfs",      "mode=755,smackfsroot=*" TMPFS_LIMITS_RUN, MS_NOSUID|MS_NODEV|MS_STRICTATIME,
           mac_smack_use, MNT_FATAL                  },
 #endif
-        { "tmpfs",       "/run",                      "tmpfs",      "mode=755"               MS_NOSUID|MS_NODEV|MS_STRICTATIME,
+        { "tmpfs",       "/run",                      "tmpfs",      "mode=755" TMPFS_LIMITS_RUN,               MS_NOSUID|MS_NODEV|MS_STRICTATIME,
           NULL,          MNT_FATAL|MNT_IN_CONTAINER },
-        { "cgroup2",     "/sys/fs/cgroup",            "cgroup2",    "nsdelegate",              MS_NOSUID|MS_NOEXEC|MS_NODEV,
+        { "cgroup2",     "/sys/fs/cgroup",            "cgroup2",    "nsdelegate",                              MS_NOSUID|MS_NOEXEC|MS_NODEV,
           cg_is_unified_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE },
-        { "cgroup2",     "/sys/fs/cgroup",            "cgroup2",    NULL,                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
+        { "cgroup2",     "/sys/fs/cgroup",            "cgroup2",    NULL,                                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
           cg_is_unified_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE },
-        { "tmpfs",       "/sys/fs/cgroup",            "tmpfs",      "mode=755",                MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME,
+        { "tmpfs",       "/sys/fs/cgroup",            "tmpfs",      "mode=755" TMPFS_LIMITS_SYS_FS_CGROUP,     MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME,
           cg_is_legacy_wanted, MNT_FATAL|MNT_IN_CONTAINER },
-        { "cgroup2",     "/sys/fs/cgroup/unified",    "cgroup2",    "nsdelegate",              MS_NOSUID|MS_NOEXEC|MS_NODEV,
+        { "cgroup2",     "/sys/fs/cgroup/unified",    "cgroup2",    "nsdelegate",                              MS_NOSUID|MS_NOEXEC|MS_NODEV,
           cg_is_hybrid_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE },
-        { "cgroup2",     "/sys/fs/cgroup/unified",    "cgroup2",    NULL,                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
+        { "cgroup2",     "/sys/fs/cgroup/unified",    "cgroup2",    NULL,                                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
           cg_is_hybrid_wanted, MNT_IN_CONTAINER|MNT_CHECK_WRITABLE },
-        { "cgroup",      "/sys/fs/cgroup/systemd",    "cgroup",     "none,name=systemd,xattr", MS_NOSUID|MS_NOEXEC|MS_NODEV,
+        { "cgroup",      "/sys/fs/cgroup/systemd",    "cgroup",     "none,name=systemd,xattr",                 MS_NOSUID|MS_NOEXEC|MS_NODEV,
           cg_is_legacy_wanted, MNT_IN_CONTAINER     },
-        { "cgroup",      "/sys/fs/cgroup/systemd",    "cgroup",     "none,name=systemd",       MS_NOSUID|MS_NOEXEC|MS_NODEV,
+        { "cgroup",      "/sys/fs/cgroup/systemd",    "cgroup",     "none,name=systemd",                       MS_NOSUID|MS_NOEXEC|MS_NODEV,
           cg_is_legacy_wanted, MNT_FATAL|MNT_IN_CONTAINER },
-        { "pstore",      "/sys/fs/pstore",            "pstore",     NULL,                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
+        { "pstore",      "/sys/fs/pstore",            "pstore",     NULL,                                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
           NULL,          MNT_NONE                   },
 #if ENABLE_EFI
-        { "efivarfs",    "/sys/firmware/efi/efivars", "efivarfs",   NULL,                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
+        { "efivarfs",    "/sys/firmware/efi/efivars", "efivarfs",   NULL,                                      MS_NOSUID|MS_NOEXEC|MS_NODEV,
           is_efi_boot,   MNT_NONE                   },
 #endif
-        { "bpf",         "/sys/fs/bpf",               "bpf",        "mode=700",                MS_NOSUID|MS_NOEXEC|MS_NODEV,
+        { "bpf",         "/sys/fs/bpf",               "bpf",        "mode=700",                                MS_NOSUID|MS_NOEXEC|MS_NODEV,
           NULL,          MNT_NONE,                  },
 };
 
@@ -352,7 +353,7 @@ int mount_cgroup_controllers(void) {
         }
 
         /* Now that we mounted everything, let's make the tmpfs the cgroup file systems are mounted into read-only. */
-        (void) mount("tmpfs", "/sys/fs/cgroup", "tmpfs", MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755");
+        (void) mount("tmpfs", "/sys/fs/cgroup", "tmpfs", MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755" TMPFS_LIMITS_SYS_FS_CGROUP);
 
         return 0;
 }
@@ -478,7 +479,7 @@ static int relabel_extra(void) {
 }
 #endif
 
-int mount_setup(bool loaded_policy) {
+int mount_setup(bool loaded_policy, bool leave_propagation) {
         int r = 0;
 
         r = mount_points_setup(ELEMENTSOF(mount_table), loaded_policy);
@@ -524,7 +525,7 @@ int mount_setup(bool loaded_policy) {
          * needed. Note that we set this only when we are invoked directly by the kernel. If we are invoked by a
          * container manager we assume the container manager knows what it is doing (for example, because it set up
          * some directories with different propagation modes). */
-        if (detect_container() <= 0)
+        if (detect_container() <= 0 && !leave_propagation)
                 if (mount(NULL, "/", NULL, MS_REC|MS_SHARED, NULL) < 0)
                         log_warning_errno(errno, "Failed to set up the root directory for shared mount propagation: %m");
 
@@ -534,9 +535,9 @@ int mount_setup(bool loaded_policy) {
         (void) mkdir_label("/run/systemd", 0755);
         (void) mkdir_label("/run/systemd/system", 0755);
 
-        /* Also create /run/systemd/inaccessible nodes, so that we always have something to mount inaccessible nodes
-         * from. */
-        (void) make_inaccessible_nodes("/run/systemd", UID_INVALID, GID_INVALID);
+        /* Also create /run/systemd/inaccessible nodes, so that we always have something to mount
+         * inaccessible nodes from. */
+        (void) make_inaccessible_nodes(NULL, UID_INVALID, GID_INVALID);
 
         return 0;
 }
index b4ca2cf4b45bf28de8bd124d7746ac51e06c73f7..bccd094961152c996d38acbc7111399723eadcbc 100644 (file)
@@ -4,7 +4,7 @@
 #include <stdbool.h>
 
 int mount_setup_early(void);
-int mount_setup(bool loaded_policy);
+int mount_setup(bool loaded_policy, bool leave_propagation);
 
 int mount_cgroup_controllers(void);
 
index 38024d1d284f5300e42f04e5a8cdfe03ee689076..337e94e90e40fab47ab4a166db38f9ae730098ee 100644 (file)
@@ -780,6 +780,7 @@ static void mount_dump(Unit *u, FILE *f, const char *prefix) {
                 "%sSloppyOptions: %s\n"
                 "%sLazyUnmount: %s\n"
                 "%sForceUnmount: %s\n"
+                "%sReadWriteOnly: %s\n"
                 "%sTimeoutSec: %s\n",
                 prefix, mount_state_to_string(m->state),
                 prefix, mount_result_to_string(m->result),
@@ -795,6 +796,7 @@ static void mount_dump(Unit *u, FILE *f, const char *prefix) {
                 prefix, yes_no(m->sloppy_options),
                 prefix, yes_no(m->lazy_unmount),
                 prefix, yes_no(m->force_unmount),
+                prefix, yes_no(m->read_write_only),
                 prefix, format_timespan(buf, sizeof(buf), m->timeout_usec, USEC_PER_SEC));
 
         if (m->control_pid > 0)
@@ -861,6 +863,8 @@ static void mount_enter_dead(Mount *m, MountResult f) {
                 m->result = f;
 
         unit_log_result(UNIT(m), m->result == MOUNT_SUCCESS, mount_result_to_string(m->result));
+        unit_warn_leftover_processes(UNIT(m), unit_log_leftover_process_stop);
+
         mount_set_state(m, m->result != MOUNT_SUCCESS ? MOUNT_FAILED : MOUNT_DEAD);
 
         m->exec_runtime = exec_runtime_unref(m->exec_runtime, true);
@@ -1006,15 +1010,18 @@ static void mount_enter_mounting(Mount *m) {
         (void) mkdir_p_label(m->where, m->directory_mode);
 
         unit_warn_if_dir_nonempty(UNIT(m), m->where);
-        unit_warn_leftover_processes(UNIT(m));
+        unit_warn_leftover_processes(UNIT(m), unit_log_leftover_process_start);
 
         m->control_command_id = MOUNT_EXEC_MOUNT;
         m->control_command = m->exec_command + MOUNT_EXEC_MOUNT;
 
         /* Create the source directory for bind-mounts if needed */
         p = get_mount_parameters_fragment(m);
-        if (p && mount_is_bind(p))
-                (void) mkdir_p_label(p->what, m->directory_mode);
+        if (p && mount_is_bind(p)) {
+                r = mkdir_p_label(p->what, m->directory_mode);
+                if (r < 0)
+                        log_unit_error_errno(UNIT(m), r, "Failed to make bind mount source '%s': %m", p->what);
+        }
 
         if (p) {
                 _cleanup_free_ char *opts = NULL;
@@ -1026,6 +1033,8 @@ static void mount_enter_mounting(Mount *m) {
                 r = exec_command_set(m->control_command, MOUNT_PATH, p->what, m->where, NULL);
                 if (r >= 0 && m->sloppy_options)
                         r = exec_command_append(m->control_command, "-s", NULL);
+                if (r >= 0 && m->read_write_only)
+                        r = exec_command_append(m->control_command, "-w", NULL);
                 if (r >= 0 && p->fstype)
                         r = exec_command_append(m->control_command, "-t", p->fstype, NULL);
                 if (r >= 0 && !isempty(opts))
@@ -1086,6 +1095,8 @@ static void mount_enter_remounting(Mount *m) {
                                      "-o", o, NULL);
                 if (r >= 0 && m->sloppy_options)
                         r = exec_command_append(m->control_command, "-s", NULL);
+                if (r >= 0 && m->read_write_only)
+                        r = exec_command_append(m->control_command, "-w", NULL);
                 if (r >= 0 && p->fstype)
                         r = exec_command_append(m->control_command, "-t", p->fstype, NULL);
         } else
@@ -1666,9 +1677,30 @@ static int mount_setup_unit(
         if (!is_path(where))
                 return 0;
 
+        /* Mount unit names have to be (like all other unit names) short enough to fit into file names. This
+         * means there's a good chance that overly long mount point paths after mangling them to look like a
+         * unit name would result in unit names we don't actually consider valid. This should be OK however
+         * as such long mount point paths should not happen on regular systems — and if they appear
+         * nonetheless they are generally synthesized by software, and thus managed by that other
+         * software. Having such long names just means you cannot use systemd to manage those specific mount
+         * points, which should be an OK restriction to make. After all we don't have to be able to manage
+         * all mount points in the world — as long as we don't choke on them when we encounter them. */
         r = unit_name_from_path(where, ".mount", &e);
-        if (r < 0)
-                return log_error_errno(r, "Failed to generate unit name from path '%s': %m", where);
+        if (r < 0) {
+                static RateLimit rate_limit = { /* Let's log about this at warning level at most once every
+                                                 * 5s. Given that we generate this whenever we read the file
+                                                 * otherwise we probably shouldn't flood the logs with
+                                                 * this */
+                        .interval = 5 * USEC_PER_SEC,
+                        .burst = 1,
+                };
+
+                return log_struct_errno(
+                                ratelimit_below(&rate_limit) ? LOG_WARNING : LOG_DEBUG, r,
+                                "MESSAGE_ID=" SD_MESSAGE_MOUNT_POINT_PATH_NOT_SUITABLE_STR,
+                                "MOUNT_POINT=%s", where,
+                                LOG_MESSAGE("Failed to generate valid unit name from path '%s', ignoring mount point: %m", where));
+        }
 
         u = manager_get_unit(m, e);
         if (u)
@@ -1678,7 +1710,7 @@ static int mount_setup_unit(
                  * by the sysadmin having called mount(8) directly. */
                 r = mount_setup_new_unit(m, e, what, where, options, fstype, &flags, &u);
         if (r < 0)
-                return log_warning_errno(r, "Failed to set up mount unit: %m");
+                return log_warning_errno(r, "Failed to set up mount unit for '%s': %m", where);
 
         /* If the mount changed properties or state, let's notify our clients */
         if (flags & (MOUNT_PROC_JUST_CHANGED|MOUNT_PROC_JUST_MOUNTED))
@@ -1906,7 +1938,7 @@ static int mount_process_proc_self_mountinfo(Manager *m) {
 
                                 /* Remember that this device might just have disappeared */
                                 if (set_ensure_allocated(&gone, &path_hash_ops) < 0 ||
-                                    set_put_strdup(gone, mount->parameters_proc_self_mountinfo.what) < 0)
+                                    set_put_strdup(&gone, mount->parameters_proc_self_mountinfo.what) < 0)
                                         log_oom(); /* we don't care too much about OOM here... */
                         }
 
@@ -1961,7 +1993,7 @@ static int mount_process_proc_self_mountinfo(Manager *m) {
                         /* Track devices currently used */
 
                         if (set_ensure_allocated(&around, &path_hash_ops) < 0 ||
-                            set_put_strdup(around, mount->parameters_proc_self_mountinfo.what) < 0)
+                            set_put_strdup(&around, mount->parameters_proc_self_mountinfo.what) < 0)
                                 log_oom();
                 }
 
@@ -2138,7 +2170,6 @@ const UnitVTable mount_vtable = {
 
         .control_pid = mount_control_pid,
 
-        .bus_vtable = bus_mount_vtable,
         .bus_set_property = bus_mount_set_property,
         .bus_commit_properties = bus_mount_commit_properties,
 
index 07fa05f3ca69635a82d4c4d5c323c58dc643a1de..a1bc2d71a64db0ac07bb288d3ab1bdefdfedd992 100644 (file)
@@ -59,6 +59,8 @@ struct Mount {
         bool lazy_unmount;
         bool force_unmount;
 
+        bool read_write_only;
+
         MountResult result;
         MountResult reload_result;
         MountResult clean_result;
index 90909a07a5bd166e2cc5bb29daea26c98def351d..36d5ff67aed6db4f4add24dbb5b05bc1816179df 100644 (file)
@@ -43,6 +43,7 @@ typedef enum MountMode {
         BIND_MOUNT,
         BIND_MOUNT_RECURSIVE,
         PRIVATE_TMP,
+        PRIVATE_TMP_READONLY,
         PRIVATE_DEV,
         BIND_DEV,
         EMPTY_DIR,
@@ -130,9 +131,9 @@ static const MountEntry protect_home_read_only_table[] = {
 
 /* ProtectHome=tmpfs table */
 static const MountEntry protect_home_tmpfs_table[] = {
-        { "/home",               TMPFS,        true, .read_only = true, .options_const = "mode=0755", .flags = MS_NODEV|MS_STRICTATIME },
-        { "/run/user",           TMPFS,        true, .read_only = true, .options_const = "mode=0755", .flags = MS_NODEV|MS_STRICTATIME },
-        { "/root",               TMPFS,        true, .read_only = true, .options_const = "mode=0700", .flags = MS_NODEV|MS_STRICTATIME },
+        { "/home",               TMPFS,        true, .read_only = true, .options_const = "mode=0755" TMPFS_LIMITS_EMPTY_OR_ALMOST, .flags = MS_NODEV|MS_STRICTATIME },
+        { "/run/user",           TMPFS,        true, .read_only = true, .options_const = "mode=0755" TMPFS_LIMITS_EMPTY_OR_ALMOST, .flags = MS_NODEV|MS_STRICTATIME },
+        { "/root",               TMPFS,        true, .read_only = true, .options_const = "mode=0700" TMPFS_LIMITS_EMPTY_OR_ALMOST, .flags = MS_NODEV|MS_STRICTATIME },
 };
 
 /* ProtectHome=yes table */
@@ -221,7 +222,7 @@ static const char *mount_entry_path(const MountEntry *p) {
 static bool mount_entry_read_only(const MountEntry *p) {
         assert(p);
 
-        return p->read_only || IN_SET(p->mode, READONLY, INACCESSIBLE);
+        return p->read_only || IN_SET(p->mode, READONLY, INACCESSIBLE, PRIVATE_TMP_READONLY);
 }
 
 static const char *mount_entry_source(const MountEntry *p) {
@@ -295,7 +296,7 @@ static int append_empty_dir_mounts(MountEntry **p, char **strv) {
                         .mode = EMPTY_DIR,
                         .ignore = false,
                         .read_only = true,
-                        .options_const = "mode=755",
+                        .options_const = "mode=755" TMPFS_LIMITS_EMPTY_OR_ALMOST,
                         .flags = MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME,
                 };
         }
@@ -325,23 +326,21 @@ static int append_bind_mounts(MountEntry **p, const BindMount *binds, size_t n)
 }
 
 static int append_tmpfs_mounts(MountEntry **p, const TemporaryFileSystem *tmpfs, size_t n) {
-        size_t i;
-        int r;
-
         assert(p);
 
-        for (i = 0; i < n; i++) {
+        for (size_t i = 0; i < n; i++) {
                 const TemporaryFileSystem *t = tmpfs + i;
                 _cleanup_free_ char *o = NULL, *str = NULL;
                 unsigned long flags;
                 bool ro = false;
+                int r;
 
                 if (!path_is_absolute(t->path))
                         return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
                                                "Path is not absolute: %s",
                                                t->path);
 
-                str = strjoin("mode=0755,", t->options);
+                str = strjoin("mode=0755" NESTED_TMPFS_LIMITS ",", t->options);
                 if (!str)
                         return -ENOMEM;
 
@@ -649,13 +648,15 @@ add_symlink:
                 return 0;
 
         /* Create symlinks like /dev/char/1:9 → ../urandom */
-        if (asprintf(&sl, "%s/dev/%s/%u:%u", temporary_mount, S_ISCHR(st.st_mode) ? "char" : "block", major(st.st_rdev), minor(st.st_rdev)) < 0)
+        if (asprintf(&sl, "%s/dev/%s/%u:%u",
+                     temporary_mount,
+                     S_ISCHR(st.st_mode) ? "char" : "block",
+                     major(st.st_rdev), minor(st.st_rdev)) < 0)
                 return log_oom();
 
         (void) mkdir_parents(sl, 0755);
 
         t = strjoina("../", bn);
-
         if (symlink(t, sl) < 0)
                 log_debug_errno(errno, "Failed to symlink '%s' to '%s', ignoring: %m", t, sl);
 
@@ -686,10 +687,15 @@ static int mount_private_dev(MountEntry *m) {
 
         dev = strjoina(temporary_mount, "/dev");
         (void) mkdir(dev, 0755);
-        if (mount("tmpfs", dev, "tmpfs", DEV_MOUNT_OPTIONS, "mode=755") < 0) {
+        if (mount("tmpfs", dev, "tmpfs", DEV_MOUNT_OPTIONS, "mode=755" TMPFS_LIMITS_DEV) < 0) {
                 r = log_debug_errno(errno, "Failed to mount tmpfs on '%s': %m", dev);
                 goto fail;
         }
+        r = label_fix_container(dev, "/dev", 0);
+        if (r < 0) {
+                log_debug_errno(errno, "Failed to fix label of '%s' as /dev: %m", dev);
+                goto fail;
+        }
 
         devpts = strjoina(temporary_mount, "/dev/pts");
         (void) mkdir(devpts, 0755);
@@ -742,7 +748,7 @@ static int mount_private_dev(MountEntry *m) {
 
         NULSTR_FOREACH(d, devnodes) {
                 r = clone_device_node(d, temporary_mount, &can_mknod);
-                /* ENXIO means the the *source* is not a device file, skip creation in that case */
+                /* ENXIO means the *source* is not a device file, skip creation in that case */
                 if (r < 0 && r != -ENXIO)
                         goto fail;
         }
@@ -855,15 +861,23 @@ static int mount_procfs(const MountEntry *m) {
 }
 
 static int mount_tmpfs(const MountEntry *m) {
+        int r;
+        const char *entry_path = mount_entry_path(m);
+        const char *source_path = m->path_const;
+
         assert(m);
 
         /* First, get rid of everything that is below if there is anything. Then, overmount with our new tmpfs */
 
-        (void) mkdir_p_label(mount_entry_path(m), 0755);
-        (void) umount_recursive(mount_entry_path(m), 0);
+        (void) mkdir_p_label(entry_path, 0755);
+        (void) umount_recursive(entry_path, 0);
 
-        if (mount("tmpfs", mount_entry_path(m), "tmpfs", m->flags, mount_entry_options(m)) < 0)
-                return log_debug_errno(errno, "Failed to mount %s: %m", mount_entry_path(m));
+        if (mount("tmpfs", entry_path, "tmpfs", m->flags, mount_entry_options(m)) < 0)
+                return log_debug_errno(errno, "Failed to mount %s: %m", entry_path);
+
+        r = label_fix_container(entry_path, source_path, 0);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to fix label of '%s' as '%s': %m", entry_path, source_path);
 
         return 1;
 }
@@ -930,14 +944,15 @@ static int apply_mount(
                         if (errno == ENOENT && m->ignore)
                                 return 0;
 
-                        return log_debug_errno(errno, "Failed to lstat() %s to determine what to mount over it: %m", mount_entry_path(m));
+                        return log_debug_errno(errno, "Failed to lstat() %s to determine what to mount over it: %m",
+                                               mount_entry_path(m));
                 }
 
                 if (geteuid() == 0)
-                        runtime_dir = "/run/systemd";
+                        runtime_dir = "/run";
                 else {
-                        if (asprintf(&tmp, "/run/user/"UID_FMT, geteuid()) < 0)
-                                log_oom();
+                        if (asprintf(&tmp, "/run/user/" UID_FMT, geteuid()) < 0)
+                                return -ENOMEM;
 
                         runtime_dir = tmp;
                 }
@@ -957,8 +972,10 @@ static int apply_mount(
                 if (r == -ENOENT && m->ignore)
                         return 0;
                 if (r < 0)
-                        return log_debug_errno(r, "Failed to determine whether %s is already a mount point: %m", mount_entry_path(m));
-                if (r > 0) /* Nothing to do here, it is already a mount. We just later toggle the MS_RDONLY bit for the mount point if needed. */
+                        return log_debug_errno(r, "Failed to determine whether %s is already a mount point: %m",
+                                               mount_entry_path(m));
+                if (r > 0) /* Nothing to do here, it is already a mount. We just later toggle the MS_RDONLY
+                            * bit for the mount point if needed. */
                         return 0;
                 /* This isn't a mount point yet, let's make it one. */
                 what = mount_entry_path(m);
@@ -971,9 +988,9 @@ static int apply_mount(
         case BIND_MOUNT_RECURSIVE: {
                 _cleanup_free_ char *chased = NULL;
 
-                /* Since mount() will always follow symlinks we chase the symlinks on our own first. Note that bind
-                 * mount source paths are always relative to the host root, hence we pass NULL as root directory to
-                 * chase_symlinks() here. */
+                /* Since mount() will always follow symlinks we chase the symlinks on our own first. Note
+                 * that bind mount source paths are always relative to the host root, hence we pass NULL as
+                 * root directory to chase_symlinks() here. */
 
                 r = chase_symlinks(mount_entry_source(m), NULL, CHASE_TRAIL_SLASH, &chased, NULL);
                 if (r == -ENOENT && m->ignore) {
@@ -997,6 +1014,7 @@ static int apply_mount(
                 return mount_tmpfs(m);
 
         case PRIVATE_TMP:
+        case PRIVATE_TMP_READONLY:
                 what = mount_entry_source(m);
                 make = true;
                 break;
@@ -1026,10 +1044,11 @@ static int apply_mount(
                 if (r == -ENOENT && make) {
                         struct stat st;
 
-                        /* Hmm, either the source or the destination are missing. Let's see if we can create the destination, then try again */
+                        /* Hmm, either the source or the destination are missing. Let's see if we can create
+                           the destination, then try again. */
 
                         if (stat(what, &st) < 0)
-                                log_debug_errno(errno, "Mount point source '%s' is not accessible: %m", what);
+                                log_error_errno(errno, "Mount point source '%s' is not accessible: %m", what);
                         else {
                                 int q;
 
@@ -1041,7 +1060,8 @@ static int apply_mount(
                                         q = touch(mount_entry_path(m));
 
                                 if (q < 0)
-                                        log_debug_errno(q, "Failed to create destination mount point node '%s': %m", mount_entry_path(m));
+                                        log_error_errno(q, "Failed to create destination mount point node '%s': %m",
+                                                        mount_entry_path(m));
                                 else
                                         try_again = true;
                         }
@@ -1055,14 +1075,14 @@ static int apply_mount(
                 }
 
                 if (r < 0)
-                        return log_debug_errno(r, "Failed to mount %s to %s: %m", what, mount_entry_path(m));
+                        return log_error_errno(r, "Failed to mount %s to %s: %m", what, mount_entry_path(m));
         }
 
         log_debug("Successfully mounted %s to %s", what, mount_entry_path(m));
         return 0;
 }
 
-static int make_read_only(const MountEntry *m, char **blacklist, FILE *proc_self_mountinfo) {
+static int make_read_only(const MountEntry *m, char **deny_list, FILE *proc_self_mountinfo) {
         unsigned long new_flags = 0, flags_mask = 0;
         bool submounts = false;
         int r = 0;
@@ -1091,7 +1111,7 @@ static int make_read_only(const MountEntry *m, char **blacklist, FILE *proc_self
                 mount_entry_read_only(m) &&
                 !IN_SET(m->mode, EMPTY_DIR, TMPFS);
         if (submounts)
-                r = bind_remount_recursive_with_mountinfo(mount_entry_path(m), new_flags, flags_mask, blacklist, proc_self_mountinfo);
+                r = bind_remount_recursive_with_mountinfo(mount_entry_path(m), new_flags, flags_mask, deny_list, proc_self_mountinfo);
         else
                 r = bind_remount_one_with_mountinfo(mount_entry_path(m), new_flags, flags_mask, proc_self_mountinfo);
 
@@ -1252,15 +1272,23 @@ int setup_namespace(
                 ProtectHome protect_home,
                 ProtectSystem protect_system,
                 unsigned long mount_flags,
+                const void *root_hash,
+                size_t root_hash_size,
+                const char *root_hash_path,
+                const void *root_hash_sig,
+                size_t root_hash_sig_size,
+                const char *root_hash_sig_path,
+                const char *root_verity,
                 DissectImageFlags dissect_image_flags,
                 char **error_path) {
 
         _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
         _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
         _cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL;
-        _cleanup_free_ void *root_hash = NULL;
+        _cleanup_free_ void *root_hash_decoded = NULL;
+        _cleanup_free_ char *verity_data = NULL, *hash_sig_path = NULL;
         MountEntry *m = NULL, *mounts = NULL;
-        size_t n_mounts, root_hash_size = 0;
+        size_t n_mounts;
         bool require_prefix = false;
         const char *root;
         int r = 0;
@@ -1289,15 +1317,35 @@ int setup_namespace(
                 if (r < 0)
                         return log_debug_errno(r, "Failed to create loop device for root image: %m");
 
-                r = root_hash_load(root_image, &root_hash, &root_hash_size);
+                r = verity_metadata_load(root_image,
+                                         root_hash_path,
+                                         root_hash ? NULL : &root_hash_decoded,
+                                         root_hash ? NULL : &root_hash_size,
+                                         root_verity ? NULL : &verity_data,
+                                         root_hash_sig || root_hash_sig_path ? NULL : &hash_sig_path);
                 if (r < 0)
                         return log_debug_errno(r, "Failed to load root hash: %m");
-
-                r = dissect_image(loop_device->fd, root_hash, root_hash_size, dissect_image_flags, &dissected_image);
+                dissect_image_flags |= root_verity || verity_data ? DISSECT_IMAGE_NO_PARTITION_TABLE : 0;
+
+                r = dissect_image(loop_device->fd,
+                                  root_hash ?: root_hash_decoded,
+                                  root_hash_size,
+                                  root_verity ?: verity_data,
+                                  dissect_image_flags,
+                                  &dissected_image);
                 if (r < 0)
                         return log_debug_errno(r, "Failed to dissect image: %m");
 
-                r = dissected_image_decrypt(dissected_image, NULL, root_hash, root_hash_size, dissect_image_flags, &decrypted_image);
+                r = dissected_image_decrypt(dissected_image,
+                                            NULL,
+                                            root_hash ?: root_hash_decoded,
+                                            root_hash_size,
+                                            root_verity ?: verity_data,
+                                            root_hash_sig_path ?: hash_sig_path,
+                                            root_hash_sig,
+                                            root_hash_sig_size,
+                                            dissect_image_flags,
+                                            &decrypted_image);
                 if (r < 0)
                         return log_debug_errno(r, "Failed to decrypt dissected image: %m");
         }
@@ -1358,17 +1406,21 @@ int setup_namespace(
                         goto finish;
 
                 if (tmp_dir) {
+                        bool ro = streq(tmp_dir, RUN_SYSTEMD_EMPTY);
+
                         *(m++) = (MountEntry) {
                                 .path_const = "/tmp",
-                                .mode = PRIVATE_TMP,
+                                .mode = ro ? PRIVATE_TMP_READONLY : PRIVATE_TMP,
                                 .source_const = tmp_dir,
                         };
                 }
 
                 if (var_tmp_dir) {
+                        bool ro = streq(var_tmp_dir, RUN_SYSTEMD_EMPTY);
+
                         *(m++) = (MountEntry) {
                                 .path_const = "/var/tmp",
-                                .mode = PRIVATE_TMP,
+                                .mode = ro ? PRIVATE_TMP_READONLY : PRIVATE_TMP,
                                 .source_const = var_tmp_dir,
                         };
                 }
@@ -1382,19 +1434,28 @@ int setup_namespace(
                 }
 
                 if (ns_info->protect_kernel_tunables) {
-                        r = append_static_mounts(&m, protect_kernel_tunables_table, ELEMENTSOF(protect_kernel_tunables_table), ns_info->ignore_protect_paths);
+                        r = append_static_mounts(&m,
+                                                 protect_kernel_tunables_table,
+                                                 ELEMENTSOF(protect_kernel_tunables_table),
+                                                 ns_info->ignore_protect_paths);
                         if (r < 0)
                                 goto finish;
                 }
 
                 if (ns_info->protect_kernel_modules) {
-                        r = append_static_mounts(&m, protect_kernel_modules_table, ELEMENTSOF(protect_kernel_modules_table), ns_info->ignore_protect_paths);
+                        r = append_static_mounts(&m,
+                                                 protect_kernel_modules_table,
+                                                 ELEMENTSOF(protect_kernel_modules_table),
+                                                 ns_info->ignore_protect_paths);
                         if (r < 0)
                                 goto finish;
                 }
 
                 if (ns_info->protect_kernel_logs) {
-                        r = append_static_mounts(&m, protect_kernel_logs_table, ELEMENTSOF(protect_kernel_logs_table), ns_info->ignore_protect_paths);
+                        r = append_static_mounts(&m,
+                                                 protect_kernel_logs_table,
+                                                 ELEMENTSOF(protect_kernel_logs_table),
+                                                 ns_info->ignore_protect_paths);
                         if (r < 0)
                                 goto finish;
                 }
@@ -1415,7 +1476,10 @@ int setup_namespace(
                         goto finish;
 
                 if (namespace_info_mount_apivfs(ns_info)) {
-                        r = append_static_mounts(&m, apivfs_table, ELEMENTSOF(apivfs_table), ns_info->ignore_protect_paths);
+                        r = append_static_mounts(&m,
+                                                 apivfs_table,
+                                                 ELEMENTSOF(apivfs_table),
+                                                 ns_info->ignore_protect_paths);
                         if (r < 0)
                                 goto finish;
                 }
@@ -1463,10 +1527,10 @@ int setup_namespace(
         if (unshare(CLONE_NEWNS) < 0) {
                 r = log_debug_errno(errno, "Failed to unshare the mount namespace: %m");
                 if (IN_SET(r, -EACCES, -EPERM, -EOPNOTSUPP, -ENOSYS))
-                        /* If the kernel doesn't support namespaces, or when there's a MAC or seccomp filter in place
-                         * that doesn't allow us to create namespaces (or a missing cap), then propagate a recognizable
-                         * error back, which the caller can use to detect this case (and only this) and optionally
-                         * continue without namespacing applied. */
+                        /* If the kernel doesn't support namespaces, or when there's a MAC or seccomp filter
+                         * in place that doesn't allow us to create namespaces (or a missing cap), then
+                         * propagate a recognizable error back, which the caller can use to detect this case
+                         * (and only this) and optionally continue without namespacing applied. */
                         r = -ENOANO;
 
                 goto finish;
@@ -1527,11 +1591,11 @@ int setup_namespace(
 
         if (n_mounts > 0) {
                 _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
-                _cleanup_free_ char **blacklist = NULL;
+                _cleanup_free_ char **deny_list = NULL;
                 size_t j;
 
-                /* Open /proc/self/mountinfo now as it may become unavailable if we mount anything on top of /proc.
-                 * For example, this is the case with the option: 'InaccessiblePaths=/proc' */
+                /* Open /proc/self/mountinfo now as it may become unavailable if we mount anything on top of
+                 * /proc. For example, this is the case with the option: 'InaccessiblePaths=/proc'. */
                 proc_self_mountinfo = fopen("/proc/self/mountinfo", "re");
                 if (!proc_self_mountinfo) {
                         r = log_debug_errno(errno, "Failed to open /proc/self/mountinfo: %m");
@@ -1556,10 +1620,10 @@ int setup_namespace(
                                         goto finish;
                                 }
                                 if (r == 0) {
-                                        /* We hit a symlinked mount point. The entry got rewritten and might point to a
-                                         * very different place now. Let's normalize the changed list, and start from
-                                         * the beginning. After all to mount the entry at the new location we might
-                                         * need some other mounts first */
+                                        /* We hit a symlinked mount point. The entry got rewritten and might
+                                         * point to a very different place now. Let's normalize the changed
+                                         * list, and start from the beginning. After all to mount the entry
+                                         * at the new location we might need some other mounts first */
                                         again = true;
                                         break;
                                 }
@@ -1580,19 +1644,19 @@ int setup_namespace(
                         normalize_mounts(root, mounts, &n_mounts);
                 }
 
-                /* Create a blacklist we can pass to bind_mount_recursive() */
-                blacklist = new(char*, n_mounts+1);
-                if (!blacklist) {
+                /* Create a deny list we can pass to bind_mount_recursive() */
+                deny_list = new(char*, n_mounts+1);
+                if (!deny_list) {
                         r = -ENOMEM;
                         goto finish;
                 }
                 for (j = 0; j < n_mounts; j++)
-                        blacklist[j] = (char*) mount_entry_path(mounts+j);
-                blacklist[j] = NULL;
+                        deny_list[j] = (char*) mount_entry_path(mounts+j);
+                deny_list[j] = NULL;
 
                 /* Second round, flip the ro bits if necessary. */
                 for (m = mounts; m < mounts + n_mounts; ++m) {
-                        r = make_read_only(m, blacklist, proc_self_mountinfo);
+                        r = make_read_only(m, deny_list, proc_self_mountinfo);
                         if (r < 0) {
                                 if (error_path && mount_entry_path(m))
                                         *error_path = strdup(mount_entry_path(m));
@@ -1763,10 +1827,28 @@ static int make_tmp_prefix(const char *prefix) {
 
 }
 
-static int setup_one_tmp_dir(const char *id, const char *prefix, char **path) {
+static int make_tmp_subdir(const char *parent, char **ret) {
+        _cleanup_free_ char *y = NULL;
+
+        RUN_WITH_UMASK(0000) {
+                y = strjoin(parent, "/tmp");
+                if (!y)
+                        return -ENOMEM;
+
+                if (mkdir(y, 0777 | S_ISVTX) < 0)
+                        return -errno;
+        }
+
+        if (ret)
+                *ret = TAKE_PTR(y);
+        return 0;
+}
+
+static int setup_one_tmp_dir(const char *id, const char *prefix, char **path, char **tmp_path) {
         _cleanup_free_ char *x = NULL;
         char bid[SD_ID128_STRING_MAX];
         sd_id128_t boot_id;
+        bool rw = true;
         int r;
 
         assert(id);
@@ -1789,49 +1871,55 @@ static int setup_one_tmp_dir(const char *id, const char *prefix, char **path) {
                 return r;
 
         RUN_WITH_UMASK(0077)
-                if (!mkdtemp(x))
-                        return -errno;
-
-        RUN_WITH_UMASK(0000) {
-                char *y;
+                if (!mkdtemp(x)) {
+                        if (errno == EROFS || ERRNO_IS_DISK_SPACE(errno))
+                                rw = false;
+                        else
+                                return -errno;
+                }
 
-                y = strjoina(x, "/tmp");
+        if (rw) {
+                r = make_tmp_subdir(x, tmp_path);
+                if (r < 0)
+                        return r;
+        } else {
+                /* Trouble: we failed to create the directory. Instead of failing, let's simulate /tmp being
+                 * read-only. This way the service will get the EROFS result as if it was writing to the real
+                 * file system. */
+                r = mkdir_p(RUN_SYSTEMD_EMPTY, 0500);
+                if (r < 0)
+                        return r;
 
-                if (mkdir(y, 0777 | S_ISVTX) < 0)
-                        return -errno;
+                x = strdup(RUN_SYSTEMD_EMPTY);
+                if (!x)
+                        return -ENOMEM;
         }
 
         *path = TAKE_PTR(x);
-
         return 0;
 }
 
 int setup_tmp_dirs(const char *id, char **tmp_dir, char **var_tmp_dir) {
-        char *a, *b;
+        _cleanup_(namespace_cleanup_tmpdirp) char *a = NULL;
+        _cleanup_(rmdir_and_freep) char *a_tmp = NULL;
+        char *b;
         int r;
 
         assert(id);
         assert(tmp_dir);
         assert(var_tmp_dir);
 
-        r = setup_one_tmp_dir(id, "/tmp", &a);
+        r = setup_one_tmp_dir(id, "/tmp", &a, &a_tmp);
         if (r < 0)
                 return r;
 
-        r = setup_one_tmp_dir(id, "/var/tmp", &b);
-        if (r < 0) {
-                char *t;
-
-                t = strjoina(a, "/tmp");
-                (void) rmdir(t);
-                (void) rmdir(a);
-
-                free(a);
+        r = setup_one_tmp_dir(id, "/var/tmp", &b, NULL);
+        if (r < 0)
                 return r;
-        }
 
-        *tmp_dir = a;
-        *var_tmp_dir = b;
+        a_tmp = mfree(a_tmp); /* avoid rmdir */
+        *tmp_dir = TAKE_PTR(a);
+        *var_tmp_dir = TAKE_PTR(b);
 
         return 0;
 }
@@ -1964,31 +2052,31 @@ bool ns_type_supported(NamespaceType type) {
 }
 
 static const char *const protect_home_table[_PROTECT_HOME_MAX] = {
-        [PROTECT_HOME_NO] = "no",
-        [PROTECT_HOME_YES] = "yes",
+        [PROTECT_HOME_NO]        = "no",
+        [PROTECT_HOME_YES]       = "yes",
         [PROTECT_HOME_READ_ONLY] = "read-only",
-        [PROTECT_HOME_TMPFS] = "tmpfs",
+        [PROTECT_HOME_TMPFS]     = "tmpfs",
 };
 
 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(protect_home, ProtectHome, PROTECT_HOME_YES);
 
 static const char *const protect_system_table[_PROTECT_SYSTEM_MAX] = {
-        [PROTECT_SYSTEM_NO] = "no",
-        [PROTECT_SYSTEM_YES] = "yes",
-        [PROTECT_SYSTEM_FULL] = "full",
+        [PROTECT_SYSTEM_NO]     = "no",
+        [PROTECT_SYSTEM_YES]    = "yes",
+        [PROTECT_SYSTEM_FULL]   = "full",
         [PROTECT_SYSTEM_STRICT] = "strict",
 };
 
 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(protect_system, ProtectSystem, PROTECT_SYSTEM_YES);
 
 static const char* const namespace_type_table[] = {
-        [NAMESPACE_MOUNT] = "mnt",
+        [NAMESPACE_MOUNT]  = "mnt",
         [NAMESPACE_CGROUP] = "cgroup",
-        [NAMESPACE_UTS] = "uts",
-        [NAMESPACE_IPC] = "ipc",
-        [NAMESPACE_USER] = "user",
-        [NAMESPACE_PID] = "pid",
-        [NAMESPACE_NET] = "net",
+        [NAMESPACE_UTS]    = "uts",
+        [NAMESPACE_IPC]    = "ipc",
+        [NAMESPACE_USER]   = "user",
+        [NAMESPACE_PID]    = "pid",
+        [NAMESPACE_NET]    = "net",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(namespace_type, NamespaceType);
index ef6c9bdc9bdcaa8bb3c68b93ff4e6ee42164d1ef..b182223bd41dddfc50fa51d4c21f29be1e511daa 100644 (file)
@@ -12,7 +12,9 @@ typedef struct TemporaryFileSystem TemporaryFileSystem;
 #include <stdbool.h>
 
 #include "dissect-image.h"
+#include "fs-util.h"
 #include "macro.h"
+#include "string-util.h"
 
 typedef enum ProtectHome {
         PROTECT_HOME_NO,
@@ -88,9 +90,26 @@ int setup_namespace(
                 ProtectHome protect_home,
                 ProtectSystem protect_system,
                 unsigned long mount_flags,
+                const void *root_hash,
+                size_t root_hash_size,
+                const char *root_hash_path,
+                const void *root_hash_sig,
+                size_t root_hash_sig_size,
+                const char *root_hash_sig_path,
+                const char *root_verity,
                 DissectImageFlags dissected_image_flags,
                 char **error_path);
 
+#define RUN_SYSTEMD_EMPTY "/run/systemd/empty"
+
+static inline void namespace_cleanup_tmpdir(char *p) {
+        PROTECT_ERRNO;
+        if (!streq_ptr(p, RUN_SYSTEMD_EMPTY))
+                (void) rmdir(p);
+        free(p);
+}
+DEFINE_TRIVIAL_CLEANUP_FUNC(char*, namespace_cleanup_tmpdir);
+
 int setup_tmp_dirs(
                 const char *id,
                 char **tmp_dir,
index 415b3f5d84f8759e0cd903412828b51d2c9a836c..9a5912c10f3e2caccca84b83bad045bce34271b1 100644 (file)
                        send_interface="org.freedesktop.systemd1.Manager"
                        send_member="AddDependencyUnitFiles"/>
 
+                <allow send_destination="org.freedesktop.systemd1"
+                       send_interface="org.freedesktop.systemd1.Manager"
+                       send_member="SetShowStatus"/>
+
                 <!-- Managed via polkit or other criteria: org.freedesktop.systemd1.Job interface -->
 
                 <allow send_destination="org.freedesktop.systemd1"
index cae56b0a2576996a80a0140885e51518b7dfee98..1c3c28e34172c2b5484ded964e723283da82f969 100644 (file)
@@ -174,15 +174,13 @@ int path_spec_fd_event(PathSpec *s, uint32_t revents) {
         return r;
 }
 
-static bool path_spec_check_good(PathSpec *s, bool initial) {
+static bool path_spec_check_good(PathSpec *s, bool initial, bool from_trigger_notify) {
         bool b, good = false;
 
         switch (s->type) {
 
         case PATH_EXISTS:
-                b = access(s->path, F_OK) >= 0;
-                good = b && !s->previous_exists;
-                s->previous_exists = b;
+                good = access(s->path, F_OK) >= 0;
                 break;
 
         case PATH_EXISTS_GLOB:
@@ -200,7 +198,7 @@ static bool path_spec_check_good(PathSpec *s, bool initial) {
         case PATH_CHANGED:
         case PATH_MODIFIED:
                 b = access(s->path, F_OK) >= 0;
-                good = !initial && b != s->previous_exists;
+                good = !initial && !from_trigger_notify && b != s->previous_exists;
                 s->previous_exists = b;
                 break;
 
@@ -425,8 +423,7 @@ static void path_set_state(Path *p, PathState state) {
         old_state = p->state;
         p->state = state;
 
-        if (state != PATH_WAITING &&
-            (state != PATH_RUNNING || p->inotify_triggered))
+        if (!IN_SET(state, PATH_WAITING, PATH_RUNNING))
                 path_unwatch(p);
 
         if (state != old_state)
@@ -435,7 +432,7 @@ static void path_set_state(Path *p, PathState state) {
         unit_notify(UNIT(p), state_translation_table[old_state], state_translation_table[state], 0);
 }
 
-static void path_enter_waiting(Path *p, bool initial, bool recheck);
+static void path_enter_waiting(Path *p, bool initial, bool from_trigger_notify);
 
 static int path_coldplug(Unit *u) {
         Path *p = PATH(u);
@@ -446,7 +443,7 @@ static int path_coldplug(Unit *u) {
         if (p->deserialized_state != p->state) {
 
                 if (IN_SET(p->deserialized_state, PATH_WAITING, PATH_RUNNING))
-                        path_enter_waiting(p, true, true);
+                        path_enter_waiting(p, true, false);
                 else
                         path_set_state(p, p->deserialized_state);
         }
@@ -486,8 +483,6 @@ static void path_enter_running(Path *p) {
         if (r < 0)
                 goto fail;
 
-        p->inotify_triggered = false;
-
         path_set_state(p, PATH_RUNNING);
         path_unwatch(p);
 
@@ -498,27 +493,35 @@ fail:
         path_enter_dead(p, PATH_FAILURE_RESOURCES);
 }
 
-static bool path_check_good(Path *p, bool initial) {
+static bool path_check_good(Path *p, bool initial, bool from_trigger_notify) {
         PathSpec *s;
 
         assert(p);
 
         LIST_FOREACH(spec, s, p->specs)
-                if (path_spec_check_good(s, initial))
+                if (path_spec_check_good(s, initial, from_trigger_notify))
                         return true;
 
         return false;
 }
 
-static void path_enter_waiting(Path *p, bool initial, bool recheck) {
+static void path_enter_waiting(Path *p, bool initial, bool from_trigger_notify) {
+        Unit *trigger;
         int r;
 
-        if (recheck)
-                if (path_check_good(p, initial)) {
-                        log_unit_debug(UNIT(p), "Got triggered.");
-                        path_enter_running(p);
-                        return;
-                }
+        /* If the triggered unit is already running, so are we */
+        trigger = UNIT_TRIGGER(UNIT(p));
+        if (trigger && !UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(trigger))) {
+                path_set_state(p, PATH_RUNNING);
+                path_unwatch(p);
+                return;
+        }
+
+        if (path_check_good(p, initial, from_trigger_notify)) {
+                log_unit_debug(UNIT(p), "Got triggered.");
+                path_enter_running(p);
+                return;
+        }
 
         r = path_watch(p);
         if (r < 0)
@@ -528,12 +531,11 @@ static void path_enter_waiting(Path *p, bool initial, bool recheck) {
          * might have appeared/been removed by now, so we must
          * recheck */
 
-        if (recheck)
-                if (path_check_good(p, false)) {
-                        log_unit_debug(UNIT(p), "Got triggered.");
-                        path_enter_running(p);
-                        return;
-                }
+        if (path_check_good(p, false, from_trigger_notify)) {
+                log_unit_debug(UNIT(p), "Got triggered.");
+                path_enter_running(p);
+                return;
+        }
 
         path_set_state(p, PATH_WAITING);
         return;
@@ -579,7 +581,7 @@ static int path_start(Unit *u) {
         path_mkdir(p);
 
         p->result = PATH_SUCCESS;
-        path_enter_waiting(p, true, true);
+        path_enter_waiting(p, true, false);
 
         return 1;
 }
@@ -728,15 +730,10 @@ static int path_dispatch_io(sd_event_source *source, int fd, uint32_t revents, v
         if (changed < 0)
                 goto fail;
 
-        /* If we are already running, then remember that one event was
-         * dispatched so that we restart the service only if something
-         * actually changed on disk */
-        p->inotify_triggered = true;
-
         if (changed)
                 path_enter_running(p);
         else
-                path_enter_waiting(p, false, true);
+                path_enter_waiting(p, false, false);
 
         return 0;
 
@@ -760,11 +757,11 @@ static void path_trigger_notify(Unit *u, Unit *other) {
         if (p->state == PATH_RUNNING &&
             UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) {
                 log_unit_debug(UNIT(p), "Got notified about unit deactivation.");
-
-                /* Hmm, so inotify was triggered since the
-                 * last activation, so I guess we need to
-                 * recheck what is going on. */
-                path_enter_waiting(p, false, p->inotify_triggered);
+                path_enter_waiting(p, false, true);
+        } else if (p->state == PATH_WAITING &&
+                   !UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) {
+                log_unit_debug(UNIT(p), "Got notified about unit activation.");
+                path_enter_waiting(p, false, true);
         }
 }
 
@@ -831,6 +828,5 @@ const UnitVTable path_vtable = {
 
         .reset_failed = path_reset_failed,
 
-        .bus_vtable = bus_path_vtable,
         .bus_set_property = bus_path_set_property,
 };
index 4d4b6236c27f155ce866eef120e1829fdb932ee0..9e2836535a978c2caff2350a31349e91a92f3a13 100644 (file)
@@ -56,8 +56,6 @@ struct Path {
 
         PathState state, deserialized_state;
 
-        bool inotify_triggered;
-
         bool make_directory;
         mode_t directory_mode;
 
index 76358c416ad1422930faced879b95c2b8523231f..42c51b08651641664183950ea874c110dbc1139c 100644 (file)
@@ -635,6 +635,9 @@ const UnitVTable scope_vtable = {
 
         .kill = scope_kill,
 
+        .freeze = unit_freeze_vtable_common,
+        .thaw = unit_thaw_vtable_common,
+
         .get_timeout = scope_get_timeout,
 
         .serialize = scope_serialize,
@@ -649,7 +652,6 @@ const UnitVTable scope_vtable = {
 
         .notify_cgroup_empty = scope_notify_cgroup_empty_event,
 
-        .bus_vtable = bus_scope_vtable,
         .bus_set_property = bus_scope_set_property,
         .bus_commit_properties = bus_scope_commit_properties,
 
index 00f5241cd11dee2fbbe643335ec92b5c387475f9..1d52b5ff0497ba606ab84220453ab3862d93eef7 100644 (file)
@@ -123,12 +123,12 @@ _printf_(2, 3) static int log_callback(int type, const char *fmt, ...) {
         fmt2 = strjoina("selinux: ", fmt);
 
         va_start(ap, fmt);
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+
+        DISABLE_WARNING_FORMAT_NONLITERAL;
         log_internalv(LOG_AUTH | callback_type_to_priority(type),
                       0, PROJECT_FILE, __LINE__, __FUNCTION__,
                       fmt2, ap);
-#pragma GCC diagnostic pop
+        REENABLE_WARNING;
         va_end(ap);
 
         return 0;
@@ -143,16 +143,17 @@ static int access_init(sd_bus_error *error) {
                 return 1;
 
         if (avc_open(NULL, 0) != 0) {
-                int enforce, saved_errno = errno;
+                int saved_errno = errno;
+                bool enforce;
 
-                enforce = security_getenforce();
-                log_full_errno(enforce != 0 ? LOG_ERR : LOG_WARNING, saved_errno, "Failed to open the SELinux AVC: %m");
+                enforce = security_getenforce() != 0;
+                log_full_errno(enforce ? LOG_ERR : LOG_WARNING, saved_errno, "Failed to open the SELinux AVC: %m");
 
                 /* If enforcement isn't on, then let's suppress this
                  * error, and just don't do any AVC checks. The
                  * warning we printed is hence all the admin will
                  * see. */
-                if (enforce == 0)
+                if (!enforce)
                         return 0;
 
                 /* Return an access denied error, if we couldn't load
@@ -185,7 +186,7 @@ int mac_selinux_generic_access_check(
         _cleanup_free_ char *cl = NULL;
         _cleanup_freecon_ char *fcon = NULL;
         char **cmdline = NULL;
-        bool enforce = false; /* Will be set to the real value later if needed */
+        bool enforce;
         int r = 0;
 
         assert(message);
@@ -196,6 +197,9 @@ int mac_selinux_generic_access_check(
         if (r <= 0)
                 return r;
 
+        /* delay call until we checked in `access_init()` if SELinux is actually enabled */
+        enforce = security_getenforce() != 0;
+
         r = sd_bus_query_sender_creds(
                         message,
                         SD_BUS_CREDS_PID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EGID|
@@ -223,9 +227,8 @@ int mac_selinux_generic_access_check(
 
                 if (getfilecon_raw(path, &fcon) < 0) {
                         r = -errno;
-                        enforce = security_getenforce() > 0;
 
-                        log_warning_errno(r, "SELinux getfilecon_raw on '%s' failed%s (perm=%s): %m",
+                        log_warning_errno(r, "SELinux getfilecon_raw() on '%s' failed%s (perm=%s): %m",
                                           path,
                                           enforce ? "" : ", ignoring",
                                           permission);
@@ -240,9 +243,8 @@ int mac_selinux_generic_access_check(
         } else {
                 if (getcon_raw(&fcon) < 0) {
                         r = -errno;
-                        enforce = security_getenforce() > 0;
 
-                        log_warning_errno(r, "SELinux getcon_raw failed%s (perm=%s): %m",
+                        log_warning_errno(r, "SELinux getcon_raw() failed%s (perm=%s): %m",
                                           enforce ? "" : ", ignoring",
                                           permission);
                         if (!enforce)
@@ -266,14 +268,13 @@ int mac_selinux_generic_access_check(
         r = selinux_check_access(scon, fcon, tclass, permission, &audit_info);
         if (r < 0) {
                 r = errno_or_else(EPERM);
-                enforce = security_getenforce() > 0;
 
                 if (enforce)
                         sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "SELinux policy denies access.");
         }
 
-        log_debug_errno(r, "SELinux access check scon=%s tcon=%s tclass=%s perm=%s path=%s cmdline=%s: %m",
-                        scon, fcon, tclass, permission, path, cl);
+        log_debug_errno(r, "SELinux access check scon=%s tcon=%s tclass=%s perm=%s state=%s path=%s cmdline=%s: %m",
+                        scon, fcon, tclass, permission, enforce ? "enforcing" : "permissive", path, cl);
         return enforce ? r : 0;
 }
 
index da2e6cbd740695c401df9e8e1270a0be3648da31..58f737de0971be154e489707bf7eec16a9728fb2 100644 (file)
@@ -7,17 +7,8 @@
 
 int mac_selinux_generic_access_check(sd_bus_message *message, const char *path, const char *permission, sd_bus_error *error);
 
-#if HAVE_SELINUX
-
 #define mac_selinux_access_check(message, permission, error) \
         mac_selinux_generic_access_check((message), NULL, (permission), (error))
 
 #define mac_selinux_unit_access_check(unit, message, permission, error) \
         mac_selinux_generic_access_check((message), unit_label_path(unit), (permission), (error))
-
-#else
-
-#define mac_selinux_access_check(message, permission, error) 0
-#define mac_selinux_unit_access_check(unit, message, permission, error) 0
-
-#endif
index db8cf370d0bef366f54826f66db277681f41d82c..00e61945baca45428b21f960f22275e3e1c364d1 100644 (file)
@@ -56,6 +56,7 @@ static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
         [SERVICE_STOP_SIGTERM] = UNIT_DEACTIVATING,
         [SERVICE_STOP_SIGKILL] = UNIT_DEACTIVATING,
         [SERVICE_STOP_POST] = UNIT_DEACTIVATING,
+        [SERVICE_FINAL_WATCHDOG] = UNIT_DEACTIVATING,
         [SERVICE_FINAL_SIGTERM] = UNIT_DEACTIVATING,
         [SERVICE_FINAL_SIGKILL] = UNIT_DEACTIVATING,
         [SERVICE_FAILED] = UNIT_FAILED,
@@ -79,6 +80,7 @@ static const UnitActiveState state_translation_table_idle[_SERVICE_STATE_MAX] =
         [SERVICE_STOP_SIGTERM] = UNIT_DEACTIVATING,
         [SERVICE_STOP_SIGKILL] = UNIT_DEACTIVATING,
         [SERVICE_STOP_POST] = UNIT_DEACTIVATING,
+        [SERVICE_FINAL_WATCHDOG] = UNIT_DEACTIVATING,
         [SERVICE_FINAL_SIGTERM] = UNIT_DEACTIVATING,
         [SERVICE_FINAL_SIGKILL] = UNIT_DEACTIVATING,
         [SERVICE_FAILED] = UNIT_FAILED,
@@ -198,15 +200,6 @@ static void service_stop_watchdog(Service *s) {
         s->watchdog_timestamp = DUAL_TIMESTAMP_NULL;
 }
 
-static usec_t service_get_watchdog_usec(Service *s) {
-        assert(s);
-
-        if (s->watchdog_override_enable)
-                return s->watchdog_override_usec;
-
-        return s->watchdog_original_usec;
-}
-
 static void service_start_watchdog(Service *s) {
         usec_t watchdog_usec;
         int r;
@@ -423,7 +416,7 @@ static int on_fd_store_io(sd_event_source *e, int fd, uint32_t revents, void *us
         return 0;
 }
 
-static int service_add_fd_store(Service *s, int fd, const char *name) {
+static int service_add_fd_store(Service *s, int fd, const char *name, bool do_poll) {
         ServiceFDStore *fs;
         int r;
 
@@ -447,25 +440,31 @@ static int service_add_fd_store(Service *s, int fd, const char *name) {
                 }
         }
 
-        fs = new0(ServiceFDStore, 1);
+        fs = new(ServiceFDStore, 1);
         if (!fs)
                 return -ENOMEM;
 
-        fs->fd = fd;
-        fs->service = s;
-        fs->fdname = strdup(name ?: "stored");
+        *fs = (ServiceFDStore) {
+                .fd = fd,
+                .service = s,
+                .do_poll = do_poll,
+                .fdname = strdup(name ?: "stored"),
+        };
+
         if (!fs->fdname) {
                 free(fs);
                 return -ENOMEM;
         }
 
-        r = sd_event_add_io(UNIT(s)->manager->event, &fs->event_source, fd, 0, on_fd_store_io, fs);
-        if (r < 0 && r != -EPERM) { /* EPERM indicates fds that aren't pollable, which is OK */
-                free(fs->fdname);
-                free(fs);
-                return r;
-        } else if (r >= 0)
-                (void) sd_event_source_set_description(fs->event_source, "service-fd-store");
+        if (do_poll) {
+                r = sd_event_add_io(UNIT(s)->manager->event, &fs->event_source, fd, 0, on_fd_store_io, fs);
+                if (r < 0 && r != -EPERM) { /* EPERM indicates fds that aren't pollable, which is OK */
+                        free(fs->fdname);
+                        free(fs);
+                        return r;
+                } else if (r >= 0)
+                        (void) sd_event_source_set_description(fs->event_source, "service-fd-store");
+        }
 
         LIST_PREPEND(fd_store, s->fd_store, fs);
         s->n_fd_store++;
@@ -473,7 +472,7 @@ static int service_add_fd_store(Service *s, int fd, const char *name) {
         return 1; /* fd newly stored */
 }
 
-static int service_add_fd_store_set(Service *s, FDSet *fds, const char *name) {
+static int service_add_fd_store_set(Service *s, FDSet *fds, const char *name, bool do_poll) {
         int r;
 
         assert(s);
@@ -485,7 +484,7 @@ static int service_add_fd_store_set(Service *s, FDSet *fds, const char *name) {
                 if (fd < 0)
                         break;
 
-                r = service_add_fd_store(s, fd, name);
+                r = service_add_fd_store(s, fd, name, do_poll);
                 if (r == -EXFULL)
                         return log_unit_warning_errno(UNIT(s), r,
                                                       "Cannot store more fds than FileDescriptorStoreMax=%u, closing remaining.",
@@ -655,7 +654,7 @@ static void service_fix_stdio(Service *s) {
 
         /* Note that EXEC_INPUT_NULL and EXEC_OUTPUT_INHERIT play a special role here: they are both the
          * default value that is subject to automatic overriding triggered by other settings and an explicit
-         * choice the user can make. We don't distuingish between these cases currently. */
+         * choice the user can make. We don't distinguish between these cases currently. */
 
         if (s->exec_context.std_input == EXEC_INPUT_NULL &&
             s->exec_context.stdin_data_size > 0)
@@ -860,10 +859,14 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
         fprintf(f,
                 "%sRestartSec: %s\n"
                 "%sTimeoutStartSec: %s\n"
-                "%sTimeoutStopSec: %s\n",
+                "%sTimeoutStopSec: %s\n"
+                "%sTimeoutStartFailureMode: %s\n"
+                "%sTimeoutStopFailureMode: %s\n",
                 prefix, format_timespan(buf_restart, sizeof(buf_restart), s->restart_usec, USEC_PER_SEC),
                 prefix, format_timespan(buf_start, sizeof(buf_start), s->timeout_start_usec, USEC_PER_SEC),
-                prefix, format_timespan(buf_stop, sizeof(buf_stop), s->timeout_stop_usec, USEC_PER_SEC));
+                prefix, format_timespan(buf_stop, sizeof(buf_stop), s->timeout_stop_usec, USEC_PER_SEC),
+                prefix, service_timeout_failure_mode_to_string(s->timeout_start_failure_mode),
+                prefix, service_timeout_failure_mode_to_string(s->timeout_stop_failure_mode));
 
         if (s->timeout_abort_set)
                 fprintf(f,
@@ -1075,7 +1078,7 @@ static void service_set_state(Service *s, ServiceState state) {
                     SERVICE_RUNNING,
                     SERVICE_RELOAD,
                     SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
-                    SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
+                    SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
                     SERVICE_AUTO_RESTART,
                     SERVICE_CLEANING))
                 s->timer_event_source = sd_event_source_unref(s->timer_event_source);
@@ -1084,7 +1087,7 @@ static void service_set_state(Service *s, ServiceState state) {
                     SERVICE_START, SERVICE_START_POST,
                     SERVICE_RUNNING, SERVICE_RELOAD,
                     SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
-                    SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) {
+                    SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) {
                 service_unwatch_main_pid(s);
                 s->main_command = NULL;
         }
@@ -1093,7 +1096,7 @@ static void service_set_state(Service *s, ServiceState state) {
                     SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
                     SERVICE_RELOAD,
                     SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
-                    SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
+                    SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
                     SERVICE_CLEANING)) {
                 service_unwatch_control_pid(s);
                 s->control_command = NULL;
@@ -1109,7 +1112,7 @@ static void service_set_state(Service *s, ServiceState state) {
                     SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
                     SERVICE_RUNNING, SERVICE_RELOAD,
                     SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
-                    SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL) &&
+                    SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL) &&
             !(state == SERVICE_DEAD && UNIT(s)->job))
                 service_close_socket_fd(s);
 
@@ -1157,6 +1160,7 @@ static usec_t service_coldplug_timeout(Service *s) {
                 return usec_add(UNIT(s)->state_change_timestamp.monotonic, s->timeout_stop_usec);
 
         case SERVICE_STOP_WATCHDOG:
+        case SERVICE_FINAL_WATCHDOG:
                 return usec_add(UNIT(s)->state_change_timestamp.monotonic, service_timeout_abort_usec(s));
 
         case SERVICE_AUTO_RESTART:
@@ -1190,7 +1194,7 @@ static int service_coldplug(Unit *u) {
                     SERVICE_START, SERVICE_START_POST,
                     SERVICE_RUNNING, SERVICE_RELOAD,
                     SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
-                    SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL))) {
+                    SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL))) {
                 r = unit_watch_pid(UNIT(s), s->main_pid, false);
                 if (r < 0)
                         return r;
@@ -1202,7 +1206,7 @@ static int service_coldplug(Unit *u) {
                    SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
                    SERVICE_RELOAD,
                    SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
-                   SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
+                   SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
                    SERVICE_CLEANING)) {
                 r = unit_watch_pid(UNIT(s), s->control_pid, false);
                 if (r < 0)
@@ -1438,17 +1442,15 @@ static int service_spawn(
                 pid_t *_pid) {
 
         _cleanup_(exec_params_clear) ExecParameters exec_params = {
-                .flags      = flags,
-                .stdin_fd   = -1,
-                .stdout_fd  = -1,
-                .stderr_fd  = -1,
-                .exec_fd    = -1,
+                .flags     = flags,
+                .stdin_fd  = -1,
+                .stdout_fd = -1,
+                .stderr_fd = -1,
+                .exec_fd   = -1,
         };
-        _cleanup_strv_free_ char **final_env = NULL, **our_env = NULL, **fd_names = NULL;
         _cleanup_(sd_event_source_unrefp) sd_event_source *exec_fd_source = NULL;
-        size_t n_socket_fds = 0, n_storage_fds = 0, n_env = 0;
-        _cleanup_close_ int exec_fd = -1;
-        _cleanup_free_ int *fds = NULL;
+        _cleanup_strv_free_ char **final_env = NULL, **our_env = NULL;
+        size_t n_env = 0;
         pid_t pid;
         int r;
 
@@ -1473,17 +1475,21 @@ static int service_spawn(
             s->exec_context.std_output == EXEC_OUTPUT_SOCKET ||
             s->exec_context.std_error == EXEC_OUTPUT_SOCKET) {
 
-                r = service_collect_fds(s, &fds, &fd_names, &n_socket_fds, &n_storage_fds);
+                r = service_collect_fds(s,
+                                        &exec_params.fds,
+                                        &exec_params.fd_names,
+                                        &exec_params.n_socket_fds,
+                                        &exec_params.n_storage_fds);
                 if (r < 0)
                         return r;
 
-                log_unit_debug(UNIT(s), "Passing %zu fds to service", n_socket_fds + n_storage_fds);
+                log_unit_debug(UNIT(s), "Passing %zu fds to service", exec_params.n_socket_fds + exec_params.n_storage_fds);
         }
 
         if (!FLAGS_SET(flags, EXEC_IS_CONTROL) && s->type == SERVICE_EXEC) {
                 assert(!s->exec_fd_event_source);
 
-                r = service_allocate_exec_fd(s, &exec_fd_source, &exec_fd);
+                r = service_allocate_exec_fd(s, &exec_fd_source, &exec_params.exec_fd);
                 if (r < 0)
                         return r;
         }
@@ -1523,7 +1529,6 @@ static int service_spawn(
 
                 if (getpeername(s->socket_fd, &sa.sa, &salen) >= 0 &&
                     IN_SET(sa.sa.sa_family, AF_INET, AF_INET6, AF_VSOCK)) {
-
                         _cleanup_free_ char *addr = NULL;
                         char *t;
                         unsigned port;
@@ -1578,10 +1583,6 @@ static int service_spawn(
                  MANAGER_IS_SYSTEM(UNIT(s)->manager) && unit_has_name(UNIT(s), SPECIAL_DBUS_SERVICE));
 
         strv_free_and_replace(exec_params.environment, final_env);
-        exec_params.fds = fds;
-        exec_params.fd_names = fd_names;
-        exec_params.n_socket_fds = n_socket_fds;
-        exec_params.n_storage_fds = n_storage_fds;
         exec_params.watchdog_usec = service_get_watchdog_usec(s);
         exec_params.selinux_context_net = s->socket_fd_selinux_context_net;
         if (s->type == SERVICE_IDLE)
@@ -1589,7 +1590,6 @@ static int service_spawn(
         exec_params.stdin_fd = s->stdin_fd;
         exec_params.stdout_fd = s->stdout_fd;
         exec_params.stderr_fd = s->stderr_fd;
-        exec_params.exec_fd = exec_fd;
 
         r = exec_spawn(UNIT(s),
                        c,
@@ -1752,6 +1752,7 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
                 unit_log_failure(UNIT(s), service_result_to_string(s->result));
                 end_state = SERVICE_FAILED;
         }
+        unit_warn_leftover_processes(UNIT(s), unit_log_leftover_process_stop);
 
         if (!allow_restart)
                 log_unit_debug(UNIT(s), "Service restart not allowed.");
@@ -1861,6 +1862,7 @@ static int state_to_kill_operation(Service *s, ServiceState state) {
         switch (state) {
 
         case SERVICE_STOP_WATCHDOG:
+        case SERVICE_FINAL_WATCHDOG:
                 return KILL_WATCHDOG;
 
         case SERVICE_STOP_SIGTERM:
@@ -1881,7 +1883,7 @@ static int state_to_kill_operation(Service *s, ServiceState state) {
 }
 
 static void service_enter_signal(Service *s, ServiceState state, ServiceResult f) {
-        int r;
+        int kill_operation, r;
 
         assert(s);
 
@@ -1895,10 +1897,11 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f
          * died now */
         (void) unit_enqueue_rewatch_pids(UNIT(s));
 
+        kill_operation = state_to_kill_operation(s, state);
         r = unit_kill_context(
                         UNIT(s),
                         &s->kill_context,
-                        state_to_kill_operation(s, state),
+                        kill_operation,
                         s->main_pid,
                         s->control_pid,
                         s->main_pid_alien);
@@ -1907,7 +1910,7 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f
 
         if (r > 0) {
                 r = service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC),
-                                      state == SERVICE_STOP_WATCHDOG ? service_timeout_abort_usec(s) : s->timeout_stop_usec));
+                                      kill_operation == KILL_WATCHDOG ? service_timeout_abort_usec(s) : s->timeout_stop_usec));
                 if (r < 0)
                         goto fail;
 
@@ -1916,7 +1919,7 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f
                 service_enter_signal(s, SERVICE_STOP_SIGKILL, SERVICE_SUCCESS);
         else if (IN_SET(state, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL))
                 service_enter_stop_post(s, SERVICE_SUCCESS);
-        else if (state == SERVICE_FINAL_SIGTERM && s->kill_context.send_sigkill)
+        else if (IN_SET(state, SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM) && s->kill_context.send_sigkill)
                 service_enter_signal(s, SERVICE_FINAL_SIGKILL, SERVICE_SUCCESS);
         else
                 service_enter_dead(s, SERVICE_SUCCESS, true);
@@ -2078,15 +2081,16 @@ static int service_adverse_to_leftover_processes(Service *s) {
         assert(s);
 
         /* KillMode=mixed and control group are used to indicate that all process should be killed off.
-         * SendSIGKILL is used for services that require a clean shutdown. These are typically database
-         * service where a SigKilled process would result in a lengthy recovery and who's shutdown or
-         * startup time is quite variable (so Timeout settings aren't of use).
+         * SendSIGKILL= is used for services that require a clean shutdown. These are typically database
+         * service where a SigKilled process would result in a lengthy recovery and who's shutdown or startup
+         * time is quite variable (so Timeout settings aren't of use).
          *
          * Here we take these two factors and refuse to start a service if there are existing processes
          * within a control group. Databases, while generally having some protection against multiple
-         * instances running, lets not stress the rigor of these. Also ExecStartPre parts of the service
+         * instances running, lets not stress the rigor of these. Also ExecStartPre= parts of the service
          * aren't as rigoriously written to protect aganst against multiple use. */
-        if (unit_warn_leftover_processes(UNIT(s)) &&
+
+        if (unit_warn_leftover_processes(UNIT(s), unit_log_leftover_process_start) > 0 &&
             IN_SET(s->kill_context.kill_mode, KILL_MIXED, KILL_CONTROL_GROUP) &&
             !s->kill_context.send_sigkill)
                return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(EBUSY),
@@ -2124,9 +2128,9 @@ static void service_enter_start(Service *s) {
 
         if (!c) {
                 if (s->type != SERVICE_ONESHOT) {
-                        /* There's no command line configured for the main command? Hmm, that is strange. This can only
-                         * happen if the configuration changes at runtime. In this case, let's enter a failure
-                         * state. */
+                        /* There's no command line configured for the main command? Hmm, that is strange.
+                         * This can only happen if the configuration changes at runtime. In this case,
+                         * let's enter a failure state. */
                         log_unit_error(UNIT(s), "There's no 'start' task anymore we could start.");
                         r = -ENXIO;
                         goto fail;
@@ -2445,7 +2449,7 @@ static int service_start(Unit *u) {
          * please! */
         if (IN_SET(s->state,
                    SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
-                   SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL, SERVICE_CLEANING))
+                   SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL, SERVICE_CLEANING))
                 return -EAGAIN;
 
         /* Already on it! */
@@ -2516,7 +2520,7 @@ static int service_stop(Unit *u) {
         /* Already on it */
         if (IN_SET(s->state,
                    SERVICE_STOP, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
-                   SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL))
+                   SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL))
                 return 0;
 
         /* A restart will be scheduled or is in progress. */
@@ -2569,6 +2573,8 @@ static unsigned service_exec_command_index(Unit *u, ServiceExecCommand id, ExecC
         ExecCommand *first, *c;
 
         assert(s);
+        assert(id >= 0);
+        assert(id < _SERVICE_EXEC_COMMAND_MAX);
 
         first = s->exec_command[id];
 
@@ -2632,10 +2638,12 @@ static int service_serialize_exec_command(Unit *u, FILE *f, ExecCommand *command
 
         p = cescape(command->path);
         if (!p)
-                return -ENOMEM;
+                return log_oom();
 
         key = strjoina(type, "-command");
-        return serialize_item_format(f, key, "%s %u %s %s", service_exec_command_to_string(id), idx, p, args);
+        (void) serialize_item_format(f, key, "%s %u %s %s", service_exec_command_to_string(id), idx, p, args);
+
+        return 0;
 }
 
 static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
@@ -2711,7 +2719,7 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
                 if (!c)
                         return log_oom();
 
-                (void) serialize_item_format(f, "fd-store-fd", "%i %s", copy, c);
+                (void) serialize_item_format(f, "fd-store-fd", "%i \"%s\" %i", copy, c, fs->do_poll);
         }
 
         if (s->main_exec_status.pid > 0) {
@@ -2737,7 +2745,11 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
         return 0;
 }
 
-static int service_deserialize_exec_command(Unit *u, const char *key, const char *value) {
+static int service_deserialize_exec_command(
+                Unit *u,
+                const char *key,
+                const char *value) {
+
         Service *s = SERVICE(u);
         int r;
         unsigned idx = 0, i;
@@ -2826,9 +2838,10 @@ static int service_deserialize_exec_command(Unit *u, const char *key, const char
                                 break;
         }
 
-        if (command && control)
+        if (command && control) {
                 s->control_command = command;
-        else if (command)
+                s->control_command_id = id;
+        } else if (command)
                 s->main_command = command;
         else
                 log_unit_warning(u, "Current command vanished from the unit file, execution of the command list won't be resumed.");
@@ -2935,30 +2948,36 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
                         s->socket_fd = fdset_remove(fds, fd);
                 }
         } else if (streq(key, "fd-store-fd")) {
-                const char *fdv;
-                size_t pf;
+                _cleanup_free_ char *fdv = NULL, *fdn = NULL, *fdp = NULL;
                 int fd;
+                int do_poll;
 
-                pf = strcspn(value, WHITESPACE);
-                fdv = strndupa(value, pf);
-
-                if (safe_atoi(fdv, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
+                r = extract_first_word(&value, &fdv, NULL, 0);
+                if (r <= 0 || safe_atoi(fdv, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) {
                         log_unit_debug(u, "Failed to parse fd-store-fd value: %s", value);
-                else {
-                        _cleanup_free_ char *t = NULL;
-                        const char *fdn;
+                        return 0;
+                }
 
-                        fdn = value + pf;
-                        fdn += strspn(fdn, WHITESPACE);
-                        (void) cunescape(fdn, 0, &t);
+                r = extract_first_word(&value, &fdn, NULL, EXTRACT_CUNESCAPE | EXTRACT_UNQUOTE);
+                if (r <= 0) {
+                        log_unit_debug_errno(u, r, "Failed to parse fd-store-fd value \"%s\": %m", value);
+                        return 0;
+                }
 
-                        r = service_add_fd_store(s, fd, t);
-                        if (r < 0)
-                                log_unit_error_errno(u, r, "Failed to add fd to store: %m");
-                        else
-                                fdset_remove(fds, fd);
+                r = extract_first_word(&value, &fdp, NULL, 0);
+                if (r == 0) {
+                        /* If the value is not present, we assume the default */
+                        do_poll = 1;
+                } else if (r < 0 || safe_atoi(fdp, &do_poll) < 0) {
+                        log_unit_debug_errno(u, r, "Failed to parse fd-store-fd value \"%s\": %m", value);
+                        return 0;
                 }
 
+                r = service_add_fd_store(s, fd, fdn, do_poll);
+                if (r < 0)
+                        log_unit_error_errno(u, r, "Failed to add fd to store: %m");
+                else
+                        fdset_remove(fds, fd);
         } else if (streq(key, "main-exec-status-pid")) {
                 pid_t pid;
 
@@ -3307,6 +3326,7 @@ static void service_notify_cgroup_empty_event(Unit *u) {
                 break;
 
         case SERVICE_STOP_POST:
+        case SERVICE_FINAL_WATCHDOG:
         case SERVICE_FINAL_SIGTERM:
         case SERVICE_FINAL_SIGKILL:
                 if (main_pid_good(s) <= 0 && control_pid_good(s) <= 0)
@@ -3507,6 +3527,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
 
                                 break;
 
+                        case SERVICE_FINAL_WATCHDOG:
                         case SERVICE_FINAL_SIGTERM:
                         case SERVICE_FINAL_SIGKILL:
 
@@ -3660,6 +3681,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
                                         service_enter_signal(s, SERVICE_FINAL_SIGTERM, f);
                                 break;
 
+                        case SERVICE_FINAL_WATCHDOG:
                         case SERVICE_FINAL_SIGTERM:
                         case SERVICE_FINAL_SIGKILL:
                                 if (main_pid_good(s) <= 0)
@@ -3706,13 +3728,32 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us
         case SERVICE_CONDITION:
         case SERVICE_START_PRE:
         case SERVICE_START:
-                log_unit_warning(UNIT(s), "%s operation timed out. Terminating.", service_state_to_string(s->state));
-                service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_TIMEOUT);
-                break;
-
         case SERVICE_START_POST:
-                log_unit_warning(UNIT(s), "Start-post operation timed out. Stopping.");
-                service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_TIMEOUT);
+                switch (s->timeout_start_failure_mode) {
+
+                case SERVICE_TIMEOUT_TERMINATE:
+                        log_unit_warning(UNIT(s), "%s operation timed out. Terminating.", service_state_to_string(s->state));
+                        service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_TIMEOUT);
+                        break;
+
+                case SERVICE_TIMEOUT_ABORT:
+                        log_unit_warning(UNIT(s), "%s operation timed out. Aborting.", service_state_to_string(s->state));
+                        service_enter_signal(s, SERVICE_STOP_WATCHDOG, SERVICE_FAILURE_TIMEOUT);
+                        break;
+
+                case SERVICE_TIMEOUT_KILL:
+                        if (s->kill_context.send_sigkill) {
+                                log_unit_warning(UNIT(s), "%s operation timed out. Killing.", service_state_to_string(s->state));
+                                service_enter_signal(s, SERVICE_STOP_SIGKILL, SERVICE_FAILURE_TIMEOUT);
+                        } else {
+                                log_unit_warning(UNIT(s), "%s operation timed out. Skipping SIGKILL.", service_state_to_string(s->state));
+                                service_enter_stop_post(s, SERVICE_FAILURE_TIMEOUT);
+                        }
+                        break;
+
+                default:
+                        assert_not_reached("unknown timeout mode");
+                }
                 break;
 
         case SERVICE_RUNNING:
@@ -3728,17 +3769,48 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us
                 break;
 
         case SERVICE_STOP:
-                log_unit_warning(UNIT(s), "Stopping timed out. Terminating.");
-                service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_TIMEOUT);
+                switch (s->timeout_stop_failure_mode) {
+
+                case SERVICE_TIMEOUT_TERMINATE:
+                        log_unit_warning(UNIT(s), "Stopping timed out. Terminating.");
+                        service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_TIMEOUT);
+                        break;
+
+                case SERVICE_TIMEOUT_ABORT:
+                        log_unit_warning(UNIT(s), "Stopping timed out. Aborting.");
+                        service_enter_signal(s, SERVICE_STOP_WATCHDOG, SERVICE_FAILURE_TIMEOUT);
+                        break;
+
+                case SERVICE_TIMEOUT_KILL:
+                        if (s->kill_context.send_sigkill) {
+                                log_unit_warning(UNIT(s), "Stopping timed out. Killing.");
+                                service_enter_signal(s, SERVICE_STOP_SIGKILL, SERVICE_FAILURE_TIMEOUT);
+                        } else {
+                                log_unit_warning(UNIT(s), "Stopping timed out. Skipping SIGKILL.");
+                                service_enter_stop_post(s, SERVICE_FAILURE_TIMEOUT);
+                        }
+                        break;
+
+                default:
+                        assert_not_reached("unknown timeout mode");
+                }
                 break;
 
         case SERVICE_STOP_WATCHDOG:
-                log_unit_warning(UNIT(s), "State 'stop-watchdog' timed out. Terminating.");
-                service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_TIMEOUT);
+                if (s->kill_context.send_sigkill) {
+                        log_unit_warning(UNIT(s), "State 'stop-watchdog' timed out. Killing.");
+                        service_enter_signal(s, SERVICE_STOP_SIGKILL, SERVICE_FAILURE_TIMEOUT);
+                } else {
+                        log_unit_warning(UNIT(s), "State 'stop-watchdog' timed out. Skipping SIGKILL.");
+                        service_enter_stop_post(s, SERVICE_FAILURE_TIMEOUT);
+                }
                 break;
 
         case SERVICE_STOP_SIGTERM:
-                if (s->kill_context.send_sigkill) {
+                if (s->timeout_stop_failure_mode == SERVICE_TIMEOUT_ABORT) {
+                        log_unit_warning(UNIT(s), "State 'stop-sigterm' timed out. Aborting.");
+                        service_enter_signal(s, SERVICE_STOP_WATCHDOG, SERVICE_FAILURE_TIMEOUT);
+                } else if (s->kill_context.send_sigkill) {
                         log_unit_warning(UNIT(s), "State 'stop-sigterm' timed out. Killing.");
                         service_enter_signal(s, SERVICE_STOP_SIGKILL, SERVICE_FAILURE_TIMEOUT);
                 } else {
@@ -3758,16 +3830,52 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us
                 break;
 
         case SERVICE_STOP_POST:
-                log_unit_warning(UNIT(s), "State 'stop-post' timed out. Terminating.");
-                service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_TIMEOUT);
+                switch (s->timeout_stop_failure_mode) {
+
+                case SERVICE_TIMEOUT_TERMINATE:
+                        log_unit_warning(UNIT(s), "State 'stop-post' timed out. Terminating.");
+                        service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_TIMEOUT);
+                        break;
+
+                case SERVICE_TIMEOUT_ABORT:
+                        log_unit_warning(UNIT(s), "State 'stop-post' timed out. Aborting.");
+                        service_enter_signal(s, SERVICE_FINAL_WATCHDOG, SERVICE_FAILURE_TIMEOUT);
+                        break;
+
+                case SERVICE_TIMEOUT_KILL:
+                        if (s->kill_context.send_sigkill) {
+                                log_unit_warning(UNIT(s), "State 'stop-post' timed out. Killing.");
+                                service_enter_signal(s, SERVICE_FINAL_SIGKILL, SERVICE_FAILURE_TIMEOUT);
+                        } else {
+                                log_unit_warning(UNIT(s), "State 'stop-post' timed out. Skipping SIGKILL. Entering failed mode.");
+                                service_enter_dead(s, SERVICE_FAILURE_TIMEOUT, false);
+                        }
+                        break;
+
+                default:
+                        assert_not_reached("unknown timeout mode");
+                }
                 break;
 
-        case SERVICE_FINAL_SIGTERM:
+        case SERVICE_FINAL_WATCHDOG:
                 if (s->kill_context.send_sigkill) {
-                        log_unit_warning(UNIT(s), "State 'stop-final-sigterm' timed out. Killing.");
+                        log_unit_warning(UNIT(s), "State 'final-watchdog' timed out. Killing.");
                         service_enter_signal(s, SERVICE_FINAL_SIGKILL, SERVICE_FAILURE_TIMEOUT);
                 } else {
-                        log_unit_warning(UNIT(s), "State 'stop-final-sigterm' timed out. Skipping SIGKILL. Entering failed mode.");
+                        log_unit_warning(UNIT(s), "State 'final-watchdog' timed out. Skipping SIGKILL. Entering failed mode.");
+                        service_enter_dead(s, SERVICE_FAILURE_TIMEOUT, false);
+                }
+                break;
+
+        case SERVICE_FINAL_SIGTERM:
+                if (s->timeout_stop_failure_mode == SERVICE_TIMEOUT_ABORT) {
+                        log_unit_warning(UNIT(s), "State 'final-sigterm' timed out. Aborting.");
+                        service_enter_signal(s, SERVICE_FINAL_WATCHDOG, SERVICE_FAILURE_TIMEOUT);
+                } else if (s->kill_context.send_sigkill) {
+                        log_unit_warning(UNIT(s), "State 'final-sigterm' timed out. Killing.");
+                        service_enter_signal(s, SERVICE_FINAL_SIGKILL, SERVICE_FAILURE_TIMEOUT);
+                } else {
+                        log_unit_warning(UNIT(s), "State 'final-sigterm' timed out. Skipping SIGKILL. Entering failed mode.");
                         service_enter_dead(s, SERVICE_FAILURE_TIMEOUT, false);
                 }
 
@@ -3829,7 +3937,7 @@ static int service_dispatch_watchdog(sd_event_source *source, usec_t usec, void
         return 0;
 }
 
-static bool service_notify_message_authorized(Service *s, pid_t pid, char **tags, FDSet *fds) {
+static bool service_notify_message_authorized(Service *s, pid_t pid, FDSet *fds) {
         assert(s);
 
         if (s->notify_access == NOTIFY_NONE) {
@@ -3876,19 +3984,19 @@ static void service_force_watchdog(Service *s) {
 static void service_notify_message(
                 Unit *u,
                 const struct ucred *ucred,
-                char **tags,
+                char * const *tags,
                 FDSet *fds) {
 
         Service *s = SERVICE(u);
         bool notify_dbus = false;
         const char *e;
-        char **i;
+        char * const *i;
         int r;
 
         assert(u);
         assert(ucred);
 
-        if (!service_notify_message_authorized(SERVICE(u), ucred->pid, tags, fds))
+        if (!service_notify_message_authorized(SERVICE(u), ucred->pid, fds))
                 return;
 
         if (DEBUG_LOGGING) {
@@ -4059,7 +4167,7 @@ static void service_notify_message(
                         name = NULL;
                 }
 
-                (void) service_add_fd_store_set(s, fds, name);
+                (void) service_add_fd_store_set(s, fds, name, !strv_contains(tags, "FDPOLL=0"));
         }
 
         /* Notify clients about changed status or main pid */
@@ -4249,6 +4357,7 @@ static bool service_needs_console(Unit *u) {
                       SERVICE_STOP_SIGTERM,
                       SERVICE_STOP_SIGKILL,
                       SERVICE_STOP_POST,
+                      SERVICE_FINAL_WATCHDOG,
                       SERVICE_FINAL_SIGTERM,
                       SERVICE_FINAL_SIGKILL);
 }
@@ -4403,6 +4512,14 @@ static const char* const service_result_table[_SERVICE_RESULT_MAX] = {
 
 DEFINE_STRING_TABLE_LOOKUP(service_result, ServiceResult);
 
+static const char* const service_timeout_failure_mode_table[_SERVICE_TIMEOUT_FAILURE_MODE_MAX] = {
+        [SERVICE_TIMEOUT_TERMINATE] = "terminate",
+        [SERVICE_TIMEOUT_ABORT] = "abort",
+        [SERVICE_TIMEOUT_KILL] = "kill",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(service_timeout_failure_mode, ServiceTimeoutFailureMode);
+
 const UnitVTable service_vtable = {
         .object_size = sizeof(Service),
         .exec_context_offset = offsetof(Service, exec_context),
@@ -4440,6 +4557,9 @@ const UnitVTable service_vtable = {
         .clean = service_clean,
         .can_clean = service_can_clean,
 
+        .freeze = unit_freeze_vtable_common,
+        .thaw = unit_thaw_vtable_common,
+
         .serialize = service_serialize,
         .deserialize_item = service_deserialize_item,
 
@@ -4463,7 +4583,6 @@ const UnitVTable service_vtable = {
 
         .bus_name_owner_change = service_bus_name_owner_change,
 
-        .bus_vtable = bus_service_vtable,
         .bus_set_property = bus_service_set_property,
         .bus_commit_properties = bus_service_commit_properties,
 
index 11e861a3d4325bf92635662811a0cd648de4c10e..4423f893bb743394379b25fc0d0047372cb660d3 100644 (file)
@@ -74,12 +74,21 @@ typedef enum ServiceResult {
         _SERVICE_RESULT_INVALID = -1
 } ServiceResult;
 
+typedef enum ServiceTimeoutFailureMode {
+        SERVICE_TIMEOUT_TERMINATE,
+        SERVICE_TIMEOUT_ABORT,
+        SERVICE_TIMEOUT_KILL,
+        _SERVICE_TIMEOUT_FAILURE_MODE_MAX,
+        _SERVICE_TIMEOUT_FAILURE_MODE_INVALID = -1
+} ServiceTimeoutFailureMode;
+
 struct ServiceFDStore {
         Service *service;
 
         int fd;
         char *fdname;
         sd_event_source *event_source;
+        bool do_poll;
 
         LIST_FIELDS(ServiceFDStore, fd_store);
 };
@@ -102,6 +111,8 @@ struct Service {
         usec_t timeout_abort_usec;
         bool timeout_abort_set;
         usec_t runtime_max_usec;
+        ServiceTimeoutFailureMode timeout_start_failure_mode;
+        ServiceTimeoutFailureMode timeout_stop_failure_mode;
 
         dual_timestamp watchdog_timestamp;
         usec_t watchdog_usec;            /* the requested watchdog timeout in the unit file */
@@ -199,6 +210,11 @@ static inline usec_t service_timeout_abort_usec(Service *s) {
         return s->timeout_abort_set ? s->timeout_abort_usec : s->timeout_stop_usec;
 }
 
+static inline usec_t service_get_watchdog_usec(Service *s) {
+        assert(s);
+        return s->watchdog_override_enable ? s->watchdog_override_usec : s->watchdog_original_usec;
+}
+
 extern const UnitVTable service_vtable;
 
 int service_set_socket_fd(Service *s, int fd, struct Socket *socket, bool selinux_context_net);
@@ -222,6 +238,9 @@ NotifyState notify_state_from_string(const char *s) _pure_;
 const char* service_result_to_string(ServiceResult i) _const_;
 ServiceResult service_result_from_string(const char *s) _pure_;
 
+const char* service_timeout_failure_mode_to_string(ServiceTimeoutFailureMode i) _const_;
+ServiceTimeoutFailureMode service_timeout_failure_mode_from_string(const char *s) _pure_;
+
 DEFINE_CAST(SERVICE, Service);
 
 #define STATUS_TEXT_MAX (16U*1024U)
index d97a262786be3d0ad873cd1615256b96bedd83d5..f4f63fcb5b1a23e33c9c879202fc05830cd3a75f 100644 (file)
@@ -5,6 +5,7 @@
 #include "alloc-util.h"
 #include "dbus-slice.h"
 #include "dbus-unit.h"
+#include "fd-util.h"
 #include "log.h"
 #include "serialize.h"
 #include "slice.h"
@@ -347,6 +348,82 @@ static void slice_enumerate_perpetual(Manager *m) {
                 (void) slice_make_perpetual(m, SPECIAL_SYSTEM_SLICE, NULL);
 }
 
+static bool slice_freezer_action_supported_by_children(Unit *s) {
+        Unit *member;
+        void *v;
+        Iterator i;
+
+        assert(s);
+
+        HASHMAP_FOREACH_KEY(v, member, s->dependencies[UNIT_BEFORE], i) {
+                int r;
+
+                if (UNIT_DEREF(member->slice) != s)
+                        continue;
+
+                if (member->type == UNIT_SLICE) {
+                        r = slice_freezer_action_supported_by_children(member);
+                        if (!r)
+                                return r;
+                }
+
+                if (!UNIT_VTABLE(member)->freeze)
+                        return false;
+        }
+
+        return true;
+}
+
+static int slice_freezer_action(Unit *s, FreezerAction action) {
+        Unit *member;
+        void *v;
+        Iterator i;
+        int r;
+
+        assert(s);
+        assert(IN_SET(action, FREEZER_FREEZE, FREEZER_THAW));
+
+        if (!slice_freezer_action_supported_by_children(s))
+                return log_unit_warning(s, "Requested freezer operation is not supported by all children of the slice");
+
+        HASHMAP_FOREACH_KEY(v, member, s->dependencies[UNIT_BEFORE], i) {
+                if (UNIT_DEREF(member->slice) != s)
+                        continue;
+
+                if (action == FREEZER_FREEZE)
+                        r = UNIT_VTABLE(member)->freeze(member);
+                else
+                        r = UNIT_VTABLE(member)->thaw(member);
+
+                if (r < 0)
+                        return r;
+        }
+
+        r = unit_cgroup_freezer_action(s, action);
+        if (r < 0)
+                return r;
+
+        return 1;
+}
+
+static int slice_freeze(Unit *s) {
+        assert(s);
+
+        return slice_freezer_action(s, FREEZER_FREEZE);
+}
+
+static int slice_thaw(Unit *s) {
+        assert(s);
+
+        return slice_freezer_action(s, FREEZER_THAW);
+}
+
+static bool slice_can_freeze(Unit *s) {
+        assert(s);
+
+        return slice_freezer_action_supported_by_children(s);
+}
+
 const UnitVTable slice_vtable = {
         .object_size = sizeof(Slice),
         .cgroup_context_offset = offsetof(Slice, cgroup_context),
@@ -371,13 +448,16 @@ const UnitVTable slice_vtable = {
 
         .kill = slice_kill,
 
+        .freeze = slice_freeze,
+        .thaw = slice_thaw,
+        .can_freeze = slice_can_freeze,
+
         .serialize = slice_serialize,
         .deserialize_item = slice_deserialize_item,
 
         .active_state = slice_active_state,
         .sub_state_to_string = slice_sub_state_to_string,
 
-        .bus_vtable = bus_slice_vtable,
         .bus_set_property = bus_slice_set_property,
         .bus_commit_properties = bus_slice_commit_properties,
 
index 4a0e3a253e854d23c71b00d128ba66ad5ac8b44b..127195c9fe811814b3788adffae3f24357fdbbbf 100644 (file)
@@ -205,38 +205,25 @@ static int socket_arm_timer(Socket *s, usec_t usec) {
         return 0;
 }
 
-int socket_instantiate_service(Socket *s) {
-        _cleanup_free_ char *prefix = NULL, *name = NULL;
+static int socket_instantiate_service(Socket *s, int cfd) {
+        Unit *service;
         int r;
-        Unit *u;
 
         assert(s);
+        assert(cfd >= 0);
 
-        /* This fills in s->service if it isn't filled in yet. For
-         * Accept=yes sockets we create the next connection service
-         * here. For Accept=no this is mostly a NOP since the service
-         * is figured out at load time anyway. */
+        /* This fills in s->service if it isn't filled in yet. For Accept=yes sockets we create the next
+         * connection service here. For Accept=no this is mostly a NOP since the service is figured out at
+         * load time anyway. */
 
-        if (UNIT_DEREF(s->service))
-                return 0;
-
-        if (!s->accept)
-                return 0;
-
-        r = unit_name_to_prefix(UNIT(s)->id, &prefix);
+        r = socket_load_service_unit(s, cfd, &service);
         if (r < 0)
                 return r;
 
-        if (asprintf(&name, "%s@%u.service", prefix, s->n_accepted) < 0)
-                return -ENOMEM;
+        unit_ref_set(&s->service, UNIT(s), service);
 
-        r = manager_load_unit(UNIT(s)->manager, name, NULL, NULL, &u);
-        if (r < 0)
-                return r;
-
-        unit_ref_set(&s->service, UNIT(s), u);
-
-        return unit_add_two_dependencies(UNIT(s), UNIT_BEFORE, UNIT_TRIGGERS, u, false, UNIT_DEPENDENCY_IMPLICIT);
+        return unit_add_two_dependencies(UNIT(s), UNIT_BEFORE, UNIT_TRIGGERS, service,
+                                         false, UNIT_DEPENDENCY_IMPLICIT);
 }
 
 static bool have_non_accept_socket(Socket *s) {
@@ -635,6 +622,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
                 "%sBroadcast: %s\n"
                 "%sPassCredentials: %s\n"
                 "%sPassSecurity: %s\n"
+                "%sPassPacketInfo: %s\n"
                 "%sTCPCongestion: %s\n"
                 "%sRemoveOnStop: %s\n"
                 "%sWritable: %s\n"
@@ -654,6 +642,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
                 prefix, yes_no(s->broadcast),
                 prefix, yes_no(s->pass_cred),
                 prefix, yes_no(s->pass_sec),
+                prefix, yes_no(s->pass_pktinfo),
                 prefix, strna(s->tcp_congestion),
                 prefix, yes_no(s->remove_on_stop),
                 prefix, yes_no(s->writable),
@@ -1070,6 +1059,12 @@ static void socket_apply_socket_options(Socket *s, int fd) {
                         log_unit_warning_errno(UNIT(s), r, "SO_PASSSEC failed: %m");
         }
 
+        if (s->pass_pktinfo) {
+                r = socket_pass_pktinfo(fd, true);
+                if (r < 0)
+                        log_unit_warning_errno(UNIT(s), r, "Failed to enable packet info socket option: %m");
+        }
+
         if (s->priority >= 0) {
                 r = setsockopt_int(fd, SOL_SOCKET, SO_PRIORITY, s->priority);
                 if (r < 0)
@@ -1398,37 +1393,81 @@ clear:
         return r;
 }
 
+int socket_load_service_unit(Socket *s, int cfd, Unit **ret) {
+        /* Figure out what the unit that will be used to handle the connections on the socket looks like.
+         *
+         * If cfd < 0, then we don't have a connection yet. In case of Accept=yes sockets, use a fake
+         * instance name.
+         */
+
+        if (UNIT_ISSET(s->service)) {
+                *ret = UNIT_DEREF(s->service);
+                return 0;
+        }
+
+        if (!s->accept)
+                return -ENODATA;
+
+        /* Build the instance name and load the unit */
+        _cleanup_free_ char *prefix = NULL, *instance = NULL, *name = NULL;
+        int r;
+
+        r = unit_name_to_prefix(UNIT(s)->id, &prefix);
+        if (r < 0)
+                return r;
+
+        if (cfd >= 0) {
+                r = instance_from_socket(cfd, s->n_accepted, &instance);
+                if (r == -ENOTCONN)
+                        /* ENOTCONN is legitimate if TCP RST was received.
+                         * This connection is over, but the socket unit lives on. */
+                        return log_unit_debug_errno(UNIT(s), r,
+                                                    "Got ENOTCONN on incoming socket, assuming aborted connection attempt, ignoring.");
+                if (r < 0)
+                        return r;
+        }
+
+        /* For accepting sockets, we don't know how the instance will be called until we get a connection and
+         * can figure out what the peer name is. So let's use "internal" as the instance to make it clear
+         * that this is not an actual peer name. We use "unknown" when we cannot figure out the peer. */
+        r = unit_name_build(prefix, instance ?: "internal", ".service", &name);
+        if (r < 0)
+                return r;
+
+        return manager_load_unit(UNIT(s)->manager, name, NULL, NULL, ret);
+}
+
 static int socket_determine_selinux_label(Socket *s, char **ret) {
-        Service *service;
-        ExecCommand *c;
-        _cleanup_free_ char *path = NULL;
         int r;
 
         assert(s);
         assert(ret);
 
         if (s->selinux_context_from_net) {
-                /* If this is requested, get label from the network label */
+                /* If this is requested, get the label from the network label */
 
                 r = mac_selinux_get_our_label(ret);
                 if (r == -EOPNOTSUPP)
                         goto no_label;
 
         } else {
-                /* Otherwise, get it from the executable we are about to start */
-                r = socket_instantiate_service(s);
-                if (r < 0)
-                        return r;
+                /* Otherwise, get it from the executable we are about to start. */
+
+                Unit *service;
+                ExecCommand *c;
+                _cleanup_free_ char *path = NULL;
 
-                if (!UNIT_ISSET(s->service))
+                r = socket_load_service_unit(s, -1, &service);
+                if (r == -ENODATA)
                         goto no_label;
+                if (r < 0)
+                        return r;
 
-                service = SERVICE(UNIT_DEREF(s->service));
-                c = service->exec_command[SERVICE_EXEC_START];
+                c = SERVICE(service)->exec_command[SERVICE_EXEC_START];
                 if (!c)
                         goto no_label;
 
-                r = chase_symlinks(c->path, service->exec_context.root_directory, CHASE_PREFIX_ROOT, &path, NULL);
+                r = chase_symlinks(c->path, SERVICE(service)->exec_context.root_directory, CHASE_PREFIX_ROOT, &path, NULL);
                 if (r < 0)
                         goto no_label;
 
@@ -1614,8 +1653,8 @@ static int socket_open_fds(Socket *_s) {
                 case SOCKET_SOCKET:
 
                         if (!know_label) {
-                                /* Figure out label, if we don't it know yet. We do it once, for the first socket where
-                                 * we need this and remember it for the rest. */
+                                /* Figure out the label, if we don't it know yet. We do it once for the first
+                                 * socket where we need this and remember it for the rest. */
 
                                 r = socket_determine_selinux_label(s, &label);
                                 if (r < 0)
@@ -2035,6 +2074,8 @@ static void socket_enter_dead(Socket *s, SocketResult f) {
         else
                 unit_log_failure(UNIT(s), socket_result_to_string(s->result));
 
+        unit_warn_leftover_processes(UNIT(s), unit_log_leftover_process_stop);
+
         socket_set_state(s, s->result != SOCKET_SUCCESS ? SOCKET_FAILED : SOCKET_DEAD);
 
         s->exec_runtime = exec_runtime_unref(s->exec_runtime, true);
@@ -2237,7 +2278,7 @@ static void socket_enter_start_pre(Socket *s) {
 
         socket_unwatch_control_pid(s);
 
-        unit_warn_leftover_processes(UNIT(s));
+        unit_warn_leftover_processes(UNIT(s), unit_log_leftover_process_start);
 
         s->control_command_id = SOCKET_EXEC_START_PRE;
         s->control_command = s->exec_command[SOCKET_EXEC_START_PRE];
@@ -2330,7 +2371,6 @@ static void socket_enter_running(Socket *s, int cfd) {
 
                 socket_set_state(s, SOCKET_RUNNING);
         } else {
-                _cleanup_free_ char *prefix = NULL, *instance = NULL, *name = NULL;
                 _cleanup_(socket_peer_unrefp) SocketPeer *p = NULL;
                 Service *service;
 
@@ -2342,9 +2382,9 @@ static void socket_enter_running(Socket *s, int cfd) {
 
                 if (s->max_connections_per_source > 0) {
                         r = socket_acquire_peer(s, cfd, &p);
-                        if (r < 0) {
+                        if (r < 0)
                                 goto refuse;
-                        } else if (r > 0 && p->n_ref > s->max_connections_per_source) {
+                        if (r > 0 && p->n_ref > s->max_connections_per_source) {
                                 _cleanup_free_ char *t = NULL;
 
                                 (void) sockaddr_pretty(&p->peer.sa, p->peer_salen, true, false, &t);
@@ -2356,30 +2396,7 @@ static void socket_enter_running(Socket *s, int cfd) {
                         }
                 }
 
-                r = socket_instantiate_service(s);
-                if (r < 0)
-                        goto fail;
-
-                r = instance_from_socket(cfd, s->n_accepted, &instance);
-                if (r < 0) {
-                        if (r != -ENOTCONN)
-                                goto fail;
-
-                        /* ENOTCONN is legitimate if TCP RST was received.
-                         * This connection is over, but the socket unit lives on. */
-                        log_unit_debug(UNIT(s), "Got ENOTCONN on incoming socket, assuming aborted connection attempt, ignoring.");
-                        goto refuse;
-                }
-
-                r = unit_name_to_prefix(UNIT(s)->id, &prefix);
-                if (r < 0)
-                        goto fail;
-
-                r = unit_name_build(prefix, instance, ".service", &name);
-                if (r < 0)
-                        goto fail;
-
-                r = unit_add_name(UNIT_DEREF(s->service), name);
+                r = socket_instantiate_service(s, cfd);
                 if (r < 0)
                         goto fail;
 
@@ -2387,21 +2404,20 @@ static void socket_enter_running(Socket *s, int cfd) {
                 unit_ref_unset(&s->service);
 
                 s->n_accepted++;
-                unit_choose_id(UNIT(service), name);
 
                 r = service_set_socket_fd(service, cfd, s, s->selinux_context_from_net);
                 if (r < 0)
                         goto fail;
 
-                cfd = -1; /* We passed ownership of the fd to the service now. Forget it here. */
+                TAKE_FD(cfd); /* We passed ownership of the fd to the service now. Forget it here. */
                 s->n_connections++;
 
                 service->peer = TAKE_PTR(p); /* Pass ownership of the peer reference */
 
                 r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(service), JOB_REPLACE, NULL, &error, NULL);
                 if (r < 0) {
-                        /* We failed to activate the new service, but it still exists. Let's make sure the service
-                         * closes and forgets the connection fd again, immediately. */
+                        /* We failed to activate the new service, but it still exists. Let's make sure the
+                         * service closes and forgets the connection fd again, immediately. */
                         service_close_socket_fd(service);
                         goto fail;
                 }
@@ -3462,7 +3478,6 @@ const UnitVTable socket_vtable = {
 
         .control_pid = socket_control_pid,
 
-        .bus_vtable = bus_socket_vtable,
         .bus_set_property = bus_socket_set_property,
         .bus_commit_properties = bus_socket_commit_properties,
 
index 9e0be15ba8d6e5226464ca9fd82b9905a5d75b6f..bb14e6b0f713e123d869e71df2d48b8d6625dd40 100644 (file)
@@ -121,6 +121,7 @@ struct Socket {
         bool broadcast;
         bool pass_cred;
         bool pass_sec;
+        bool pass_pktinfo;
 
         /* Only for INET6 sockets: issue IPV6_V6ONLY sockopt */
         SocketAddressBindIPv6Only bind_ipv6_only;
@@ -165,7 +166,7 @@ void socket_connection_unref(Socket *s);
 
 void socket_free_ports(Socket *s);
 
-int socket_instantiate_service(Socket *s);
+int socket_load_service_unit(Socket *s, int cfd, Unit **ret);
 
 char *socket_fdname(Socket *s);
 
index c5945371df7c7b8f38b359c40bd8c2b820bd2c3f..20179de2d26df384bb43e7af27494dd4323ba1ba 100644 (file)
@@ -701,6 +701,7 @@ static void swap_enter_dead(Swap *s, SwapResult f) {
                 s->result = f;
 
         unit_log_result(UNIT(s), s->result == SWAP_SUCCESS, swap_result_to_string(s->result));
+        unit_warn_leftover_processes(UNIT(s), unit_log_leftover_process_stop);
         swap_set_state(s, s->result != SWAP_SUCCESS ? SWAP_FAILED : SWAP_DEAD);
 
         s->exec_runtime = exec_runtime_unref(s->exec_runtime, true);
@@ -788,7 +789,7 @@ static void swap_enter_activating(Swap *s) {
 
         assert(s);
 
-        unit_warn_leftover_processes(UNIT(s));
+        unit_warn_leftover_processes(UNIT(s), unit_log_leftover_process_start);
 
         s->control_command_id = SWAP_EXEC_ACTIVATE;
         s->control_command = s->exec_command + SWAP_EXEC_ACTIVATE;
@@ -1655,7 +1656,6 @@ const UnitVTable swap_vtable = {
 
         .control_pid = swap_control_pid,
 
-        .bus_vtable = bus_swap_vtable,
         .bus_set_property = bus_swap_set_property,
         .bus_commit_properties = bus_swap_commit_properties,
 
index 8112125468a5b9d411ed7e0447bb3b35f3e7e70a..40bb548887110bb967038e5546661fc725be2071 100644 (file)
@@ -16,6 +16,7 @@
 #LogTarget=journal-or-kmsg
 #LogColor=yes
 #LogLocation=no
+#LogTime=no
 #DumpCore=yes
 #ShowStatus=yes
 #CrashChangeVT=no
index 8331832c7a208dcfe7d380cd30599682431da8f8..8424837824b5ab38ad678e29af7c7f53515cebd9 100644 (file)
@@ -7,36 +7,93 @@
 #  the Free Software Foundation; either version 2.1 of the License, or
 #  (at your option) any later version.
 
+# Names with prefixes are preferred, and the run-together names should be
+# considered deprecated (though there is no plan to remove them). New names
+# shall have underscores.
+
 prefix=@prefix@
-rootprefix=@rootprefix_noslash@
-sysconfdir=@sysconfdir@
-systemdutildir=${rootprefix}/lib/systemd
-systemdsystemunitdir=${rootprefix}/lib/systemd/system
-systemdsystempresetdir=${rootprefix}/lib/systemd/system-preset
-systemduserunitdir=${prefix}/lib/systemd/user
-systemduserpresetdir=${prefix}/lib/systemd/user-preset
-systemdsystemconfdir=${sysconfdir}/systemd/system
-systemduserconfdir=${sysconfdir}/systemd/user
-systemdsystemunitpath=${systemdsystemconfdir}:/etc/systemd/system:/run/systemd/system:/usr/local/lib/systemd/system:${systemdsystemunitdir}:/usr/lib/systemd/system:/lib/systemd/system
-systemduserunitpath=${systemduserconfdir}:/etc/systemd/user:/run/systemd/user:/usr/local/lib/systemd/user:/usr/local/share/systemd/user:${systemduserunitdir}:/usr/lib/systemd/user:/usr/share/systemd/user
-systemdsystemgeneratordir=${rootprefix}/lib/systemd/system-generators
-systemdusergeneratordir=${prefix}/lib/systemd/user-generators
-systemdsystemgeneratorpath=/run/systemd/system-generators:/etc/systemd/system-generators:/usr/local/lib/systemd/system-generators:${systemdsystemgeneratordir}
-systemdusergeneratorpath=/run/systemd/user-generators:/etc/systemd/user-generators:/usr/local/lib/systemd/user-generators:${systemdusergeneratordir}
-systemdsleepdir=${rootprefix}/lib/systemd/system-sleep
-systemdshutdowndir=${rootprefix}/lib/systemd/system-shutdown
-tmpfilesdir=${prefix}/lib/tmpfiles.d
-sysusersdir=${prefix}/lib/sysusers.d
-sysctldir=${prefix}/lib/sysctl.d
-binfmtdir=${prefix}/lib/binfmt.d
-modulesloaddir=${prefix}/lib/modules-load.d
-catalogdir=${prefix}/lib/systemd/catalog
-systemuidmax=@systemuidmax@
-systemgidmax=@systemgidmax@
-dynamicuidmin=@dynamicuidmin@
-dynamicuidmax=@dynamicuidmax@
-containeruidbasemin=@containeruidbasemin@
-containeruidbasemax=@containeruidbasemax@
+root_prefix=@rootprefix_noslash@
+rootprefix=${root_prefix}
+sysconf_dir=@sysconfdir@
+sysconfdir=${sysconf_dir}
+
+systemd_util_dir=${root_prefix}/lib/systemd
+systemdutildir=${systemd_util_dir}
+
+systemd_system_unit_dir=${rootprefix}/lib/systemd/system
+systemdsystemunitdir=${systemd_system_unit_dir}
+
+systemd_system_preset_dir=${rootprefix}/lib/systemd/system-preset
+systemdsystempresetdir=${systemd_system_preset_dir}
+
+systemd_user_unit_dir=${prefix}/lib/systemd/user
+systemduserunitdir=${systemd_user_unit_dir}
+
+systemd_user_preset_dir=${prefix}/lib/systemd/user-preset
+systemduserpresetdir=${systemd_user_preset_dir}
+
+systemd_system_conf_dir=${sysconfdir}/systemd/system
+systemdsystemconfdir=${systemd_system_conf_dir}
+
+systemd_user_conf_dir=${sysconfdir}/systemd/user
+systemduserconfdir=${systemd_user_conf_dir}
+
+systemd_system_unit_path=${systemd_system_conf_dir}:/etc/systemd/system:/run/systemd/system:/usr/local/lib/systemd/system:${systemd_system_unit_dir}:/usr/lib/systemd/system:/lib/systemd/system
+systemdsystemunitpath=${systemd_system_unit_path}
+
+systemd_user_unit_path=${systemd_user_conf_dir}:/etc/systemd/user:/run/systemd/user:/usr/local/lib/systemd/user:/usr/local/share/systemd/user:${systemd_user_unit_dir}:/usr/lib/systemd/user:/usr/share/systemd/user
+systemduserunitpath=${systemd_user_unit_path}
+
+systemd_system_generator_dir=${root_prefix}/lib/systemd/system-generators
+systemdsystemgeneratordir=${systemd_system_generator_dir}
+
+systemd_user_generator_dir=${prefix}/lib/systemd/user-generators
+systemdusergeneratordir=${systemd_user_generator_dir}
+
+systemd_system_generator_path=/run/systemd/system-generators:/etc/systemd/system-generators:/usr/local/lib/systemd/system-generators:${systemd_system_generator_dir}
+systemdsystemgeneratorpath=${systemd_system_generator_path}
+
+systemd_user_generator_path=/run/systemd/user-generators:/etc/systemd/user-generators:/usr/local/lib/systemd/user-generators:${systemd_user_generator_dir}
+systemdusergeneratorpath=${systemd_user_generator_path}
+
+systemd_sleep_dir=${root_prefix}/lib/systemd/system-sleep
+systemdsleepdir=${systemd_sleep_dir}
+
+systemd_shutdown_dir=${root_prefix}/lib/systemd/system-shutdown
+systemdshutdowndir=${systemd_shutdown_dir}
+
+tmpfiles_dir=${prefix}/lib/tmpfiles.d
+tmpfilesdir=${tmpfiles_dir}
+
+sysusers_dir=${rootprefix}/lib/sysusers.d
+sysusersdir=${sysusers_dir}
+
+sysctl_dir=${rootprefix}/lib/sysctl.d
+sysctldir=${sysctl_dir}
+
+binfmt_dir=${rootprefix}/lib/binfmt.d
+binfmtdir=${binfmt_dir}
+
+modules_load_dir=${rootprefix}/lib/modules-load.d
+modulesloaddir=${modules_load_dir}
+
+catalog_dir=${prefix}/lib/systemd/catalog
+catalogdir=${catalog_dir}
+
+system_uid_max=@systemuidmax@
+systemuidmax=${system_uid_max}
+system_gid_max=@systemgidmax@
+systemgidmax=${system_gid_max}
+
+dynamic_uid_min=@dynamicuidmin@
+dynamicuidmin=${dynamic_uid_min}
+dynamic_uid_max=@dynamicuidmax@
+dynamicuidmax=${dynamic_uid_max}
+
+container_uid_base_min=@containeruidbasemin@
+containeruidbasemin=${container_uid_base_min}
+container_uid_base_max=@containeruidbasemax@
+containeruidbasemax=${container_uid_base_max}
 
 Name: systemd
 Description: systemd System and Service Manager
index e433e46efe681c4804bd85733d547fd2cc9a3f85..65affcec191a0b930702410ff1078ae5927f3acf 100644 (file)
@@ -209,8 +209,6 @@ const UnitVTable target_vtable = {
         .active_state = target_active_state,
         .sub_state_to_string = target_sub_state_to_string,
 
-        .bus_vtable = bus_target_vtable,
-
         .status_message_formats = {
                 .finished_start_job = {
                         [JOB_DONE]       = "Reached target %s.",
index 57d979d52da1e58d3e7b79319cae29418ee9cf66..03a9c14f766a8e72035de2dcaf8e518a4a55b6e4 100644 (file)
@@ -367,7 +367,7 @@ static void timer_enter_waiting(Timer *t, bool time_change) {
                         continue;
 
                 if (v->base == TIMER_CALENDAR) {
-                        usec_t b;
+                        usec_t b, rebased;
 
                         /* If we know the last time this was
                          * triggered, schedule the job based relative
@@ -387,12 +387,14 @@ static void timer_enter_waiting(Timer *t, bool time_change) {
                         if (r < 0)
                                 continue;
 
-                        /* To make the delay due to RandomizedDelaySec= work even at boot,
-                         * if the scheduled time has already passed, set the time when systemd
-                         * first started as the scheduled time.
-                         * Also, we don't have to check t->persistent since the logic implicitly express true. */
-                        if (v->next_elapse < UNIT(t)->manager->timestamps[MANAGER_TIMESTAMP_USERSPACE].realtime)
-                                v->next_elapse = UNIT(t)->manager->timestamps[MANAGER_TIMESTAMP_USERSPACE].realtime;
+                        /* To make the delay due to RandomizedDelaySec= work even at boot, if the scheduled
+                         * time has already passed, set the time when systemd first started as the scheduled
+                         * time. Note that we base this on the monotonic timestamp of the boot, not the
+                         * realtime one, since the wallclock might have been off during boot. */
+                        rebased = map_clock_usec(UNIT(t)->manager->timestamps[MANAGER_TIMESTAMP_USERSPACE].monotonic,
+                                                 CLOCK_MONOTONIC, CLOCK_REALTIME);
+                        if (v->next_elapse < rebased)
+                                v->next_elapse = rebased;
 
                         if (!found_realtime)
                                 t->next_elapse_realtime = v->next_elapse;
@@ -925,6 +927,5 @@ const UnitVTable timer_vtable = {
         .time_change = timer_time_change,
         .timezone_change = timer_timezone_change,
 
-        .bus_vtable = bus_timer_vtable,
         .bus_set_property = bus_timer_set_property,
 };
index 6dc4e95bebc45aa7af0f5e9f9b77538438d85d04..4a57b8e3f9c32f65db09cff8f8b76f97f260f7d5 100644 (file)
@@ -336,9 +336,8 @@ static char* merge_unit_ids(const char* unit_log_field, char **pairs) {
 
         STRV_FOREACH_PAIR(unit_id, job_type, pairs) {
                 next = strlen(unit_log_field) + strlen(*unit_id);
-                if (!GREEDY_REALLOC(ans, alloc, size + next + 1)) {
+                if (!GREEDY_REALLOC(ans, alloc, size + next + 1))
                         return mfree(ans);
-                }
 
                 sprintf(ans + size, "%s%s", unit_log_field, *unit_id);
                 if (*(unit_id+1))
@@ -657,6 +656,10 @@ static int transaction_apply(
                 assert(!j->transaction_prev);
                 assert(!j->transaction_next);
 
+                r = hashmap_ensure_allocated(&m->jobs, NULL);
+                if (r < 0)
+                        return r;
+
                 r = hashmap_put(m->jobs, UINT32_TO_PTR(j->id), j);
                 if (r < 0)
                         goto rollback;
@@ -951,6 +954,24 @@ int transaction_add_job_and_dependencies(
 
         if (type != JOB_STOP) {
                 r = bus_unit_validate_load_state(unit, e);
+                /* The time-based cache allows to start new units without daemon-reload,
+                 * but if they are already referenced (because of dependencies or ordering)
+                 * then we have to force a load of the fragment. As an optimization, check
+                 * first if anything in the usual paths was modified since the last time
+                 * the cache was loaded. Also check if the last time an attempt to load the
+                 * unit was made was before the most recent cache refresh, so that we know
+                 * we need to try again - even if the cache is current, it might have been
+                 * updated in a different context before we had a chance to retry loading
+                 * this particular unit.
+                 * Given building up the transaction is a synchronous operation, attempt
+                 * to load the unit immediately. */
+                if (r < 0 && manager_unit_file_maybe_loadable_from_cache(unit)) {
+                        unit->load_state = UNIT_STUB;
+                        r = unit_load(unit);
+                        if (r < 0 || unit->load_state == UNIT_STUB)
+                                unit->load_state = UNIT_NOT_FOUND;
+                        r = bus_unit_validate_load_state(unit, e);
+                }
                 if (r < 0)
                         return r;
         }
index 2daaaf997175c142c865d81a649bf28578e8889d..4fee5dc6dc3e83362397d5eaf1e474102dd70578 100644 (file)
@@ -186,8 +186,14 @@ int unit_name_printf(const Unit *u, const char* format, char **ret) {
          * %u: the username of the running user
          *
          * %m: the machine ID of the running system
-         * %H: the host name of the running system
          * %b: the boot ID of the running system
+         * %H: the hostname of the running system
+         * %v: the kernel version
+         * %a: the native userspace architecture
+         * %o: the OS ID according to /etc/os-release
+         * %w: the OS version ID, according to /etc/os-release
+         * %B: the OS build ID, according to /etc/os-release
+         * %W: the OS variant ID, according to /etc/os-release
          */
 
         const Specifier table[] = {
@@ -203,8 +209,14 @@ int unit_name_printf(const Unit *u, const char* format, char **ret) {
                 { 'u', specifier_user_name,           NULL },
 
                 { 'm', specifier_machine_id,          NULL },
-                { 'H', specifier_host_name,           NULL },
                 { 'b', specifier_boot_id,             NULL },
+                { 'H', specifier_host_name,           NULL },
+                { 'v', specifier_kernel_release,      NULL },
+                { 'a', specifier_architecture,        NULL },
+                { 'o', specifier_os_id,               NULL },
+                { 'w', specifier_os_version_id,       NULL },
+                { 'B', specifier_os_build_id,         NULL },
+                { 'W', specifier_os_variant_id,       NULL },
                 {}
         };
 
@@ -279,6 +291,7 @@ int unit_full_printf(const Unit *u, const char *format, char **ret) {
 
                 { 'm', specifier_machine_id,               NULL },
                 { 'H', specifier_host_name,                NULL },
+                { 'l', specifier_short_host_name,          NULL },
                 { 'b', specifier_boot_id,                  NULL },
                 { 'v', specifier_kernel_release,           NULL },
                 {}
index 92c37fc59a0230d2fc614905c044dabd4f4016ad..2c09def06f3b228450cea414b582287422175fe5 100644 (file)
@@ -93,10 +93,6 @@ Unit *unit_new(Manager *m, size_t size) {
         if (!u)
                 return NULL;
 
-        u->names = set_new(&string_hash_ops);
-        if (!u->names)
-                return mfree(u);
-
         u->manager = m;
         u->type = _UNIT_TYPE_INVALID;
         u->default_dependencies = true;
@@ -152,7 +148,8 @@ bool unit_has_name(const Unit *u, const char *name) {
         assert(u);
         assert(name);
 
-        return set_contains(u->names, (char*) name);
+        return streq_ptr(name, u->id) ||
+               set_contains(u->aliases, name);
 }
 
 static void unit_init(Unit *u) {
@@ -187,8 +184,16 @@ static void unit_init(Unit *u) {
         if (ec) {
                 exec_context_init(ec);
 
-                ec->keyring_mode = MANAGER_IS_SYSTEM(u->manager) ?
-                        EXEC_KEYRING_SHARED : EXEC_KEYRING_INHERIT;
+                if (MANAGER_IS_SYSTEM(u->manager))
+                        ec->keyring_mode = EXEC_KEYRING_SHARED;
+                else {
+                        ec->keyring_mode = EXEC_KEYRING_INHERIT;
+
+                        /* User manager might have its umask redefined by PAM or UMask=. In this
+                         * case let the units it manages inherit this value by default. They can
+                         * still tune this value through their own unit file */
+                        (void) get_process_umask(getpid_cached(), &ec->umask);
+                }
         }
 
         kc = unit_get_kill_context(u);
@@ -199,8 +204,25 @@ static void unit_init(Unit *u) {
                 UNIT_VTABLE(u)->init(u);
 }
 
+static int unit_add_alias(Unit *u, char *donated_name) {
+        int r;
+
+        /* Make sure that u->names is allocated. We may leave u->names
+         * empty if we fail later, but this is not a problem. */
+        r = set_ensure_allocated(&u->aliases, &string_hash_ops);
+        if (r < 0)
+                return r;
+
+        r = set_put(u->aliases, donated_name);
+        if (r < 0)
+                return r;
+        assert(r > 0);
+
+        return 0;
+}
+
 int unit_add_name(Unit *u, const char *text) {
-        _cleanup_free_ char *s = NULL, *i = NULL;
+        _cleanup_free_ char *name = NULL, *instance = NULL;
         UnitType t;
         int r;
 
@@ -208,90 +230,101 @@ int unit_add_name(Unit *u, const char *text) {
         assert(text);
 
         if (unit_name_is_valid(text, UNIT_NAME_TEMPLATE)) {
-
                 if (!u->instance)
-                        return -EINVAL;
+                        return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EINVAL),
+                                                    "instance is not set when adding name '%s': %m", text);
 
-                r = unit_name_replace_instance(text, u->instance, &s);
+                r = unit_name_replace_instance(text, u->instance, &name);
                 if (r < 0)
-                        return r;
+                        return log_unit_debug_errno(u, r,
+                                                    "failed to build instance name from '%s': %m", text);
         } else {
-                s = strdup(text);
-                if (!s)
+                name = strdup(text);
+                if (!name)
                         return -ENOMEM;
         }
 
-        if (set_contains(u->names, s))
+        if (unit_has_name(u, name))
                 return 0;
-        if (hashmap_contains(u->manager->units, s))
-                return -EEXIST;
 
-        if (!unit_name_is_valid(s, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
-                return -EINVAL;
+        if (hashmap_contains(u->manager->units, name))
+                return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EEXIST),
+                                            "unit already exist when adding name '%s': %m", name);
+
+        if (!unit_name_is_valid(name, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
+                return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EINVAL),
+                                            "name '%s' is invalid: %m", name);
 
-        t = unit_name_to_type(s);
+        t = unit_name_to_type(name);
         if (t < 0)
-                return -EINVAL;
+                return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EINVAL),
+                                            "failed to derive unit type from name '%s': %m", name);
 
         if (u->type != _UNIT_TYPE_INVALID && t != u->type)
-                return -EINVAL;
+                return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EINVAL),
+                                            "unit type is illegal: u->type(%d) and t(%d) for name '%s': %m",
+                                            u->type, t, name);
 
-        r = unit_name_to_instance(s, &i);
+        r = unit_name_to_instance(name, &instance);
         if (r < 0)
-                return r;
+                return log_unit_debug_errno(u, r, "failed to extract instance from name '%s': %m", name);
 
-        if (i && !unit_type_may_template(t))
-                return -EINVAL;
+        if (instance && !unit_type_may_template(t))
+                return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EINVAL), "templates are not allowed for name '%s': %m", name);
 
-        /* Ensure that this unit is either instanced or not instanced,
-         * but not both. Note that we do allow names with different
-         * instance names however! */
-        if (u->type != _UNIT_TYPE_INVALID && !u->instance != !i)
-                return -EINVAL;
+        /* Ensure that this unit either has no instance, or that the instance matches. */
+        if (u->type != _UNIT_TYPE_INVALID && !streq_ptr(u->instance, instance))
+                return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EINVAL),
+                                            "cannot add name %s, the instances don't match (\"%s\" != \"%s\").",
+                                            name, instance, u->instance);
 
-        if (!unit_type_may_alias(t) && !set_isempty(u->names))
-                return -EEXIST;
+        if (u->id && !unit_type_may_alias(t))
+                return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EEXIST),
+                                            "cannot add name %s, aliases are not allowed for %s units.",
+                                            name, unit_type_to_string(t));
 
         if (hashmap_size(u->manager->units) >= MANAGER_MAX_NAMES)
-                return -E2BIG;
+                return log_unit_warning_errno(u, SYNTHETIC_ERRNO(E2BIG), "cannot add name, manager has too many units: %m");
 
-        r = set_put(u->names, s);
+        /* Add name to the global hashmap first, because that's easier to undo */
+        r = hashmap_put(u->manager->units, name, u);
         if (r < 0)
-                return r;
-        assert(r > 0);
+                return log_unit_debug_errno(u, r, "add unit to hashmap failed for name '%s': %m", text);
 
-        r = hashmap_put(u->manager->units, s, u);
-        if (r < 0) {
-                (void) set_remove(u->names, s);
-                return r;
-        }
+        if (u->id) {
+                r = unit_add_alias(u, name); /* unit_add_alias() takes ownership of the name on success */
+                if (r < 0) {
+                        hashmap_remove(u->manager->units, name);
+                        return r;
+                }
+                TAKE_PTR(name);
+
+        } else {
+                /* A new name, we don't need the set yet. */
+                assert(u->type == _UNIT_TYPE_INVALID);
+                assert(!u->instance);
 
-        if (u->type == _UNIT_TYPE_INVALID) {
                 u->type = t;
-                u->id = s;
-                u->instance = TAKE_PTR(i);
+                u->id = TAKE_PTR(name);
+                u->instance = TAKE_PTR(instance);
 
                 LIST_PREPEND(units_by_type, u->manager->units_by_type[t], u);
-
                 unit_init(u);
         }
 
-        s = NULL;
-
         unit_add_to_dbus_queue(u);
         return 0;
 }
 
 int unit_choose_id(Unit *u, const char *name) {
         _cleanup_free_ char *t = NULL;
-        char *s, *i;
+        char *s;
         int r;
 
         assert(u);
         assert(name);
 
         if (unit_name_is_valid(name, UNIT_NAME_TEMPLATE)) {
-
                 if (!u->instance)
                         return -EINVAL;
 
@@ -302,21 +335,22 @@ int unit_choose_id(Unit *u, const char *name) {
                 name = t;
         }
 
-        /* Selects one of the names of this unit as the id */
-        s = set_get(u->names, (char*) name);
+        if (streq_ptr(u->id, name))
+                return 0; /* Nothing to do. */
+
+        /* Selects one of the aliases of this unit as the id */
+        s = set_get(u->aliases, (char*) name);
         if (!s)
                 return -ENOENT;
 
-        /* Determine the new instance from the new id */
-        r = unit_name_to_instance(s, &i);
-        if (r < 0)
-                return r;
-
-        u->id = s;
-
-        free(u->instance);
-        u->instance = i;
+        if (u->id) {
+                r = set_remove_and_put(u->aliases, name, u->id);
+                if (r < 0)
+                        return r;
+        } else
+                assert_se(set_remove(u->aliases, name)); /* see set_get() above… */
 
+        u->id = s; /* Old u->id is now stored in the set, and s is not stored anywhere */
         unit_add_to_dbus_queue(u);
 
         return 0;
@@ -484,9 +518,7 @@ static void bidi_set_free(Unit *u, Hashmap *h) {
         /* Frees the hashmap and makes sure we are dropped from the inverse pointers */
 
         HASHMAP_FOREACH_KEY(v, other, h, i) {
-                UnitDependency d;
-
-                for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++)
+                for (UnitDependency d = 0; d < _UNIT_DEPENDENCY_MAX; d++)
                         hashmap_remove(other->dependencies[d], u);
 
                 unit_add_to_gc_queue(other);
@@ -582,7 +614,6 @@ static void unit_done(Unit *u) {
 }
 
 void unit_free(Unit *u) {
-        UnitDependency d;
         Iterator i;
         char *t;
 
@@ -611,11 +642,14 @@ void unit_free(Unit *u) {
         sd_bus_slot_unref(u->match_bus_slot);
         sd_bus_track_unref(u->bus_track);
         u->deserialized_refs = strv_free(u->deserialized_refs);
+        u->pending_freezer_message = sd_bus_message_unref(u->pending_freezer_message);
 
         unit_free_requires_mounts_for(u);
 
-        SET_FOREACH(t, u->names, i)
+        SET_FOREACH(t, u->aliases, i)
                 hashmap_remove_value(u->manager->units, t, u);
+        if (u->id)
+                hashmap_remove_value(u->manager->units, u->id, u);
 
         if (!sd_id128_is_null(u->invocation_id))
                 hashmap_remove_value(u->manager->units_by_invocation_id, &u->invocation_id, u);
@@ -632,7 +666,7 @@ void unit_free(Unit *u) {
                 job_free(j);
         }
 
-        for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++)
+        for (UnitDependency d = 0; d < _UNIT_DEPENDENCY_MAX; d++)
                 bidi_set_free(u, u->dependencies[d]);
 
         if (u->on_console)
@@ -712,14 +746,46 @@ void unit_free(Unit *u) {
         free(u->instance);
 
         free(u->job_timeout_reboot_arg);
-
-        set_free_free(u->names);
-
         free(u->reboot_arg);
 
+        set_free_free(u->aliases);
+        free(u->id);
+
         free(u);
 }
 
+FreezerState unit_freezer_state(Unit *u) {
+        assert(u);
+
+        return u->freezer_state;
+}
+
+int unit_freezer_state_kernel(Unit *u, FreezerState *ret) {
+        char *values[1] = {};
+        int r;
+
+        assert(u);
+
+        r = cg_get_keyed_attribute(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, "cgroup.events",
+                                   STRV_MAKE("frozen"), values);
+        if (r < 0)
+                return r;
+
+        r = _FREEZER_STATE_INVALID;
+
+        if (values[0])  {
+                if (streq(values[0], "0"))
+                        r = FREEZER_RUNNING;
+                else if (streq(values[0], "1"))
+                        r = FREEZER_FROZEN;
+        }
+
+        free(values[0]);
+        *ret = r;
+
+        return 0;
+}
+
 UnitActiveState unit_active_state(Unit *u) {
         assert(u);
 
@@ -739,21 +805,6 @@ const char* unit_sub_state_to_string(Unit *u) {
         return UNIT_VTABLE(u)->sub_state_to_string(u);
 }
 
-static int set_complete_move(Set **s, Set **other) {
-        assert(s);
-        assert(other);
-
-        if (!other)
-                return 0;
-
-        if (*s)
-                return set_move(*s, *other);
-        else
-                *s = TAKE_PTR(*other);
-
-        return 0;
-}
-
 static int hashmap_complete_move(Hashmap **s, Hashmap **other) {
         assert(s);
         assert(other);
@@ -770,23 +821,28 @@ static int hashmap_complete_move(Hashmap **s, Hashmap **other) {
 }
 
 static int merge_names(Unit *u, Unit *other) {
-        char *t;
+        char *name;
         Iterator i;
         int r;
 
         assert(u);
         assert(other);
 
-        r = set_complete_move(&u->names, &other->names);
+        r = unit_add_alias(u, other->id);
         if (r < 0)
                 return r;
 
-        set_free_free(other->names);
-        other->names = NULL;
-        other->id = NULL;
+        r = set_move(u->aliases, other->aliases);
+        if (r < 0) {
+                set_remove(u->aliases, other->id);
+                return r;
+        }
+
+        TAKE_PTR(other->id);
+        other->aliases = set_free_free(other->aliases);
 
-        SET_FOREACH(t, u->names, i)
-                assert_se(hashmap_replace(u->manager->units, t, u) == 0);
+        SET_FOREACH(name, u->aliases, i)
+                assert_se(hashmap_replace(u->manager->units, name, u) == 0);
 
         return 0;
 }
@@ -825,19 +881,17 @@ static void merge_dependencies(Unit *u, Unit *other, const char *other_id, UnitD
         assert(d < _UNIT_DEPENDENCY_MAX);
 
         /* Fix backwards pointers. Let's iterate through all dependent units of the other unit. */
-        HASHMAP_FOREACH_KEY(v, back, other->dependencies[d], i) {
-                UnitDependency k;
-
-                /* Let's now iterate through the dependencies of that dependencies of the other units, looking for
-                 * pointers back, and let's fix them up, to instead point to 'u'. */
+        HASHMAP_FOREACH_KEY(v, back, other->dependencies[d], i)
 
-                for (k = 0; k < _UNIT_DEPENDENCY_MAX; k++) {
+                /* Let's now iterate through the dependencies of that dependencies of the other units,
+                 * looking for pointers back, and let's fix them up, to instead point to 'u'. */
+                for (UnitDependency k = 0; k < _UNIT_DEPENDENCY_MAX; k++)
                         if (back == u) {
                                 /* Do not add dependencies between u and itself. */
                                 if (hashmap_remove(back->dependencies[k], other))
                                         maybe_warn_about_dependency(u, other_id, k);
                         } else {
-                                UnitDependencyInfo di_u, di_other, di_merged;
+                                UnitDependencyInfo di_u, di_other;
 
                                 /* Let's drop this dependency between "back" and "other", and let's create it between
                                  * "back" and "u" instead. Let's merge the bit masks of the dependency we are moving,
@@ -849,7 +903,7 @@ static void merge_dependencies(Unit *u, Unit *other, const char *other_id, UnitD
 
                                 di_u.data = hashmap_get(back->dependencies[k], u);
 
-                                di_merged = (UnitDependencyInfo) {
+                                UnitDependencyInfo di_merged = {
                                         .origin_mask = di_u.origin_mask | di_other.origin_mask,
                                         .destination_mask = di_u.destination_mask | di_other.destination_mask,
                                 };
@@ -861,9 +915,6 @@ static void merge_dependencies(Unit *u, Unit *other, const char *other_id, UnitD
 
                                 /* assert_se(hashmap_remove_and_replace(back->dependencies[k], other, u, di_merged.data) >= 0); */
                         }
-                }
-
-        }
 
         /* Also do not move dependencies on u to itself */
         back = hashmap_remove(other->dependencies[d], u);
@@ -877,7 +928,6 @@ static void merge_dependencies(Unit *u, Unit *other, const char *other_id, UnitD
 }
 
 int unit_merge(Unit *u, Unit *other) {
-        UnitDependency d;
         const char *other_id = NULL;
         int r;
 
@@ -894,15 +944,15 @@ int unit_merge(Unit *u, Unit *other) {
         if (u->type != other->type)
                 return -EINVAL;
 
-        if (!u->instance != !other->instance)
-                return -EINVAL;
-
         if (!unit_type_may_alias(u->type)) /* Merging only applies to unit names that support aliases */
                 return -EEXIST;
 
         if (!IN_SET(other->load_state, UNIT_STUB, UNIT_NOT_FOUND))
                 return -EEXIST;
 
+        if (!streq_ptr(u->instance, other->instance))
+                return -EINVAL;
+
         if (other->job)
                 return -EEXIST;
 
@@ -916,7 +966,7 @@ int unit_merge(Unit *u, Unit *other) {
                 other_id = strdupa(other->id);
 
         /* Make reservations to ensure merge_dependencies() won't fail */
-        for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) {
+        for (UnitDependency d = 0; d < _UNIT_DEPENDENCY_MAX; d++) {
                 r = reserve_dependencies(u, other, d);
                 /*
                  * We don't rollback reservations if we fail. We don't have
@@ -936,7 +986,7 @@ int unit_merge(Unit *u, Unit *other) {
                 unit_ref_set(other->refs_by_target, other->refs_by_target->source, u);
 
         /* Merge dependencies */
-        for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++)
+        for (UnitDependency d = 0; d < _UNIT_DEPENDENCY_MAX; d++)
                 merge_dependencies(u, other, other_id, d);
 
         other->load_state = UNIT_MERGED;
@@ -1038,6 +1088,16 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) {
         if (!MANAGER_IS_SYSTEM(u->manager))
                 return 0;
 
+        /* For the following three directory types we need write access, and /var/ is possibly on the root
+         * fs. Hence order after systemd-remount-fs.service, to ensure things are writable. */
+        if (!strv_isempty(c->directories[EXEC_DIRECTORY_STATE].paths) ||
+            !strv_isempty(c->directories[EXEC_DIRECTORY_CACHE].paths) ||
+            !strv_isempty(c->directories[EXEC_DIRECTORY_LOGS].paths)) {
+                r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_REMOUNT_FS_SERVICE, true, UNIT_DEPENDENCY_FILE);
+                if (r < 0)
+                        return r;
+        }
+
         if (c->private_tmp) {
                 const char *p;
 
@@ -1052,14 +1112,21 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) {
                         return r;
         }
 
+        if (c->root_image) {
+                /* We need to wait for /dev/loopX to appear when doing RootImage=, hence let's add an
+                 * implicit dependency on udev */
+
+                r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_UDEVD_SERVICE, true, UNIT_DEPENDENCY_FILE);
+                if (r < 0)
+                        return r;
+        }
+
         if (!IN_SET(c->std_output,
                     EXEC_OUTPUT_JOURNAL, EXEC_OUTPUT_JOURNAL_AND_CONSOLE,
-                    EXEC_OUTPUT_KMSG, EXEC_OUTPUT_KMSG_AND_CONSOLE,
-                    EXEC_OUTPUT_SYSLOG, EXEC_OUTPUT_SYSLOG_AND_CONSOLE) &&
+                    EXEC_OUTPUT_KMSG, EXEC_OUTPUT_KMSG_AND_CONSOLE) &&
             !IN_SET(c->std_error,
                     EXEC_OUTPUT_JOURNAL, EXEC_OUTPUT_JOURNAL_AND_CONSOLE,
-                    EXEC_OUTPUT_KMSG, EXEC_OUTPUT_KMSG_AND_CONSOLE,
-                    EXEC_OUTPUT_SYSLOG, EXEC_OUTPUT_SYSLOG_AND_CONSOLE) &&
+                    EXEC_OUTPUT_KMSG, EXEC_OUTPUT_KMSG_AND_CONSOLE) &&
             !c->log_namespace)
                 return 0;
 
@@ -1154,7 +1221,6 @@ static void print_unit_dependency_mask(FILE *f, const char *kind, UnitDependency
 
 void unit_dump(Unit *u, FILE *f, const char *prefix) {
         char *t, **j;
-        UnitDependency d;
         Iterator i;
         const char *prefix2;
         char timestamp[5][FORMAT_TIMESTAMP_MAX], timespan[FORMAT_TIMESPAN_MAX];
@@ -1174,9 +1240,8 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
                 "%s-> Unit %s:\n",
                 prefix, u->id);
 
-        SET_FOREACH(t, u->names, i)
-                if (!streq(t, u->id))
-                        fprintf(f, "%s\tAlias: %s\n", prefix, t);
+        SET_FOREACH(t, u->aliases, i)
+                fprintf(f, "%s\tAlias: %s\n", prefix, t);
 
         fprintf(f,
                 "%s\tDescription: %s\n"
@@ -1310,7 +1375,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
                         prefix, strna(format_timestamp(timestamp[0], sizeof(timestamp[0]), u->assert_timestamp.realtime)),
                         prefix, yes_no(u->assert_result));
 
-        for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) {
+        for (UnitDependency d = 0; d < _UNIT_DEPENDENCY_MAX; d++) {
                 UnitDependencyInfo di;
                 Unit *other;
 
@@ -1455,7 +1520,6 @@ int unit_add_default_target_dependency(Unit *u, Unit *target) {
 }
 
 static int unit_add_slice_dependencies(Unit *u) {
-        UnitDependencyMask mask;
         assert(u);
 
         if (!UNIT_HAS_CGROUP_CONTEXT(u))
@@ -1464,7 +1528,7 @@ static int unit_add_slice_dependencies(Unit *u) {
         /* Slice units are implicitly ordered against their parent slices (as this relationship is encoded in the
            name), while all other units are ordered based on configuration (as in their case Slice= configures the
            relationship). */
-        mask = u->type == UNIT_SLICE ? UNIT_DEPENDENCY_IMPLICIT : UNIT_DEPENDENCY_FILE;
+        UnitDependencyMask mask = u->type == UNIT_SLICE ? UNIT_DEPENDENCY_IMPLICIT : UNIT_DEPENDENCY_FILE;
 
         if (UNIT_ISSET(u->slice))
                 return unit_add_two_dependencies(u, UNIT_AFTER, UNIT_REQUIRES, UNIT_DEREF(u->slice), true, mask);
@@ -1527,7 +1591,6 @@ static int unit_add_mount_dependencies(Unit *u) {
 
 static int unit_add_startup_units(Unit *u) {
         CGroupContext *c;
-        int r;
 
         c = unit_get_cgroup_context(u);
         if (!c)
@@ -1538,11 +1601,7 @@ static int unit_add_startup_units(Unit *u) {
             c->startup_blockio_weight == CGROUP_BLKIO_WEIGHT_INVALID)
                 return 0;
 
-        r = set_ensure_allocated(&u->manager->startup_units, NULL);
-        if (r < 0)
-                return r;
-
-        return set_put(u->manager->startup_units, u);
+        return set_ensure_put(&u->manager->startup_units, NULL, u);
 }
 
 int unit_load(Unit *u) {
@@ -1623,6 +1682,11 @@ fail:
                                                      UNIT_ERROR;
         u->load_error = r;
 
+        /* Record the last time we tried to load the unit, so that if the cache gets updated between now
+         * and the next time an attempt is made to load this unit, we know we need to check again */
+        if (u->load_state == UNIT_NOT_FOUND)
+                u->fragment_loadtime = now(CLOCK_REALTIME);
+
         unit_add_to_dbus_queue(u);
         unit_add_to_gc_queue(u);
 
@@ -1651,24 +1715,50 @@ static int log_unit_internal(void *userdata, int level, int error, const char *f
 }
 
 static bool unit_test_condition(Unit *u) {
+        _cleanup_strv_free_ char **env = NULL;
+        int r;
+
         assert(u);
 
         dual_timestamp_get(&u->condition_timestamp);
-        u->condition_result = condition_test_list(u->conditions, condition_type_to_string, log_unit_internal, u);
 
-        unit_add_to_dbus_queue(u);
+        r = manager_get_effective_environment(u->manager, &env);
+        if (r < 0) {
+                log_unit_error_errno(u, r, "Failed to determine effective environment: %m");
+                u->condition_result = CONDITION_ERROR;
+        } else
+                u->condition_result = condition_test_list(
+                                u->conditions,
+                                env,
+                                condition_type_to_string,
+                                log_unit_internal,
+                                u);
 
+        unit_add_to_dbus_queue(u);
         return u->condition_result;
 }
 
 static bool unit_test_assert(Unit *u) {
+        _cleanup_strv_free_ char **env = NULL;
+        int r;
+
         assert(u);
 
         dual_timestamp_get(&u->assert_timestamp);
-        u->assert_result = condition_test_list(u->asserts, assert_type_to_string, log_unit_internal, u);
 
-        unit_add_to_dbus_queue(u);
+        r = manager_get_effective_environment(u->manager, &env);
+        if (r < 0) {
+                log_unit_error_errno(u, r, "Failed to determine effective environment: %m");
+                u->assert_result = CONDITION_ERROR;
+        } else
+                u->assert_result = condition_test_list(
+                                u->asserts,
+                                env,
+                                assert_type_to_string,
+                                log_unit_internal,
+                                u);
 
+        unit_add_to_dbus_queue(u);
         return u->assert_result;
 }
 
@@ -1823,6 +1913,7 @@ int unit_start(Unit *u) {
          * waits for a holdoff timer to elapse before it will start again. */
 
         unit_add_to_dbus_queue(u);
+        unit_cgroup_freezer_action(u, FREEZER_THAW);
 
         return UNIT_VTABLE(u)->start(u);
 }
@@ -1875,6 +1966,7 @@ int unit_stop(Unit *u) {
                 return -EBADR;
 
         unit_add_to_dbus_queue(u);
+        unit_cgroup_freezer_action(u, FREEZER_THAW);
 
         return UNIT_VTABLE(u)->stop(u);
 }
@@ -1931,6 +2023,8 @@ int unit_reload(Unit *u) {
                 return 0;
         }
 
+        unit_cgroup_freezer_action(u, FREEZER_THAW);
+
         return UNIT_VTABLE(u)->reload(u);
 }
 
@@ -2156,7 +2250,7 @@ static int unit_log_resources(Unit *u) {
         struct iovec iovec[1 + _CGROUP_IP_ACCOUNTING_METRIC_MAX + _CGROUP_IO_ACCOUNTING_METRIC_MAX + 4];
         bool any_traffic = false, have_ip_accounting = false, any_io = false, have_io_accounting = false;
         _cleanup_free_ char *igress = NULL, *egress = NULL, *rr = NULL, *wr = NULL;
-        int log_level = LOG_DEBUG; /* May be raised if resources consumed over a treshold */
+        int log_level = LOG_DEBUG; /* May be raised if resources consumed over a threshold */
         size_t n_message_parts = 0, n_iovec = 0;
         char* message_parts[1 + 2 + 2 + 1], *t;
         nsec_t nsec = NSEC_INFINITY;
@@ -2837,13 +2931,13 @@ bool unit_job_is_applicable(Unit *u, JobType j) {
         case JOB_START:
         case JOB_NOP:
                 /* Note that we don't check unit_can_start() here. That's because .device units and suchlike are not
-                 * startable by us but may appear due to external events, and it thus makes sense to permit enqueing
+                 * startable by us but may appear due to external events, and it thus makes sense to permit enqueuing
                  * jobs for it. */
                 return true;
 
         case JOB_STOP:
                 /* Similar as above. However, perpetual units can never be stopped (neither explicitly nor due to
-                 * external events), hence it makes no sense to permit enqueing such a request either. */
+                 * external events), hence it makes no sense to permit enqueuing such a request either. */
                 return !u->perpetual;
 
         case JOB_RESTART:
@@ -3121,6 +3215,43 @@ char *unit_dbus_path_invocation_id(Unit *u) {
         return unit_dbus_path_from_name(u->invocation_id_string);
 }
 
+static int unit_set_invocation_id(Unit *u, sd_id128_t id) {
+        int r;
+
+        assert(u);
+
+        /* Set the invocation ID for this unit. If we cannot, this will not roll back, but reset the whole thing. */
+
+        if (sd_id128_equal(u->invocation_id, id))
+                return 0;
+
+        if (!sd_id128_is_null(u->invocation_id))
+                (void) hashmap_remove_value(u->manager->units_by_invocation_id, &u->invocation_id, u);
+
+        if (sd_id128_is_null(id)) {
+                r = 0;
+                goto reset;
+        }
+
+        r = hashmap_ensure_allocated(&u->manager->units_by_invocation_id, &id128_hash_ops);
+        if (r < 0)
+                goto reset;
+
+        u->invocation_id = id;
+        sd_id128_to_string(id, u->invocation_id_string);
+
+        r = hashmap_put(u->manager->units_by_invocation_id, &u->invocation_id, u);
+        if (r < 0)
+                goto reset;
+
+        return 0;
+
+reset:
+        u->invocation_id = SD_ID128_NULL;
+        u->invocation_id_string[0] = 0;
+        return r;
+}
+
 int unit_set_slice(Unit *u, Unit *slice) {
         assert(u);
         assert(slice);
@@ -3471,6 +3602,8 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
         if (!sd_id128_is_null(u->invocation_id))
                 (void) serialize_item_format(f, "invocation-id", SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(u->invocation_id));
 
+        (void) serialize_item_format(f, "freezer-state", "%s", freezer_state_to_string(unit_freezer_state(u)));
+
         bus_track_serialize(u->bus_track, f, "ref");
 
         for (m = 0; m < _CGROUP_IP_ACCOUNTING_METRIC_MAX; m++) {
@@ -3779,6 +3912,16 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
                                         log_unit_warning_errno(u, r, "Failed to set invocation ID for unit: %m");
                         }
 
+                        continue;
+                } else if (streq(l, "freezer-state")) {
+                        FreezerState s;
+
+                        s = freezer_state_from_string(v);
+                        if (s < 0)
+                                log_unit_debug(u, "Failed to deserialize freezer-state '%s', ignoring.", v);
+                        else
+                                u->freezer_state = s;
+
                         continue;
                 }
 
@@ -4241,7 +4384,8 @@ int unit_get_unit_file_preset(Unit *u) {
                 u->unit_file_preset = unit_file_query_preset(
                                 u->manager->unit_file_scope,
                                 NULL,
-                                basename(u->fragment_path));
+                                basename(u->fragment_path),
+                                NULL);
 
         return u->unit_file_preset;
 }
@@ -4385,24 +4529,27 @@ int unit_patch_contexts(Unit *u) {
 
                 if (ec->root_image &&
                     (cc->device_policy != CGROUP_DEVICE_POLICY_AUTO || cc->device_allow)) {
+                        const char *p;
 
                         /* When RootImage= is specified, the following devices are touched. */
-                        r = cgroup_add_device_allow(cc, "/dev/loop-control", "rw");
-                        if (r < 0)
-                                return r;
-
-                        r = cgroup_add_device_allow(cc, "block-loop", "rwm");
-                        if (r < 0)
-                                return r;
-
-                        r = cgroup_add_device_allow(cc, "block-blkext", "rwm");
-                        if (r < 0)
-                                return r;
+                        FOREACH_STRING(p, "/dev/loop-control", "/dev/mapper/control") {
+                                r = cgroup_add_device_allow(cc, p, "rw");
+                                if (r < 0)
+                                        return r;
+                        }
+                        FOREACH_STRING(p, "block-loop", "block-blkext", "block-device-mapper") {
+                                r = cgroup_add_device_allow(cc, p, "rwm");
+                                if (r < 0)
+                                        return r;
+                        }
 
-                        /* Make sure "block-loop" can be resolved, i.e. make sure "loop" shows up in /proc/devices */
-                        r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_WANTS, "modprobe@loop.service", true, UNIT_DEPENDENCY_FILE);
-                        if (r < 0)
-                                return r;
+                        /* Make sure "block-loop" can be resolved, i.e. make sure "loop" shows up in /proc/devices.
+                         * Same for mapper and verity. */
+                        FOREACH_STRING(p, "modprobe@loop.service", "modprobe@dm_mod.service", "modprobe@dm_verity.service") {
+                                r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_WANTS, p, true, UNIT_DEPENDENCY_FILE);
+                                if (r < 0)
+                                        return r;
+                        }
                 }
 
                 if (ec->protect_clock) {
@@ -4633,7 +4780,7 @@ int unit_write_setting(Unit *u, UnitWriteFlags flags, const char *name, const ch
         /* Make sure the drop-in dir is registered in our path cache. This way we don't need to stupidly
          * recreate the cache after every drop-in we write. */
         if (u->manager->unit_path_cache) {
-                r = set_put_strdup(u->manager->unit_path_cache, p);
+                r = set_put_strdup(&u->manager->unit_path_cache, p);
                 if (r < 0)
                         return r;
         }
@@ -4874,11 +5021,11 @@ int unit_kill_context(
                                 if (!pid_set)
                                         return -ENOMEM;
 
-                                cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path,
-                                                  SIGHUP,
-                                                  CGROUP_IGNORE_SELF,
-                                                  pid_set,
-                                                  NULL, NULL);
+                                (void) cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path,
+                                                         SIGHUP,
+                                                         CGROUP_IGNORE_SELF,
+                                                         pid_set,
+                                                         NULL, NULL);
                         }
                 }
         }
@@ -5247,43 +5394,6 @@ void unit_notify_user_lookup(Unit *u, uid_t uid, gid_t gid) {
                 unit_add_to_dbus_queue(u);
 }
 
-int unit_set_invocation_id(Unit *u, sd_id128_t id) {
-        int r;
-
-        assert(u);
-
-        /* Set the invocation ID for this unit. If we cannot, this will not roll back, but reset the whole thing. */
-
-        if (sd_id128_equal(u->invocation_id, id))
-                return 0;
-
-        if (!sd_id128_is_null(u->invocation_id))
-                (void) hashmap_remove_value(u->manager->units_by_invocation_id, &u->invocation_id, u);
-
-        if (sd_id128_is_null(id)) {
-                r = 0;
-                goto reset;
-        }
-
-        r = hashmap_ensure_allocated(&u->manager->units_by_invocation_id, &id128_hash_ops);
-        if (r < 0)
-                goto reset;
-
-        u->invocation_id = id;
-        sd_id128_to_string(id, u->invocation_id_string);
-
-        r = hashmap_put(u->manager->units_by_invocation_id, &u->invocation_id, u);
-        if (r < 0)
-                goto reset;
-
-        return 0;
-
-reset:
-        u->invocation_id = SD_ID128_NULL;
-        u->invocation_id_string[0] = 0;
-        return r;
-}
-
 int unit_acquire_invocation_id(Unit *u) {
         sd_id128_t id;
         int r;
@@ -5405,8 +5515,6 @@ static void unit_update_dependency_mask(Unit *u, UnitDependency d, Unit *other,
 }
 
 void unit_remove_dependencies(Unit *u, UnitDependencyMask mask) {
-        UnitDependency d;
-
         assert(u);
 
         /* Removes all dependencies u has on other units marked for ownership by 'mask'. */
@@ -5414,7 +5522,7 @@ void unit_remove_dependencies(Unit *u, UnitDependencyMask mask) {
         if (mask == 0)
                 return;
 
-        for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) {
+        for (UnitDependency d = 0; d < _UNIT_DEPENDENCY_MAX; d++) {
                 bool done;
 
                 do {
@@ -5425,8 +5533,6 @@ void unit_remove_dependencies(Unit *u, UnitDependencyMask mask) {
                         done = true;
 
                         HASHMAP_FOREACH_KEY(di.data, other, u->dependencies[d], i) {
-                                UnitDependency q;
-
                                 if ((di.origin_mask & ~mask) == di.origin_mask)
                                         continue;
                                 di.origin_mask &= ~mask;
@@ -5437,7 +5543,7 @@ void unit_remove_dependencies(Unit *u, UnitDependencyMask mask) {
                                  * all dependency types on the other unit and delete all those which point to us and
                                  * have the right mask set. */
 
-                                for (q = 0; q < _UNIT_DEPENDENCY_MAX; q++) {
+                                for (UnitDependency q = 0; q < _UNIT_DEPENDENCY_MAX; q++) {
                                         UnitDependencyInfo dj;
 
                                         dj.data = hashmap_get(other->dependencies[q], u);
@@ -5764,14 +5870,20 @@ int unit_prepare_exec(Unit *u) {
         return 0;
 }
 
-static int log_leftover(pid_t pid, int sig, void *userdata) {
+static bool ignore_leftover_process(const char *comm) {
+        return comm && comm[0] == '('; /* Most likely our own helper process (PAM?), ignore */
+}
+
+int unit_log_leftover_process_start(pid_t pid, int sig, void *userdata) {
         _cleanup_free_ char *comm = NULL;
 
         (void) get_process_comm(pid, &comm);
 
-        if (comm && comm[0] == '(') /* Most likely our own helper process (PAM?), ignore */
+        if (ignore_leftover_process(comm))
                 return 0;
 
+        /* During start we print a warning */
+
         log_unit_warning(userdata,
                          "Found left-over process " PID_FMT " (%s) in control group while starting unit. Ignoring.\n"
                          "This usually indicates unclean termination of a previous run, or service implementation deficiencies.",
@@ -5780,7 +5892,24 @@ static int log_leftover(pid_t pid, int sig, void *userdata) {
         return 1;
 }
 
-int unit_warn_leftover_processes(Unit *u) {
+int unit_log_leftover_process_stop(pid_t pid, int sig, void *userdata) {
+        _cleanup_free_ char *comm = NULL;
+
+        (void) get_process_comm(pid, &comm);
+
+        if (ignore_leftover_process(comm))
+                return 0;
+
+        /* During stop we only print an informational message */
+
+        log_unit_info(userdata,
+                      "Unit process " PID_FMT " (%s) remains running after unit stopped.",
+                      pid, strna(comm));
+
+        return 1;
+}
+
+int unit_warn_leftover_processes(Unit *u, cg_kill_log_func_t log_func) {
         assert(u);
 
         (void) unit_pick_cgroup_path(u);
@@ -5788,7 +5917,7 @@ int unit_warn_leftover_processes(Unit *u) {
         if (!u->cgroup_path)
                 return 0;
 
-        return cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, 0, 0, NULL, log_leftover, u);
+        return cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, 0, 0, NULL, log_func, u);
 }
 
 bool unit_needs_console(Unit *u) {
@@ -5826,7 +5955,7 @@ const char *unit_label_path(const Unit *u) {
                 return NULL;
 
         /* If a unit is masked, then don't read the SELinux label of /dev/null, as that really makes no sense */
-        if (path_equal(p, "/dev/null"))
+        if (null_or_empty_path(p) > 0)
                 return NULL;
 
         return p;
@@ -6050,6 +6179,80 @@ int unit_can_clean(Unit *u, ExecCleanMask *ret) {
         return UNIT_VTABLE(u)->can_clean(u, ret);
 }
 
+bool unit_can_freeze(Unit *u) {
+        assert(u);
+
+        if (UNIT_VTABLE(u)->can_freeze)
+                return UNIT_VTABLE(u)->can_freeze(u);
+
+        return UNIT_VTABLE(u)->freeze;
+}
+
+void unit_frozen(Unit *u) {
+        assert(u);
+
+        u->freezer_state = FREEZER_FROZEN;
+
+        bus_unit_send_pending_freezer_message(u);
+}
+
+void unit_thawed(Unit *u) {
+        assert(u);
+
+        u->freezer_state = FREEZER_RUNNING;
+
+        bus_unit_send_pending_freezer_message(u);
+}
+
+static int unit_freezer_action(Unit *u, FreezerAction action) {
+        UnitActiveState s;
+        int (*method)(Unit*);
+        int r;
+
+        assert(u);
+        assert(IN_SET(action, FREEZER_FREEZE, FREEZER_THAW));
+
+        method = action == FREEZER_FREEZE ? UNIT_VTABLE(u)->freeze : UNIT_VTABLE(u)->thaw;
+        if (!method || !cg_freezer_supported())
+                return -EOPNOTSUPP;
+
+        if (u->job)
+                return -EBUSY;
+
+        if (u->load_state != UNIT_LOADED)
+                return -EHOSTDOWN;
+
+        s = unit_active_state(u);
+        if (s != UNIT_ACTIVE)
+                return -EHOSTDOWN;
+
+        if (IN_SET(u->freezer_state, FREEZER_FREEZING, FREEZER_THAWING))
+                return -EALREADY;
+
+        r = method(u);
+        if (r <= 0)
+                return r;
+
+        return 1;
+}
+
+int unit_freeze(Unit *u) {
+        return unit_freezer_action(u, FREEZER_FREEZE);
+}
+
+int unit_thaw(Unit *u) {
+        return unit_freezer_action(u, FREEZER_THAW);
+}
+
+/* Wrappers around low-level cgroup freezer operations common for service and scope units */
+int unit_freeze_vtable_common(Unit *u) {
+        return unit_cgroup_freezer_action(u, FREEZER_FREEZE);
+}
+
+int unit_thaw_vtable_common(Unit *u) {
+        return unit_cgroup_freezer_action(u, FREEZER_THAW);
+}
+
 static const char* const collect_mode_table[_COLLECT_MODE_MAX] = {
         [COLLECT_INACTIVE] = "inactive",
         [COLLECT_INACTIVE_OR_FAILED] = "inactive-or-failed",
index eb0a34477fd20dec34d31127bcd7d41bad0bcf29..d5e4c65989e400922724efb333714b91bfa06e67 100644 (file)
@@ -114,10 +114,13 @@ typedef struct Unit {
         UnitLoadState load_state;
         Unit *merged_into;
 
-        char *id; /* One name is special because we use it for identification. Points to an entry in the names set */
+        FreezerState freezer_state;
+        sd_bus_message *pending_freezer_message;
+
+        char *id;   /* The one special name that we use for identification */
         char *instance;
 
-        Set *names;
+        Set *aliases; /* All the other names. */
 
         /* For each dependency type we maintain a Hashmap whose key is the Unit* object, and the value encodes why the
          * dependency exists, using the UnitDependencyInfo type */
@@ -133,6 +136,7 @@ typedef struct Unit {
         char *source_path; /* if converted, the source file */
         char **dropin_paths;
 
+        usec_t fragment_loadtime;
         usec_t fragment_mtime;
         usec_t source_mtime;
         usec_t dropin_mtime;
@@ -483,6 +487,11 @@ typedef struct UnitVTable {
         /* Clear out the various runtime/state/cache/logs/configuration data */
         int (*clean)(Unit *u, ExecCleanMask m);
 
+        /* Freeze the unit */
+        int (*freeze)(Unit *u);
+        int (*thaw)(Unit *u);
+        bool (*can_freeze)(Unit *u);
+
         /* Return which kind of data can be cleaned */
         int (*can_clean)(Unit *u, ExecCleanMask *ret);
 
@@ -531,7 +540,7 @@ typedef struct UnitVTable {
         void (*notify_cgroup_oom)(Unit *u);
 
         /* Called whenever a process of this unit sends us a message */
-        void (*notify_message)(Unit *u, const struct ucred *ucred, char **tags, FDSet *fds);
+        void (*notify_message)(Unit *u, const struct ucred *ucred, char * const *tags, FDSet *fds);
 
         /* Called whenever a name this Unit registered for comes or goes away. */
         void (*bus_name_owner_change)(Unit *u, const char *new_owner);
@@ -592,9 +601,6 @@ typedef struct UnitVTable {
          * of this type will immediately fail. */
         bool (*supported)(void);
 
-        /* The bus vtable */
-        const sd_bus_vtable *bus_vtable;
-
         /* The strings to print in status messages */
         UnitStatusMessageFormats status_message_formats;
 
@@ -692,6 +698,8 @@ const char *unit_status_string(Unit *u) _pure_;
 bool unit_has_name(const Unit *u, const char *name);
 
 UnitActiveState unit_active_state(Unit *u);
+FreezerState unit_freezer_state(Unit *u);
+int unit_freezer_state_kernel(Unit *u, FreezerState *ret);
 
 const char* unit_sub_state_to_string(Unit *u);
 
@@ -822,7 +830,6 @@ void unit_unref_uid_gid(Unit *u, bool destroy_now);
 
 void unit_notify_user_lookup(Unit *u, uid_t uid, gid_t gid);
 
-int unit_set_invocation_id(Unit *u, sd_id128_t id);
 int unit_acquire_invocation_id(Unit *u);
 
 bool unit_shall_confirm_spawn(Unit *u);
@@ -839,7 +846,9 @@ void unit_unlink_state_files(Unit *u);
 
 int unit_prepare_exec(Unit *u);
 
-int unit_warn_leftover_processes(Unit *u);
+int unit_log_leftover_process_start(pid_t pid, int sig, void *userdata);
+int unit_log_leftover_process_stop(pid_t pid, int sig, void *userdata);
+int unit_warn_leftover_processes(Unit *u, cg_kill_log_func_t log_func);
 
 bool unit_needs_console(Unit *u);
 
@@ -875,6 +884,16 @@ void unit_destroy_runtime_directory(Unit *u, const ExecContext *context);
 int unit_clean(Unit *u, ExecCleanMask mask);
 int unit_can_clean(Unit *u, ExecCleanMask *ret_mask);
 
+bool unit_can_freeze(Unit *u);
+int unit_freeze(Unit *u);
+void unit_frozen(Unit *u);
+
+int unit_thaw(Unit *u);
+void unit_thawed(Unit *u);
+
+int unit_freeze_vtable_common(Unit *u);
+int unit_thaw_vtable_common(Unit *u);
+
 /* Macros which append UNIT= or USER_UNIT= to the message */
 
 #define log_unit_full(unit, level, error, ...)                          \
index 95a162e0f6cc0d33f873c6f2fb947980af378e36..bbe06319c9acfd73d77e5a0e317848c1ec405baf 100644 (file)
@@ -15,6 +15,7 @@
 #LogTarget=console
 #LogColor=yes
 #LogLocation=no
+#LogTime=no
 #SystemCallArchitectures=
 #TimerSlackNSec=
 #StatusUnitFormat=@STATUS_UNIT_FORMAT_DEFAULT@
index 6b512403057f4926d7c04b4adaf01bd61193b30a..8b052dac26cb9636993a2816aa0507d588c08758 100644 (file)
@@ -155,11 +155,14 @@ static int parse_config(void) {
                 {}
         };
 
-        return config_parse_many_nulstr(PKGSYSCONFDIR "/coredump.conf",
-                                        CONF_PATHS_NULSTR("systemd/coredump.conf.d"),
-                                        "Coredump\0",
-                                        config_item_table_lookup, items,
-                                        CONFIG_PARSE_WARN, NULL);
+        return config_parse_many_nulstr(
+                        PKGSYSCONFDIR "/coredump.conf",
+                        CONF_PATHS_NULSTR("systemd/coredump.conf.d"),
+                        "Coredump\0",
+                        config_item_table_lookup, items,
+                        CONFIG_PARSE_WARN,
+                        NULL,
+                        NULL);
 }
 
 static uint64_t storage_size_max(void) {
@@ -174,38 +177,18 @@ static uint64_t storage_size_max(void) {
 static int fix_acl(int fd, uid_t uid) {
 
 #if HAVE_ACL
-        _cleanup_(acl_freep) acl_t acl = NULL;
-        acl_entry_t entry;
-        acl_permset_t permset;
         int r;
 
         assert(fd >= 0);
+        assert(uid_is_valid(uid));
 
         if (uid_is_system(uid) || uid_is_dynamic(uid) || uid == UID_NOBODY)
                 return 0;
 
-        /* Make sure normal users can read (but not write or delete)
-         * their own coredumps */
-
-        acl = acl_get_fd(fd);
-        if (!acl)
-                return log_error_errno(errno, "Failed to get ACL: %m");
-
-        if (acl_create_entry(&acl, &entry) < 0 ||
-            acl_set_tag_type(entry, ACL_USER) < 0 ||
-            acl_set_qualifier(entry, &uid) < 0)
-                return log_error_errno(errno, "Failed to patch ACL: %m");
-
-        if (acl_get_permset(entry, &permset) < 0 ||
-            acl_add_perm(permset, ACL_READ) < 0)
-                return log_warning_errno(errno, "Failed to patch ACL: %m");
-
-        r = calc_acl_mask_if_needed(&acl);
+        /* Make sure normal users can read (but not write or delete) their own coredumps */
+        r = add_acls_for_user(fd, uid);
         if (r < 0)
-                return log_warning_errno(r, "Failed to patch ACL: %m");
-
-        if (acl_set_fd(fd, acl) < 0)
-                return log_error_errno(errno, "Failed to apply ACL: %m");
+                return log_error_errno(r, "Failed to adjust ACL of coredump: %m");
 #endif
 
         return 0;
@@ -420,7 +403,7 @@ static int save_external_coredump(
                 goto fail;
         }
 
-#if HAVE_XZ || HAVE_LZ4
+#if HAVE_COMPRESSION
         /* If we will remove the coredump anyway, do not compress. */
         if (arg_compress && !maybe_remove_external_coredump(NULL, st.st_size)) {
 
@@ -560,7 +543,7 @@ static int compose_open_fds(pid_t pid, char **open_fds) {
         FOREACH_DIRENT(dent, proc_fd_dir, return -errno) {
                 _cleanup_fclose_ FILE *fdinfo = NULL;
                 _cleanup_free_ char *fdname = NULL;
-                int fd;
+                _cleanup_close_ int fd = -1;
 
                 r = readlinkat_malloc(dirfd(proc_fd_dir), dent->d_name, &fdname);
                 if (r < 0)
@@ -574,11 +557,9 @@ static int compose_open_fds(pid_t pid, char **open_fds) {
                 if (fd < 0)
                         continue;
 
-                fdinfo = fdopen(fd, "r");
-                if (!fdinfo) {
-                        safe_close(fd);
+                fdinfo = take_fdopen(&fd, "r");
+                if (!fdinfo)
                         continue;
-                }
 
                 for (;;) {
                         _cleanup_free_ char *line = NULL;
@@ -886,10 +867,7 @@ static int process_socket(int fd) {
         log_debug("Processing coredump received on stdin...");
 
         for (;;) {
-                union {
-                        struct cmsghdr cmsghdr;
-                        uint8_t buf[CMSG_SPACE(sizeof(int))];
-                } control = {};
+                CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int))) control;
                 struct msghdr mh = {
                         .msg_control = &control,
                         .msg_controllen = sizeof(control),
@@ -923,19 +901,11 @@ static int process_socket(int fd) {
                 /* The final zero-length datagram carries the file descriptor and tells us
                  * that we're done. */
                 if (n == 0) {
-                        struct cmsghdr *cmsg, *found = NULL;
+                        struct cmsghdr *found;
 
                         free(iovec.iov_base);
 
-                        CMSG_FOREACH(cmsg, &mh) {
-                                if (cmsg->cmsg_level == SOL_SOCKET &&
-                                    cmsg->cmsg_type == SCM_RIGHTS &&
-                                    cmsg->cmsg_len == CMSG_LEN(sizeof(int))) {
-                                        assert(!found);
-                                        found = cmsg;
-                                }
-                        }
-
+                        found = cmsg_find(&mh, SOL_SOCKET, SCM_RIGHTS, CMSG_LEN(sizeof(int)));
                         if (!found) {
                                 cmsg_close_all(&mh);
                                 r = log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
index 4172e8e84c4f2c636bfbaf7c5e4eced45bd67dd2..02502528afe2bf3909d53742410e29f8e19c64e5 100644 (file)
@@ -765,7 +765,7 @@ static int save_core(sd_journal *j, FILE *file, char **path, bool *unlink_temp)
                 if (access(filename, R_OK) < 0)
                         return log_error_errno(errno, "File \"%s\" is not readable: %m", filename);
 
-                if (path && !endswith(filename, ".xz") && !endswith(filename, ".lz4")) {
+                if (path && !ENDSWITH_SET(filename, ".xz", ".lz4", ".zst")) {
                         *path = TAKE_PTR(filename);
 
                         return 0;
@@ -824,7 +824,7 @@ static int save_core(sd_journal *j, FILE *file, char **path, bool *unlink_temp)
         }
 
         if (filename) {
-#if HAVE_XZ || HAVE_LZ4
+#if HAVE_COMPRESSION
                 _cleanup_close_ int fdf;
 
                 fdf = open(filename, O_RDONLY | O_CLOEXEC);
@@ -839,8 +839,8 @@ static int save_core(sd_journal *j, FILE *file, char **path, bool *unlink_temp)
                         goto error;
                 }
 #else
-                log_error("Cannot decompress file. Compiled without compression support.");
-                r = -EOPNOTSUPP;
+                r = log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+                                    "Cannot decompress file. Compiled without compression support.");
                 goto error;
 #endif
         } else {
@@ -1091,9 +1091,7 @@ static int run(int argc, char *argv[]) {
         int r, units_active;
 
         setlocale(LC_ALL, "");
-        log_show_color(true);
-        log_parse_environment();
-        log_open();
+        log_setup_cli();
 
         /* The journal merging logic potentially needs a lot of fds. */
         (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE);
index 1deab765fbb94070fa4a247c99d4863ba4af2ea6..b93fbb86604ece64b63a3d096f5c0927922b6d58 100644 (file)
@@ -39,7 +39,7 @@ static bool arg_enabled = true;
 static bool arg_read_crypttab = true;
 static const char *arg_crypttab = NULL;
 static const char *arg_runtime_directory = NULL;
-static bool arg_whitelist = false;
+static bool arg_allow_list = false;
 static Hashmap *arg_disks = NULL;
 static char *arg_default_options = NULL;
 static char *arg_default_keyfile = NULL;
@@ -188,7 +188,11 @@ static int print_dependencies(FILE *f, const char* device_path) {
                 /* None, nothing to do */
                 return 0;
 
-        if (PATH_IN_SET(device_path, "/dev/urandom", "/dev/random", "/dev/hw_random")) {
+        if (PATH_IN_SET(device_path,
+                        "/dev/urandom",
+                        "/dev/random",
+                        "/dev/hw_random",
+                        "/dev/hwrng")) {
                 /* RNG device, add random dep */
                 fputs("After=systemd-random-seed.service\n", f);
                 return 0;
@@ -202,14 +206,16 @@ static int print_dependencies(FILE *f, const char* device_path) {
                 return 0;
 
         if (path_startswith(udev_node, "/dev/")) {
-                /* We are dealing with a block device, add dependency for correspoding unit */
+                /* We are dealing with a block device, add dependency for corresponding unit */
                 _cleanup_free_ char *unit = NULL;
 
                 r = unit_name_from_path(udev_node, ".device", &unit);
                 if (r < 0)
                         return log_error_errno(r, "Failed to generate unit name: %m");
 
-                fprintf(f, "After=%1$s\nRequires=%1$s\n", unit);
+                fprintf(f,
+                        "After=%1$s\n"
+                        "Requires=%1$s\n", unit);
         } else {
                 /* Regular file, add mount dependency */
                 _cleanup_free_ char *escaped_path = specifier_escape(device_path);
@@ -231,18 +237,18 @@ static int create_disk(
 
         _cleanup_free_ char *n = NULL, *d = NULL, *u = NULL, *e = NULL,
                 *keydev_mount = NULL, *keyfile_timeout_value = NULL,
-                *filtered = NULL, *u_escaped = NULL, *name_escaped = NULL, *header_path = NULL, *password_buffer = NULL;
+                *filtered = NULL, *u_escaped = NULL, *name_escaped = NULL, *header_path = NULL, *password_buffer = NULL,
+                *tmp_fstype = NULL;
         _cleanup_fclose_ FILE *f = NULL;
         const char *dmname;
-        bool noauto, nofail, tmp, swap, netdev, attach_in_initrd;
-        int r, detached_header, keyfile_can_timeout;
+        bool noauto, nofail, swap, netdev, attach_in_initrd;
+        int r, detached_header, keyfile_can_timeout, tmp;
 
         assert(name);
         assert(device);
 
         noauto = fstab_test_yes_no_option(options, "noauto\0" "auto\0");
         nofail = fstab_test_yes_no_option(options, "nofail\0" "fail\0");
-        tmp = fstab_test_option(options, "tmp\0");
         swap = fstab_test_option(options, "swap\0");
         netdev = fstab_test_option(options, "_netdev\0");
         attach_in_initrd = fstab_test_option(options, "x-initrd.attach\0");
@@ -255,6 +261,10 @@ static int create_disk(
         if (detached_header < 0)
                 return log_error_errno(detached_header, "Failed to parse header= option value: %m");
 
+        tmp = fstab_filter_options(options, "tmp\0", NULL, &tmp_fstype, NULL);
+        if (tmp < 0)
+                return log_error_errno(tmp, "Failed to parse tmp= option value: %m");
+
         if (tmp && swap)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                        "Device '%s' cannot be both 'tmp' and 'swap'. Ignoring.",
@@ -365,14 +375,23 @@ static int create_disk(
         if (r < 0)
                 return r;
 
-        if (tmp)
+        if (tmp) {
+                _cleanup_free_ char *tmp_fstype_escaped = NULL;
+
+                if (tmp_fstype) {
+                        tmp_fstype_escaped = specifier_escape(tmp_fstype);
+                        if (!tmp_fstype_escaped)
+                                return log_oom();
+                }
+
                 fprintf(f,
-                        "ExecStartPost=/sbin/mke2fs '/dev/mapper/%s'\n",
-                        name_escaped);
+                        "ExecStartPost=" ROOTLIBEXECDIR "/systemd-makefs '%s' '/dev/mapper/%s'\n",
+                        tmp_fstype_escaped ?: "ext4", name_escaped);
+        }
 
         if (swap)
                 fprintf(f,
-                        "ExecStartPost=/sbin/mkswap '/dev/mapper/%s'\n",
+                        "ExecStartPost=" ROOTLIBEXECDIR "/systemd-makefs swap '/dev/mapper/%s'\n",
                         name_escaped);
 
         if (keydev)
@@ -476,7 +495,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
                 if (!d)
                         return log_oom();
 
-                d->create = arg_whitelist = true;
+                d->create = arg_allow_list = true;
 
         } else if (streq(key, "luks.options")) {
 
@@ -540,7 +559,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
                         if (!d)
                                 return log_oom();
 
-                        d->create = arg_whitelist = true;
+                        d->create = arg_allow_list = true;
 
                         free_and_replace(d->name, uuid_value);
                 } else
@@ -603,7 +622,7 @@ static int add_crypttab_devices(void) {
                 if (uuid)
                         d = hashmap_get(arg_disks, uuid);
 
-                if (arg_whitelist && !d) {
+                if (arg_allow_list && !d) {
                         log_info("Not creating device '%s' because it was not specified on the kernel command line.", name);
                         continue;
                 }
index ec9186a6aaa527fd59b0dfc425f48553cf6b11af..642a1b7d11a8843732d9e781d4e9e91830cc1d44 100644 (file)
@@ -10,6 +10,7 @@
 #include "alloc-util.h"
 #include "ask-password-api.h"
 #include "cryptsetup-pkcs11.h"
+#include "cryptsetup-util.h"
 #include "escape.h"
 #include "fd-util.h"
 #include "format-util.h"
 #include "stat-util.h"
 #include "strv.h"
 
-#define KEY_FILE_SIZE_MAX (16U*1024U*1024U) /* 16 MiB */
-
-static int load_key_file(
-                const char *key_file,
-                size_t key_file_size,
-                uint64_t key_file_offset,
-                void **ret_encrypted_key,
-                size_t *ret_encrypted_key_size) {
-
-        _cleanup_(erase_and_freep) char *buffer = NULL;
-        _cleanup_close_ int fd = -1;
-        ssize_t n;
-        int r;
-
-        assert(key_file);
-        assert(ret_encrypted_key);
-        assert(ret_encrypted_key_size);
-
-        fd = open(key_file, O_RDONLY|O_CLOEXEC);
-        if (fd < 0)
-                return log_error_errno(errno, "Failed to load encrypted PKCS#11 key: %m");
-
-        if (key_file_size == 0) {
-                struct stat st;
-
-                if (fstat(fd, &st) < 0)
-                        return log_error_errno(errno, "Failed to stat key file: %m");
-
-                r = stat_verify_regular(&st);
-                if (r < 0)
-                        return log_error_errno(r, "Key file is not a regular file: %m");
-
-                if (st.st_size == 0)
-                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Key file is empty, refusing.");
-                if ((uint64_t) st.st_size > KEY_FILE_SIZE_MAX) {
-                        char buf1[FORMAT_BYTES_MAX], buf2[FORMAT_BYTES_MAX];
-                        return log_error_errno(SYNTHETIC_ERRNO(ERANGE),
-                                               "Key file larger (%s) than allowed maximum size (%s), refusing.",
-                                               format_bytes(buf1, sizeof(buf1), st.st_size),
-                                               format_bytes(buf2, sizeof(buf2), KEY_FILE_SIZE_MAX));
-                }
-
-                if (key_file_offset >= (uint64_t) st.st_size)
-                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Key file offset too large for file, refusing.");
-
-                key_file_size = st.st_size - key_file_offset;
-        }
-
-        buffer = malloc(key_file_size);
-        if (!buffer)
-                return log_oom();
-
-        if (key_file_offset > 0)
-                n = pread(fd, buffer, key_file_size, key_file_offset);
-        else
-                n = read(fd, buffer, key_file_size);
-        if (n < 0)
-                return log_error_errno(errno, "Failed to read PKCS#11 key file: %m");
-        if (n == 0)
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Empty encrypted key found, refusing.");
-
-        *ret_encrypted_key = TAKE_PTR(buffer);
-        *ret_encrypted_key_size = (size_t) n;
-
-        return 0;
-}
-
 struct pkcs11_callback_data {
         const char *friendly_name;
         usec_t until;
@@ -93,11 +27,14 @@ struct pkcs11_callback_data {
         size_t encrypted_key_size;
         void *decrypted_key;
         size_t decrypted_key_size;
+        bool free_encrypted_key;
 };
 
 static void pkcs11_callback_data_release(struct pkcs11_callback_data *data) {
         free(data->decrypted_key);
-        free(data->encrypted_key);
+
+        if (data->free_encrypted_key)
+                free(data->encrypted_key);
 }
 
 static int pkcs11_callback(
@@ -160,9 +97,11 @@ static int pkcs11_callback(
 int decrypt_pkcs11_key(
                 const char *friendly_name,
                 const char *pkcs11_uri,
-                const char *key_file,
+                const char *key_file,         /* We either expect key_file and associated parameters to be set (for file keys) … */
                 size_t key_file_size,
                 uint64_t key_file_offset,
+                const void *key_data,         /* … or key_data and key_data_size (for literal keys) */
+                size_t key_data_size,
                 usec_t until,
                 void **ret_decrypted_key,
                 size_t *ret_decrypted_key_size) {
@@ -175,15 +114,24 @@ int decrypt_pkcs11_key(
 
         assert(friendly_name);
         assert(pkcs11_uri);
-        assert(key_file);
+        assert(key_file || key_data);
         assert(ret_decrypted_key);
         assert(ret_decrypted_key_size);
 
         /* The functions called here log about all errors, except for EAGAIN which means "token not found right now" */
 
-        r = load_key_file(key_file, key_file_size, key_file_offset, &data.encrypted_key, &data.encrypted_key_size);
-        if (r < 0)
-                return r;
+        if (key_data) {
+                data.encrypted_key = (void*) key_data;
+                data.encrypted_key_size = key_data_size;
+
+                data.free_encrypted_key = false;
+        } else {
+                r = load_key_file(key_file, NULL, key_file_size, key_file_offset, &data.encrypted_key, &data.encrypted_key_size);
+                if (r < 0)
+                        return r;
+
+                data.free_encrypted_key = true;
+        }
 
         r = pkcs11_find_token(pkcs11_uri, pkcs11_callback, &data);
         if (r < 0)
index 264ccb66b107d279a63ec9f04cbfe0fcacbafb5c..af2487e75bdfc5d8c38dc0297af42f015d70363d 100644 (file)
@@ -14,6 +14,8 @@ int decrypt_pkcs11_key(
                 const char *key_file,
                 size_t key_file_size,
                 uint64_t key_file_offset,
+                const void *key_data,
+                size_t key_data_size,
                 usec_t until,
                 void **ret_decrypted_key,
                 size_t *ret_decrypted_key_size);
@@ -26,6 +28,8 @@ static inline int decrypt_pkcs11_key(
                 const char *key_file,
                 size_t key_file_size,
                 uint64_t key_file_offset,
+                const void *key_data,
+                size_t key_data_size,
                 usec_t until,
                 void **ret_decrypted_key,
                 size_t *ret_decrypted_key_size) {
diff --git a/src/cryptsetup/cryptsetup-util.c b/src/cryptsetup/cryptsetup-util.c
new file mode 100644 (file)
index 0000000..8ae70a5
--- /dev/null
@@ -0,0 +1,110 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <unistd.h>
+
+#include "cryptsetup-util.h"
+#include "fd-util.h"
+#include "format-util.h"
+#include "memory-util.h"
+#include "path-util.h"
+#include "stat-util.h"
+#include "strv.h"
+
+#define KEY_FILE_SIZE_MAX (16U*1024U*1024U) /* 16 MiB */
+
+int load_key_file(
+                const char *key_file,
+                char **search_path,
+                size_t key_file_size,
+                uint64_t key_file_offset,
+                void **ret_key,
+                size_t *ret_key_size) {
+
+        _cleanup_(erase_and_freep) char *buffer = NULL;
+        _cleanup_free_ char *discovered_path = NULL;
+        _cleanup_close_ int fd = -1;
+        ssize_t n;
+        int r;
+
+        assert(key_file);
+        assert(ret_key);
+        assert(ret_key_size);
+
+        if (strv_isempty(search_path) || path_is_absolute(key_file)) {
+                fd = open(key_file, O_RDONLY|O_CLOEXEC);
+                if (fd < 0)
+                        return log_error_errno(errno, "Failed to load key file '%s': %m", key_file);
+        } else {
+                char **i;
+
+                STRV_FOREACH(i, search_path) {
+                        _cleanup_free_ char *joined;
+
+                        joined = path_join(*i, key_file);
+                        if (!joined)
+                                return log_oom();
+
+                        fd = open(joined, O_RDONLY|O_CLOEXEC);
+                        if (fd >= 0) {
+                                discovered_path = TAKE_PTR(joined);
+                                break;
+                        }
+                        if (errno != ENOENT)
+                                return log_error_errno(errno, "Failed to load key file '%s': %m", joined);
+                }
+
+                if (!discovered_path) {
+                        /* Search path supplied, but file not found, report by returning NULL, but not failing */
+                        *ret_key = NULL;
+                        *ret_key_size = 0;
+                        return 0;
+                }
+
+                assert(fd >= 0);
+                key_file = discovered_path;
+        }
+
+        if (key_file_size == 0) {
+                struct stat st;
+
+                if (fstat(fd, &st) < 0)
+                        return log_error_errno(errno, "Failed to stat key file '%s': %m", key_file);
+
+                r = stat_verify_regular(&st);
+                if (r < 0)
+                        return log_error_errno(r, "Key file is not a regular file: %m");
+
+                if (st.st_size == 0)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Key file is empty, refusing.");
+                if ((uint64_t) st.st_size > KEY_FILE_SIZE_MAX) {
+                        char buf1[FORMAT_BYTES_MAX], buf2[FORMAT_BYTES_MAX];
+                        return log_error_errno(SYNTHETIC_ERRNO(ERANGE),
+                                               "Key file larger (%s) than allowed maximum size (%s), refusing.",
+                                               format_bytes(buf1, sizeof(buf1), st.st_size),
+                                               format_bytes(buf2, sizeof(buf2), KEY_FILE_SIZE_MAX));
+                }
+
+                if (key_file_offset >= (uint64_t) st.st_size)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Key file offset too large for file, refusing.");
+
+                key_file_size = st.st_size - key_file_offset;
+        }
+
+        buffer = malloc(key_file_size);
+        if (!buffer)
+                return log_oom();
+
+        if (key_file_offset > 0)
+                n = pread(fd, buffer, key_file_size, key_file_offset);
+        else
+                n = read(fd, buffer, key_file_size);
+        if (n < 0)
+                return log_error_errno(errno, "Failed to read key file '%s': %m", key_file);
+        if (n == 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Empty encrypted key found, refusing.");
+
+        *ret_key = TAKE_PTR(buffer);
+        *ret_key_size = (size_t) n;
+
+        return 1;
+}
diff --git a/src/cryptsetup/cryptsetup-util.h b/src/cryptsetup/cryptsetup-util.h
new file mode 100644 (file)
index 0000000..7bb7822
--- /dev/null
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <inttypes.h>
+#include <sys/types.h>
+
+int load_key_file(
+                const char *key_file,
+                char **search_path,
+                size_t key_file_size,
+                uint64_t key_file_offset,
+                void **ret_key,
+                size_t *ret_key_size);
index 860b29b3b6c049aac28f1293b5d094a236c4b922..6d3f842dbe1ec5fb5f6bf7d70ec3ff0b574f67ac 100644 (file)
 #include "ask-password-api.h"
 #include "crypt-util.h"
 #include "cryptsetup-pkcs11.h"
+#include "cryptsetup-util.h"
 #include "device-util.h"
 #include "escape.h"
 #include "fileio.h"
+#include "fs-util.h"
 #include "fstab-util.h"
 #include "hexdecoct.h"
 #include "log.h"
 #include "main-func.h"
+#include "memory-util.h"
 #include "mount-util.h"
 #include "nulstr-util.h"
 #include "parse-util.h"
 #define CRYPT_SECTOR_SIZE 512
 #define CRYPT_MAX_SECTOR_SIZE 4096
 
-static const char *arg_type = NULL; /* ANY_LUKS, CRYPT_LUKS1, CRYPT_LUKS2, CRYPT_TCRYPT or CRYPT_PLAIN */
+static const char *arg_type = NULL; /* ANY_LUKS, CRYPT_LUKS1, CRYPT_LUKS2, CRYPT_TCRYPT, CRYPT_BITLK or CRYPT_PLAIN */
 static char *arg_cipher = NULL;
 static unsigned arg_key_size = 0;
 static unsigned arg_sector_size = CRYPT_SECTOR_SIZE;
 static int arg_key_slot = CRYPT_ANY_SLOT;
 static unsigned arg_keyfile_size = 0;
 static uint64_t arg_keyfile_offset = 0;
+static bool arg_keyfile_erase = false;
+static bool arg_try_empty_password = false;
 static char *arg_hash = NULL;
 static char *arg_header = NULL;
 static unsigned arg_tries = 3;
@@ -67,12 +72,13 @@ STATIC_DESTRUCTOR_REGISTER(arg_pkcs11_uri, freep);
 
 /* Options Debian's crypttab knows we don't:
 
-    precheck=
     check=
     checkargs=
-    noearly=
-    loud=
+    noearly
+    loud
+    quiet
     keyscript=
+    initramfs
 */
 
 static int parse_one_option(const char *option) {
@@ -126,7 +132,8 @@ static int parse_one_option(const char *option) {
                         return 0;
                 }
 
-        } else if ((val = startswith(option, "key-slot="))) {
+        } else if ((val = startswith(option, "key-slot=")) ||
+                   (val = startswith(option, "keyslot="))) {
 
                 arg_type = ANY_LUKS;
                 r = safe_atoi(val, &arg_key_slot);
@@ -160,7 +167,20 @@ static int parse_one_option(const char *option) {
                         return 0;
                 }
 
-        } else if ((val = startswith(option, "hash="))) {
+        } else if ((val = startswith(option, "keyfile-erase="))) {
+
+                r = parse_boolean(val);
+                if (r < 0) {
+                        log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
+                        return 0;
+                }
+
+                arg_keyfile_erase = r;
+
+        } else if (streq(option, "keyfile-erase"))
+                arg_keyfile_erase = true;
+
+        else if ((val = startswith(option, "hash="))) {
                 r = free_and_strdup(&arg_hash, val);
                 if (r < 0)
                         return log_oom();
@@ -200,18 +220,24 @@ static int parse_one_option(const char *option) {
                 arg_submit_from_crypt_cpus = true;
         else if (streq(option, "luks"))
                 arg_type = ANY_LUKS;
+/* since cryptsetup 2.3.0 (Feb 2020) */
+#ifdef CRYPT_BITLK
+        else if (streq(option, "bitlk"))
+                arg_type = CRYPT_BITLK;
+#endif
         else if (streq(option, "tcrypt"))
                 arg_type = CRYPT_TCRYPT;
-        else if (streq(option, "tcrypt-hidden")) {
+        else if (STR_IN_SET(option, "tcrypt-hidden", "tcrypthidden")) {
                 arg_type = CRYPT_TCRYPT;
                 arg_tcrypt_hidden = true;
         } else if (streq(option, "tcrypt-system")) {
                 arg_type = CRYPT_TCRYPT;
                 arg_tcrypt_system = true;
-        } else if (streq(option, "tcrypt-veracrypt")) {
+        } else if (STR_IN_SET(option, "tcrypt-veracrypt", "veracrypt")) {
                 arg_type = CRYPT_TCRYPT;
                 arg_tcrypt_veracrypt = true;
-        } else if (STR_IN_SET(option, "plain", "swap", "tmp"))
+        } else if (STR_IN_SET(option, "plain", "swap", "tmp") ||
+                   startswith(option, "tmp="))
                 arg_type = CRYPT_PLAIN;
         else if ((val = startswith(option, "timeout="))) {
 
@@ -242,7 +268,20 @@ static int parse_one_option(const char *option) {
                 if (r < 0)
                         return log_oom();
 
-        } else if (!streq(option, "x-initrd.attach"))
+        } else if ((val = startswith(option, "try-empty-password="))) {
+
+                r = parse_boolean(val);
+                if (r < 0) {
+                        log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
+                        return 0;
+                }
+
+                arg_try_empty_password = r;
+
+        } else if (streq(option, "try-empty-password"))
+                arg_try_empty_password = true;
+
+        else if (!streq(option, "x-initrd.attach"))
                 log_warning("Encountered unknown /etc/crypttab option '%s', ignoring.", option);
 
         return 0;
@@ -440,6 +479,8 @@ static int attach_tcrypt(
                 struct crypt_device *cd,
                 const char *name,
                 const char *key_file,
+                const void *key_data,
+                size_t key_data_size,
                 char **passwords,
                 uint32_t flags) {
 
@@ -453,7 +494,7 @@ static int attach_tcrypt(
 
         assert(cd);
         assert(name);
-        assert(key_file || (passwords && passwords[0]));
+        assert(key_file || key_data || !strv_isempty(passwords));
 
         if (arg_pkcs11_uri)
                 /* Ask for a regular password */
@@ -469,22 +510,33 @@ static int attach_tcrypt(
         if (arg_tcrypt_veracrypt)
                 params.flags |= CRYPT_TCRYPT_VERA_MODES;
 
-        if (key_file) {
-                r = read_one_line_file(key_file, &passphrase);
-                if (r < 0) {
-                        log_error_errno(r, "Failed to read password file '%s': %m", key_file);
-                        return -EAGAIN; /* log with the actual error, but return EAGAIN */
-                }
+        if (key_data) {
+                params.passphrase = key_data;
+                params.passphrase_size = key_data_size;
+        } else {
+                if (key_file) {
+                        r = read_one_line_file(key_file, &passphrase);
+                        if (r < 0) {
+                                log_error_errno(r, "Failed to read password file '%s': %m", key_file);
+                                return -EAGAIN; /* log with the actual error, but return EAGAIN */
+                        }
 
-                params.passphrase = passphrase;
-        } else
-                params.passphrase = passwords[0];
-        params.passphrase_size = strlen(params.passphrase);
+                        params.passphrase = passphrase;
+                } else
+                        params.passphrase = passwords[0];
+
+                params.passphrase_size = strlen(params.passphrase);
+        }
 
         r = crypt_load(cd, CRYPT_TCRYPT, &params);
         if (r < 0) {
-                if (key_file && r == -EPERM) {
-                        log_error_errno(r, "Failed to activate using password file '%s'. (Key data not correct?)", key_file);
+                if (r == -EPERM) {
+                        if (key_data)
+                                log_error_errno(r, "Failed to activate using discovered key. (Key not correct?)");
+
+                        if (key_file)
+                                log_error_errno(r, "Failed to activate using password file '%s'. (Key data not correct?)", key_file);
+
                         return -EAGAIN; /* log the actual error, but return EAGAIN */
                 }
 
@@ -498,10 +550,12 @@ static int attach_tcrypt(
         return 0;
 }
 
-static int attach_luks_or_plain(
+static int attach_luks_or_plain_or_bitlk(
                 struct crypt_device *cd,
                 const char *name,
                 const char *key_file,
+                const void *key_data,
+                size_t key_data_size,
                 char **passwords,
                 uint32_t flags,
                 usec_t until) {
@@ -571,7 +625,7 @@ static int attach_luks_or_plain(
                 _cleanup_free_ char *friendly = NULL;
                 size_t decrypted_key_size = 0;
 
-                if (!key_file)
+                if (!key_file && !key_data)
                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "PKCS#11 mode selected but no key file specified, refusing.");
 
                 friendly = friendly_disk_name(crypt_get_device_name(cd), name);
@@ -584,8 +638,8 @@ static int attach_luks_or_plain(
                         r = decrypt_pkcs11_key(
                                         friendly,
                                         arg_pkcs11_uri,
-                                        key_file,
-                                        arg_keyfile_size, arg_keyfile_offset,
+                                        key_file, arg_keyfile_size, arg_keyfile_offset,
+                                        key_data, key_data_size,
                                         until,
                                         &decrypted_key, &decrypted_key_size);
                         if (r >= 0)
@@ -620,7 +674,7 @@ static int attach_luks_or_plain(
                                         return log_error_errno(r, "Failed to start device monitor: %m");
 
                                 log_notice("Security token %s not present for unlocking volume %s, please plug it in.",
-                                         arg_pkcs11_uri, friendly);
+                                           arg_pkcs11_uri, friendly);
 
                                 /* Let's immediately rescan in case the token appeared in the time we needed
                                  * to create and configure the monitor */
@@ -668,6 +722,18 @@ static int attach_luks_or_plain(
                 if (r < 0)
                         return log_error_errno(r, "Failed to activate with PKCS#11 acquired key: %m");
 
+        } else if (key_data) {
+                if (pass_volume_key)
+                        r = crypt_activate_by_volume_key(cd, name, key_data, key_data_size, flags);
+                else
+                        r = crypt_activate_by_passphrase(cd, name, arg_key_slot, key_data, key_data_size, flags);
+                if (r == -EPERM) {
+                        log_error_errno(r, "Failed to activate. (Key incorrect?)");
+                        return -EAGAIN; /* Log actual error, but return EAGAIN */
+                }
+                if (r < 0)
+                        return log_error_errno(r, "Failed to activate: %m");
+
         } else if (key_file) {
                 r = crypt_activate_by_keyfile_device_offset(cd, name, arg_key_slot, key_file, arg_keyfile_size, arg_keyfile_offset, flags);
                 if (r == -EPERM) {
@@ -748,6 +814,17 @@ static uint32_t determine_flags(void) {
         return flags;
 }
 
+static void remove_and_erasep(const char **p) {
+        int r;
+
+        if (!*p)
+                return;
+
+        r = unlinkat_deallocate(AT_FDCWD, *p, UNLINK_ERASE);
+        if (r < 0 && r != -ENOENT)
+                log_warning_errno(r, "Unable to erase key file '%s', ignoring: %m", *p);
+}
+
 static int run(int argc, char *argv[]) {
         _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
         int r;
@@ -774,13 +851,19 @@ static int run(int argc, char *argv[]) {
                 unsigned tries;
                 usec_t until;
                 crypt_status_info status;
+                _cleanup_(remove_and_erasep) const char *destroy_key_file = NULL;
                 const char *key_file = NULL;
+                _cleanup_(erase_and_freep) void *key_data = NULL;
+                size_t key_data_size = 0;
 
                 /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */
 
                 if (argc < 4)
                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "attach requires at least two arguments.");
 
+                if (!filename_is_valid(argv[2]))
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Volume name '%s' is not valid.", argv[2]);
+
                 if (argc >= 5 && !STR_IN_SET(argv[4], "", "-", "none")) {
                         if (path_is_absolute(argv[4]))
                                 key_file = argv[4];
@@ -797,6 +880,24 @@ static int run(int argc, char *argv[]) {
                 /* A delicious drop of snake oil */
                 (void) mlockall(MCL_FUTURE);
 
+                if (!key_file) {
+                        const char *fn;
+
+                        /* If a key file is not explicitly specified, search for a key in a well defined
+                         * search path, and load it. */
+
+                        fn = strjoina(argv[2], ".key");
+                        r = load_key_file(fn,
+                                          STRV_MAKE("/etc/cryptsetup-keys.d", "/run/cryptsetup-keys.d"),
+                                          0, 0,  /* Note we leave arg_keyfile_offset/arg_keyfile_size as something that only applies to arg_keyfile! */
+                                          &key_data, &key_data_size);
+                        if (r < 0)
+                                return r;
+                        if (r > 0)
+                                log_debug("Automatically discovered key for volume '%s'.", argv[2]);
+                } else if (arg_keyfile_erase)
+                        destroy_key_file = key_file; /* let's get this baby erased when we leave */
+
                 if (arg_header) {
                         log_debug("LUKS header: %s", arg_header);
                         r = crypt_init(&cd, arg_header);
@@ -831,8 +932,8 @@ static int run(int argc, char *argv[]) {
                                 log_warning("Key file %s is world-readable. This is not a good idea!", key_file);
                 }
 
-                if (!arg_type || STR_IN_SET(arg_type, ANY_LUKS, CRYPT_LUKS1)) {
-                        r = crypt_load(cd, CRYPT_LUKS, NULL);
+                if (!arg_type || STR_IN_SET(arg_type, ANY_LUKS, CRYPT_LUKS1, CRYPT_LUKS2)) {
+                        r = crypt_load(cd, !arg_type || streq(arg_type, ANY_LUKS) ? CRYPT_LUKS : arg_type, NULL);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to load LUKS superblock on device %s: %m", crypt_get_device_name(cd));
 
@@ -843,7 +944,7 @@ static int run(int argc, char *argv[]) {
                         }
 
                         /* Tokens are available in LUKS2 only, but it is ok to call (and fail) with LUKS1. */
-                        if (!key_file) {
+                        if (!key_file && !key_data) {
                                 r = crypt_activate_by_token(cd, argv[2], CRYPT_ANY_TOKEN, NULL, flags);
                                 if (r >= 0) {
                                         log_debug("Volume %s activated with LUKS token id %i.", argv[2], r);
@@ -854,29 +955,65 @@ static int run(int argc, char *argv[]) {
                         }
                 }
 
+/* since cryptsetup 2.3.0 (Feb 2020) */
+#ifdef CRYPT_BITLK
+                if (streq_ptr(arg_type, CRYPT_BITLK)) {
+                        r = crypt_load(cd, CRYPT_BITLK, NULL);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to load Bitlocker superblock on device %s: %m", crypt_get_device_name(cd));
+                }
+#endif
+
                 for (tries = 0; arg_tries == 0 || tries < arg_tries; tries++) {
                         _cleanup_strv_free_erase_ char **passwords = NULL;
 
-                        if (!key_file && !arg_pkcs11_uri) {
-                                r = get_password(argv[2], argv[3], until, tries == 0 && !arg_verify, &passwords);
-                                if (r == -EAGAIN)
-                                        continue;
-                                if (r < 0)
-                                        return r;
+                        /* When we were able to acquire multiple keys, let's always process them in this order:
+                         *
+                         *    1. A key acquired via PKCS#11 token
+                         *    2. The discovered key: i.e. key_data + key_data_size
+                         *    3. The configured key: i.e. key_file + arg_keyfile_offset + arg_keyfile_size
+                         *    4. The empty password, in case arg_try_empty_password is set
+                         *    5. We enquire the user for a password
+                         */
+
+                        if (!key_file && !key_data && !arg_pkcs11_uri) {
+
+                                if (arg_try_empty_password) {
+                                        /* Hmm, let's try an empty password now, but only once */
+                                        arg_try_empty_password = false;
+
+                                        key_data = strdup("");
+                                        if (!key_data)
+                                                return log_oom();
+
+                                        key_data_size = 0;
+                                } else {
+                                        /* Ask the user for a passphrase only as last resort, if we have
+                                         * nothing else to check for */
+
+                                        r = get_password(argv[2], argv[3], until, tries == 0 && !arg_verify, &passwords);
+                                        if (r == -EAGAIN)
+                                                continue;
+                                        if (r < 0)
+                                                return r;
+                                }
                         }
 
                         if (streq_ptr(arg_type, CRYPT_TCRYPT))
-                                r = attach_tcrypt(cd, argv[2], key_file, passwords, flags);
+                                r = attach_tcrypt(cd, argv[2], key_file, key_data, key_data_size, passwords, flags);
                         else
-                                r = attach_luks_or_plain(cd, argv[2], key_file, passwords, flags, until);
+                                r = attach_luks_or_plain_or_bitlk(cd, argv[2], key_file, key_data, key_data_size, passwords, flags, until);
                         if (r >= 0)
                                 break;
                         if (r != -EAGAIN)
                                 return r;
 
-                        /* Passphrase not correct? Let's try again! */
+                        /* Key not correct? Let's try again! */
+
                         key_file = NULL;
-                        arg_pkcs11_uri = NULL;
+                        key_data = erase_and_free(key_data);
+                        key_data_size = 0;
+                        arg_pkcs11_uri = mfree(arg_pkcs11_uri);
                 }
 
                 if (arg_tries != 0 && tries >= arg_tries)
@@ -884,6 +1021,9 @@ static int run(int argc, char *argv[]) {
 
         } else if (streq(argv[1], "detach")) {
 
+                if (!filename_is_valid(argv[2]))
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Volume name '%s' is not valid.", argv[2]);
+
                 r = crypt_init_by_name(&cd, argv[2]);
                 if (r == -ENODEV) {
                         log_info("Volume %s already inactive.", argv[2]);
index 2d80d3a664b3871edcb4c190fba3c5f152c8a6e8..29e5120375e22d52c4892ad191dcde9ee5c323eb 100644 (file)
@@ -643,9 +643,7 @@ static int parse_argv(int argc, char *argv[]) {
 static int run(int argc, char *argv[]) {
         int r, k, n_found = 0;
 
-        log_show_color(true);
-        log_parse_environment();
-        log_open();
+        log_setup_cli();
 
         r = parse_argv(argc, argv);
         if (r <= 0)
index 7fb80ca138abe6d68f97eb1e7506d7fd22c1080b..4f38de8e29fb8315bcae96c45222e9c7402441d6 100644 (file)
@@ -128,9 +128,7 @@ static int run(int argc, char *argv[]) {
          * to detect whether we are being run in a virtualized
          * environment or not */
 
-        log_show_color(true);
-        log_parse_environment();
-        log_open();
+        log_setup_cli();
 
         r = parse_argv(argc, argv);
         if (r <= 0)
index e1418419f7b628b221075440ce4bac8cf1c970e6..66ac638401cf50d8c19fceda7073a37df7833d71 100644 (file)
@@ -12,6 +12,7 @@
 #include "loop-util.h"
 #include "main-func.h"
 #include "parse-util.h"
+#include "path-util.h"
 #include "string-util.h"
 #include "strv.h"
 #include "user-util.h"
@@ -25,21 +26,34 @@ static const char *arg_image = NULL;
 static const char *arg_path = NULL;
 static DissectImageFlags arg_flags = DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_DISCARD_ON_LOOP|DISSECT_IMAGE_RELAX_VAR_CHECK|DISSECT_IMAGE_FSCK;
 static void *arg_root_hash = NULL;
+static char *arg_verity_data = NULL;
 static size_t arg_root_hash_size = 0;
+static char *arg_root_hash_sig_path = NULL;
+static void *arg_root_hash_sig = NULL;
+static size_t arg_root_hash_sig_size = 0;
 
 STATIC_DESTRUCTOR_REGISTER(arg_root_hash, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_verity_data, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_root_hash_sig_path, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_root_hash_sig, freep);
 
 static void help(void) {
         printf("%s [OPTIONS...] IMAGE\n"
                "%s [OPTIONS...] --mount IMAGE PATH\n"
                "Dissect a file system OS image.\n\n"
-               "  -h --help            Show this help\n"
-               "     --version         Show package version\n"
-               "  -m --mount           Mount the image to the specified directory\n"
-               "  -r --read-only       Mount read-only\n"
-               "     --fsck=BOOL       Run fsck before mounting\n"
-               "     --discard=MODE    Choose 'discard' mode (disabled, loop, all, crypto)\n"
-               "     --root-hash=HASH  Specify root hash for verity\n",
+               "  -h --help               Show this help\n"
+               "     --version            Show package version\n"
+               "  -m --mount              Mount the image to the specified directory\n"
+               "  -r --read-only          Mount read-only\n"
+               "     --fsck=BOOL          Run fsck before mounting\n"
+               "     --discard=MODE       Choose 'discard' mode (disabled, loop, all, crypto)\n"
+               "     --root-hash=HASH     Specify root hash for verity\n"
+               "     --root-hash-sig=SIG  Specify pkcs7 signature of root hash for verity\n"
+               "                          as a DER encoded PKCS7, either as a path to a file\n"
+               "                          or as an ASCII base64 encoded string prefixed by\n"
+               "                          'base64:'\n"
+               "     --verity-data=PATH   Specify data file with hash tree for verity if it is\n"
+               "                          not embedded in IMAGE\n",
                program_invocation_short_name,
                program_invocation_short_name);
 }
@@ -51,16 +65,20 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_DISCARD,
                 ARG_ROOT_HASH,
                 ARG_FSCK,
+                ARG_VERITY_DATA,
+                ARG_ROOT_HASH_SIG,
         };
 
         static const struct option options[] = {
-                { "help",      no_argument,       NULL, 'h'           },
-                { "version",   no_argument,       NULL, ARG_VERSION   },
-                { "mount",     no_argument,       NULL, 'm'           },
-                { "read-only", no_argument,       NULL, 'r'           },
-                { "discard",   required_argument, NULL, ARG_DISCARD   },
-                { "root-hash", required_argument, NULL, ARG_ROOT_HASH },
-                { "fsck",      required_argument, NULL, ARG_FSCK      },
+                { "help",          no_argument,       NULL, 'h'               },
+                { "version",       no_argument,       NULL, ARG_VERSION       },
+                { "mount",         no_argument,       NULL, 'm'               },
+                { "read-only",     no_argument,       NULL, 'r'               },
+                { "discard",       required_argument, NULL, ARG_DISCARD       },
+                { "root-hash",     required_argument, NULL, ARG_ROOT_HASH     },
+                { "fsck",          required_argument, NULL, ARG_FSCK          },
+                { "verity-data",   required_argument, NULL, ARG_VERITY_DATA   },
+                { "root-hash-sig", required_argument, NULL, ARG_ROOT_HASH_SIG },
                 {}
         };
 
@@ -127,6 +145,37 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
                 }
 
+                case ARG_VERITY_DATA:
+                        r = parse_path_argument_and_warn(optarg, false, &arg_verity_data);
+                        if (r < 0)
+                                return r;
+                        break;
+
+                case ARG_ROOT_HASH_SIG: {
+                        char *value;
+
+                        if ((value = startswith(optarg, "base64:"))) {
+                                void *p;
+                                size_t l;
+
+                                r = unbase64mem(value, strlen(value), &p, &l);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to parse root hash signature '%s': %m", optarg);
+
+                                free_and_replace(arg_root_hash_sig, p);
+                                arg_root_hash_sig_size = l;
+                                arg_root_hash_sig_path = mfree(arg_root_hash_sig_path);
+                        } else {
+                                r = parse_path_argument_and_warn(optarg, false, &arg_root_hash_sig_path);
+                                if (r < 0)
+                                        return r;
+                                arg_root_hash_sig = mfree(arg_root_hash_sig);
+                                arg_root_hash_sig_size = 0;
+                        }
+
+                        break;
+                }
+
                 case ARG_FSCK:
                         r = parse_boolean(optarg);
                         if (r < 0)
@@ -188,13 +237,14 @@ static int run(int argc, char *argv[]) {
         if (r < 0)
                 return log_error_errno(r, "Failed to set up loopback device: %m");
 
-        if (!arg_root_hash) {
-                r = root_hash_load(arg_image, &arg_root_hash, &arg_root_hash_size);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to read root hash file for %s: %m", arg_image);
-        }
+        r = verity_metadata_load(arg_image, NULL, arg_root_hash ? NULL : &arg_root_hash, &arg_root_hash_size,
+                           arg_verity_data ? NULL : &arg_verity_data,
+                           arg_root_hash_sig_path || arg_root_hash_sig ? NULL : &arg_root_hash_sig_path);
+        if (r < 0)
+                return log_error_errno(r, "Failed to read verity artefacts for %s: %m", arg_image);
+        arg_flags |= arg_verity_data ? DISSECT_IMAGE_NO_PARTITION_TABLE : 0;
 
-        r = dissect_image_and_warn(d->fd, arg_image, arg_root_hash, arg_root_hash_size, arg_flags, &m);
+        r = dissect_image_and_warn(d->fd, arg_image, arg_root_hash, arg_root_hash_size, arg_verity_data, arg_flags, &m);
         if (r < 0)
                 return r;
 
@@ -205,7 +255,6 @@ static int run(int argc, char *argv[]) {
 
                 for (i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
                         DissectedPartition *p = m->partitions + i;
-                        int k;
 
                         if (!p->found)
                                 continue;
@@ -223,9 +272,8 @@ static int run(int argc, char *argv[]) {
                         if (p->architecture != _ARCHITECTURE_INVALID)
                                 printf(" for %s", architecture_to_string(p->architecture));
 
-                        k = PARTITION_VERITY_OF(i);
-                        if (k >= 0)
-                                printf(" %s verity", m->partitions[k].found ? "with" : "without");
+                        if (dissected_image_can_do_verity(m, i))
+                                printf(" %s verity", dissected_image_has_verity(m, i) ? "with" : "without");
 
                         if (p->partno >= 0)
                                 printf(" on partition #%i", p->partno);
@@ -268,7 +316,7 @@ static int run(int argc, char *argv[]) {
         }
 
         case ACTION_MOUNT:
-                r = dissected_image_decrypt_interactively(m, NULL, arg_root_hash, arg_root_hash_size, arg_flags, &di);
+                r = dissected_image_decrypt_interactively(m, NULL, arg_root_hash, arg_root_hash_size, arg_verity_data, arg_root_hash_sig_path, arg_root_hash_sig, arg_root_hash_sig_size, arg_flags, &di);
                 if (r < 0)
                         return r;
 
index 5fe51359f60d50a79ed7ae5ae24522640e011dbf..4a14c23f11499dceb7555459033da17eb30172b3 100644 (file)
@@ -20,7 +20,7 @@ static int environment_dirs(char ***ret) {
                 return -ENOMEM;
 
         /* ~/.config/systemd/environment.d */
-        r = sd_path_home(SD_PATH_USER_CONFIGURATION, "environment.d", &c);
+        r = sd_path_lookup(SD_PATH_USER_CONFIGURATION, "environment.d", &c);
         if (r < 0)
                 return r;
 
index 9066c30853bc8bfd682d69c3e2d06a34551febb2..0c543a90f6b7994267f42f28ebe32af9642ff398 100644 (file)
@@ -159,9 +159,7 @@ static int run(int argc, char *argv[]) {
         char **i;
         int r;
 
-        log_show_color(true);
-        log_parse_environment();
-        log_open();
+        log_setup_cli();
 
         r = parse_argv(argc, argv);
         if (r <= 0)
index 901fbf081591347bc2b480ed7097ee3b9f24dd2a..a3f442518ec43d207c8290523e2716a33f5cb1b5 100644 (file)
@@ -2,6 +2,7 @@
 
 #include <fcntl.h>
 #include <getopt.h>
+#include <linux/loop.h>
 #include <unistd.h>
 
 #include "sd-id128.h"
@@ -9,6 +10,7 @@
 #include "alloc-util.h"
 #include "ask-password-api.h"
 #include "copy.h"
+#include "dissect-image.h"
 #include "env-file.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "kbd-util.h"
 #include "libcrypt-util.h"
 #include "locale-util.h"
+#include "loop-util.h"
 #include "main-func.h"
 #include "memory-util.h"
 #include "mkdir.h"
+#include "mount-util.h"
+#include "namespace-util.h"
 #include "os-util.h"
 #include "parse-util.h"
 #include "path-util.h"
 #include "strv.h"
 #include "terminal-util.h"
 #include "time-util.h"
+#include "tmpfile-util-label.h"
+#include "tmpfile-util.h"
 #include "umask-util.h"
 #include "user-util.h"
 
 static char *arg_root = NULL;
+static char *arg_image = NULL;
 static char *arg_locale = NULL;  /* $LANG */
 static char *arg_keymap = NULL;
 static char *arg_locale_messages = NULL; /* $LC_MESSAGES */
@@ -41,17 +49,26 @@ static char *arg_timezone = NULL;
 static char *arg_hostname = NULL;
 static sd_id128_t arg_machine_id = {};
 static char *arg_root_password = NULL;
+static char *arg_root_shell = NULL;
+static char *arg_kernel_cmdline = NULL;
 static bool arg_prompt_locale = false;
 static bool arg_prompt_keymap = false;
 static bool arg_prompt_timezone = false;
 static bool arg_prompt_hostname = false;
 static bool arg_prompt_root_password = false;
+static bool arg_prompt_root_shell = false;
 static bool arg_copy_locale = false;
 static bool arg_copy_keymap = false;
 static bool arg_copy_timezone = false;
 static bool arg_copy_root_password = false;
+static bool arg_copy_root_shell = false;
+static bool arg_force = false;
+static bool arg_delete_root_password = false;
+static bool arg_root_password_is_hashed = false;
+static bool arg_welcome = true;
 
 STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_locale, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_locale_messages, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_keymap, freep);
@@ -80,6 +97,9 @@ static void print_welcome(void) {
         const char *pn;
         int r;
 
+        if (!arg_welcome)
+                return;
+
         if (done)
                 return;
 
@@ -205,6 +225,14 @@ static int prompt_loop(const char *text, char **l, unsigned percentage, bool (*i
         }
 }
 
+static bool locale_is_ok(const char *name) {
+
+        if (arg_root)
+                return locale_is_valid(name);
+
+        return locale_is_installed(name) > 0;
+}
+
 static int prompt_locale(void) {
         _cleanup_strv_free_ char **locales = NULL;
         int r;
@@ -238,7 +266,7 @@ static int prompt_locale(void) {
                 print_welcome();
 
                 r = prompt_loop("Please enter system locale name or number",
-                                locales, 60, locale_is_valid, &arg_locale);
+                                locales, 60, locale_is_ok, &arg_locale);
                 if (r < 0)
                         return r;
 
@@ -246,7 +274,7 @@ static int prompt_locale(void) {
                         return 0;
 
                 r = prompt_loop("Please enter system message locale name or number",
-                                locales, 60, locale_is_valid, &arg_locale_messages);
+                                locales, 60, locale_is_ok, &arg_locale_messages);
                 if (r < 0)
                         return r;
 
@@ -265,7 +293,7 @@ static int process_locale(void) {
         int r;
 
         etc_localeconf = prefix_roota(arg_root, "/etc/locale.conf");
-        if (laccess(etc_localeconf, F_OK) >= 0)
+        if (laccess(etc_localeconf, F_OK) >= 0 && !arg_force)
                 return 0;
 
         if (arg_copy_locale && arg_root) {
@@ -332,7 +360,7 @@ static int process_keymap(void) {
         int r;
 
         etc_vconsoleconf = prefix_roota(arg_root, "/etc/vconsole.conf");
-        if (laccess(etc_vconsoleconf, F_OK) >= 0)
+        if (laccess(etc_vconsoleconf, F_OK) >= 0 && !arg_force)
                 return 0;
 
         if (arg_copy_keymap && arg_root) {
@@ -404,7 +432,7 @@ static int process_timezone(void) {
         int r;
 
         etc_localtime = prefix_roota(arg_root, "/etc/localtime");
-        if (laccess(etc_localtime, F_OK) >= 0)
+        if (laccess(etc_localtime, F_OK) >= 0 && !arg_force)
                 return 0;
 
         if (arg_copy_timezone && arg_root) {
@@ -484,7 +512,7 @@ static int process_hostname(void) {
         int r;
 
         etc_hostname = prefix_roota(arg_root, "/etc/hostname");
-        if (laccess(etc_hostname, F_OK) >= 0)
+        if (laccess(etc_hostname, F_OK) >= 0 && !arg_force)
                 return 0;
 
         r = prompt_hostname();
@@ -495,7 +523,8 @@ static int process_hostname(void) {
                 return 0;
 
         r = write_string_file(etc_hostname, arg_hostname,
-                              WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_SYNC | WRITE_STRING_FILE_MKDIR_0755);
+                              WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_SYNC | WRITE_STRING_FILE_MKDIR_0755 |
+                              (arg_force ? WRITE_STRING_FILE_ATOMIC : 0));
         if (r < 0)
                 return log_error_errno(r, "Failed to write %s: %m", etc_hostname);
 
@@ -509,14 +538,15 @@ static int process_machine_id(void) {
         int r;
 
         etc_machine_id = prefix_roota(arg_root, "/etc/machine-id");
-        if (laccess(etc_machine_id, F_OK) >= 0)
+        if (laccess(etc_machine_id, F_OK) >= 0 && !arg_force)
                 return 0;
 
         if (sd_id128_is_null(arg_machine_id))
                 return 0;
 
         r = write_string_file(etc_machine_id, sd_id128_to_string(arg_machine_id, id),
-                              WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_SYNC | WRITE_STRING_FILE_MKDIR_0755);
+                              WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_SYNC | WRITE_STRING_FILE_MKDIR_0755 |
+                              (arg_force ? WRITE_STRING_FILE_ATOMIC : 0));
         if (r < 0)
                 return log_error_errno(r, "Failed to write machine id: %m");
 
@@ -525,7 +555,7 @@ static int process_machine_id(void) {
 }
 
 static int prompt_root_password(void) {
-        const char *msg1, *msg2, *etc_shadow;
+        const char *msg1, *msg2;
         int r;
 
         if (arg_root_password)
@@ -534,10 +564,6 @@ static int prompt_root_password(void) {
         if (!arg_prompt_root_password)
                 return 0;
 
-        etc_shadow = prefix_roota(arg_root, "/etc/shadow");
-        if (laccess(etc_shadow, F_OK) >= 0)
-                return 0;
-
         print_welcome();
         putchar('\n');
 
@@ -578,95 +604,281 @@ static int prompt_root_password(void) {
         return 0;
 }
 
-static int write_root_shadow(const char *path, const struct spwd *p) {
-        _cleanup_fclose_ FILE *f = NULL;
+static int find_shell(const char *path, const char *root) {
         int r;
 
         assert(path);
-        assert(p);
 
-        RUN_WITH_UMASK(0777)
-                f = fopen(path, "wex");
-        if (!f)
-                return -errno;
+        if (!valid_shell(path))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "%s is not a valid shell", path);
+
+        r = chase_symlinks(path, root, CHASE_PREFIX_ROOT, NULL, NULL);
+        if (r < 0) {
+                const char *p;
+                p = prefix_roota(root, path);
+                return log_error_errno(r, "Failed to resolve shell %s: %m", p);
+        }
+
+        return 0;
+}
+
+static int prompt_root_shell(void) {
+        int r;
+
+        if (arg_root_shell || !arg_prompt_root_shell)
+                return 0;
+
+        print_welcome();
+        putchar('\n');
+
+        for (;;) {
+                _cleanup_free_ char *s = NULL;
+
+                r = ask_string(&s, "%s Please enter root shell for new system (empty to skip): ", special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET));
+                if (r < 0)
+                        return log_error_errno(r, "Failed to query root shell: %m");
+
+                if (isempty(s)) {
+                        log_warning("No shell entered, skipping.");
+                        break;
+                }
+
+                r = find_shell(s, arg_root);
+                if (r < 0)
+                        continue;
+
+                arg_root_shell = TAKE_PTR(s);
+                break;
+        }
+
+        return 0;
+}
+
+static int write_root_passwd(const char *passwd_path, const char *password, const char *shell) {
+        _cleanup_fclose_ FILE *original = NULL, *passwd = NULL;
+        _cleanup_(unlink_and_freep) char *passwd_tmp = NULL;
+        int r;
+
+        assert(password);
+
+        r = fopen_temporary_label("/etc/passwd", passwd_path, &passwd, &passwd_tmp);
+        if (r < 0)
+                return r;
+
+        original = fopen(passwd_path, "re");
+        if (original) {
+                struct passwd *i;
+
+                r = sync_rights(fileno(original), fileno(passwd));
+                if (r < 0)
+                        return r;
+
+                while ((r = fgetpwent_sane(original, &i)) > 0) {
+
+                        if (streq(i->pw_name, "root")) {
+                                i->pw_passwd = (char *) password;
+                                if (shell)
+                                        i->pw_shell = (char *) shell;
+                        }
+
+                        r = putpwent_sane(i, passwd);
+                        if (r < 0)
+                                return r;
+                }
+                if (r < 0)
+                        return r;
+
+        } else {
+                struct passwd root = {
+                        .pw_name = (char *) "root",
+                        .pw_passwd = (char *) password,
+                        .pw_uid = 0,
+                        .pw_gid = 0,
+                        .pw_gecos = (char *) "Super User",
+                        .pw_dir = (char *) "/root",
+                        .pw_shell = (char *) (shell ?: "/bin/sh"),
+                };
+
+                if (errno != ENOENT)
+                        return -errno;
+
+                r = fchmod(fileno(passwd), 0644);
+                if (r < 0)
+                        return -errno;
+
+                r = putpwent_sane(&root, passwd);
+                if (r < 0)
+                        return r;
+        }
 
-        r = putspent_sane(p, f);
+        r = fflush_sync_and_check(passwd);
         if (r < 0)
                 return r;
 
-        return fflush_sync_and_check(f);
+        r = rename_and_apply_smack_floor_label(passwd_tmp, passwd_path);
+        if (r < 0)
+                return r;
+
+        return 0;
 }
 
-static int process_root_password(void) {
+static int write_root_shadow(const char *shadow_path, const char *hashed_password) {
+        _cleanup_fclose_ FILE *original = NULL, *shadow = NULL;
+        _cleanup_(unlink_and_freep) char *shadow_tmp = NULL;
+        int r;
 
-        struct spwd item = {
-                .sp_namp = (char*) "root",
-                .sp_min = -1,
-                .sp_max = -1,
-                .sp_warn = -1,
-                .sp_inact = -1,
-                .sp_expire = -1,
-                .sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */
-        };
-        _cleanup_free_ char *salt = NULL;
+        assert(hashed_password);
+
+        r = fopen_temporary_label("/etc/shadow", shadow_path, &shadow, &shadow_tmp);
+        if (r < 0)
+                return r;
+
+        original = fopen(shadow_path, "re");
+        if (original) {
+                struct spwd *i;
+
+                r = sync_rights(fileno(original), fileno(shadow));
+                if (r < 0)
+                        return r;
+
+                while ((r = fgetspent_sane(original, &i)) > 0) {
+
+                        if (streq(i->sp_namp, "root")) {
+                                i->sp_pwdp = (char *) hashed_password;
+                                i->sp_lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY);
+                        }
+
+                        r = putspent_sane(i, shadow);
+                        if (r < 0)
+                                return r;
+                }
+                if (r < 0)
+                        return r;
+
+        } else {
+                struct spwd root = {
+                        .sp_namp = (char*) "root",
+                        .sp_pwdp = (char *) hashed_password,
+                        .sp_lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY),
+                        .sp_min = -1,
+                        .sp_max = -1,
+                        .sp_warn = -1,
+                        .sp_inact = -1,
+                        .sp_expire = -1,
+                        .sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */
+                };
+
+                if (errno != ENOENT)
+                        return -errno;
+
+                r = fchmod(fileno(shadow), 0000);
+                if (r < 0)
+                        return -errno;
+
+                r = putspent_sane(&root, shadow);
+                if (r < 0)
+                        return r;
+        }
+
+        r = fflush_sync_and_check(shadow);
+        if (r < 0)
+                return r;
+
+        r = rename_and_apply_smack_floor_label(shadow_tmp, shadow_path);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+static int process_root_args(void) {
         _cleanup_close_ int lock = -1;
         struct crypt_data cd = {};
-
-        const char *etc_shadow;
+        const char *password, *hashed_password;
+        const char *etc_passwd, *etc_shadow;
         int r;
 
+        etc_passwd = prefix_roota(arg_root, "/etc/passwd");
         etc_shadow = prefix_roota(arg_root, "/etc/shadow");
-        if (laccess(etc_shadow, F_OK) >= 0)
+
+        /* We only mess with passwd and shadow if both do not exist or --force is specified. These files are
+         * tightly coupled and hence we make sure we have permission from the user to create/modify both
+         * files. */
+        if ((laccess(etc_passwd, F_OK) >= 0 || laccess(etc_shadow, F_OK) >= 0) && !arg_force)
                 return 0;
 
-        (void) mkdir_parents(etc_shadow, 0755);
+        (void) mkdir_parents(etc_passwd, 0755);
 
         lock = take_etc_passwd_lock(arg_root);
         if (lock < 0)
-                return log_error_errno(lock, "Failed to take a lock: %m");
+                return log_error_errno(lock, "Failed to take a lock on %s: %m", etc_passwd);
+
+        if (arg_copy_root_shell && arg_root) {
+                struct passwd *p;
+
+                errno = 0;
+                p = getpwnam("root");
+                if (!p)
+                        return log_error_errno(errno_or_else(EIO), "Failed to find passwd entry for root: %m");
+
+                r = free_and_strdup(&arg_root_shell, p->pw_shell);
+                if (r < 0)
+                        return log_oom();
+        }
+
+        r = prompt_root_shell();
+        if (r < 0)
+                return r;
 
         if (arg_copy_root_password && arg_root) {
                 struct spwd *p;
 
                 errno = 0;
                 p = getspnam("root");
-                if (p || errno != ENOENT) {
-                        if (!p) {
-                                if (!errno)
-                                        errno = EIO;
-
-                                return log_error_errno(errno, "Failed to find shadow entry for root: %m");
-                        }
+                if (!p)
+                        return log_error_errno(errno_or_else(EIO), "Failed to find shadow entry for root: %m");
 
-                        r = write_root_shadow(etc_shadow, p);
-                        if (r < 0)
-                                return log_error_errno(r, "Failed to write %s: %m", etc_shadow);
+                r = free_and_strdup(&arg_root_password, p->sp_pwdp);
+                if (r < 0)
+                        return log_oom();
 
-                        log_info("%s copied.", etc_shadow);
-                        return 0;
-                }
+                arg_root_password_is_hashed = true;
         }
 
         r = prompt_root_password();
         if (r < 0)
                 return r;
 
-        if (!arg_root_password)
-                return 0;
+        if (arg_root_password && arg_root_password_is_hashed) {
+                password = "x";
+                hashed_password = arg_root_password;
+        } else if (arg_root_password) {
+                _cleanup_free_ char *salt = NULL;
+                /* hashed_password points inside cd after crypt_r returns so cd has function scope. */
 
-        r = make_salt(&salt);
-        if (r < 0)
-                return log_error_errno(r, "Failed to get salt: %m");
+                password = "x";
 
-        errno = 0;
-        item.sp_pwdp = crypt_r(arg_root_password, salt, &cd);
-        if (!item.sp_pwdp)
-                return log_error_errno(errno == 0 ? SYNTHETIC_ERRNO(EINVAL) : errno,
-                                       "Failed to encrypt password: %m");
+                r = make_salt(&salt);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to get salt: %m");
 
-        item.sp_lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY);
+                errno = 0;
+                hashed_password = crypt_r(arg_root_password, salt, &cd);
+                if (!hashed_password)
+                        return log_error_errno(errno == 0 ? SYNTHETIC_ERRNO(EINVAL) : errno,
+                                        "Failed to encrypt password: %m");
+        } else if (arg_delete_root_password)
+                password = hashed_password = "";
+        else
+                password = hashed_password = "!";
 
-        r = write_root_shadow(etc_shadow, &item);
+        r = write_root_passwd(etc_passwd, password, arg_root_shell);
+        if (r < 0)
+                return log_error_errno(r, "Failed to write %s: %m", etc_passwd);
+
+        log_info("%s written", etc_passwd);
+
+        r = write_root_shadow(etc_shadow, hashed_password);
         if (r < 0)
                 return log_error_errno(r, "Failed to write %s: %m", etc_shadow);
 
@@ -674,6 +886,96 @@ static int process_root_password(void) {
         return 0;
 }
 
+static int process_kernel_cmdline(void) {
+        const char *etc_kernel_cmdline;
+        int r;
+
+        etc_kernel_cmdline = prefix_roota(arg_root, "/etc/kernel/cmdline");
+        if (laccess(etc_kernel_cmdline, F_OK) >= 0 && !arg_force)
+                return 0;
+
+        if (!arg_kernel_cmdline)
+                return 0;
+
+        r = write_string_file(etc_kernel_cmdline, arg_kernel_cmdline,
+                              WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_SYNC | WRITE_STRING_FILE_MKDIR_0755 |
+                              (arg_force ? WRITE_STRING_FILE_ATOMIC : 0));
+        if (r < 0)
+                return log_error_errno(r, "Failed to write %s: %m", etc_kernel_cmdline);
+
+        log_info("%s written.", etc_kernel_cmdline);
+        return 0;
+}
+
+static int setup_image(char **ret_mount_dir, LoopDevice **ret_loop_device, DecryptedImage **ret_decrypted_image) {
+        DissectImageFlags f = DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_VALIDATE_OS|DISSECT_IMAGE_RELAX_VAR_CHECK|DISSECT_IMAGE_FSCK;
+        _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
+        _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
+        _cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL;
+        _cleanup_(rmdir_and_freep) char *mount_dir = NULL;
+        _cleanup_free_ char *temp = NULL;
+        int r;
+
+        if (!arg_image) {
+                *ret_mount_dir = NULL;
+                *ret_decrypted_image = NULL;
+                *ret_loop_device = NULL;
+                return 0;
+        }
+
+        assert(!arg_root);
+
+        r = tempfn_random_child(NULL, "firstboot", &temp);
+        if (r < 0)
+                return log_error_errno(r, "Failed to generate temporary mount directory: %m");
+
+        r = loop_device_make_by_path(arg_image, O_RDWR, LO_FLAGS_PARTSCAN, &d);
+        if (r < 0)
+                return log_error_errno(r, "Failed to set up loopback device: %m");
+
+        r = dissect_image_and_warn(d->fd, arg_image, NULL, 0, NULL, f, &dissected_image);
+        if (r < 0)
+                return r;
+
+        r = dissected_image_decrypt_interactively(dissected_image, NULL, NULL, 0, NULL, NULL, NULL, 0, f, &decrypted_image);
+        if (r < 0)
+                return r;
+
+        r = detach_mount_namespace();
+        if (r < 0)
+                return log_error_errno(r, "Failed to detach mount namespace: %m");
+
+        mount_dir = strdup(temp);
+        if (!mount_dir)
+                return log_oom();
+
+        r = mkdir_p(mount_dir, 0700);
+        if (r < 0) {
+                mount_dir = mfree(mount_dir);
+                return log_error_errno(r, "Failed to create mount point: %m");
+        }
+
+        r = dissected_image_mount(dissected_image, mount_dir, UID_INVALID, f);
+        if (r < 0)
+                return log_error_errno(r, "Failed to mount image: %m");
+
+        if (decrypted_image) {
+                r = decrypted_image_relinquish(decrypted_image);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to relinquish DM devices: %m");
+        }
+
+        loop_device_relinquish(d);
+
+        arg_root = TAKE_PTR(temp);
+
+        *ret_mount_dir = TAKE_PTR(mount_dir);
+        *ret_decrypted_image = TAKE_PTR(decrypted_image);
+        *ret_loop_device = TAKE_PTR(d);
+
+        return 1;
+}
+
 static int help(void) {
         _cleanup_free_ char *link = NULL;
         int r;
@@ -684,29 +986,37 @@ static int help(void) {
 
         printf("%s [OPTIONS...]\n\n"
                "Configures basic settings of the system.\n\n"
-               "  -h --help                    Show this help\n"
-               "     --version                 Show package version\n"
-               "     --root=PATH               Operate on an alternate filesystem root\n"
-               "     --locale=LOCALE           Set primary locale (LANG=)\n"
-               "     --locale-messages=LOCALE  Set message locale (LC_MESSAGES=)\n"
-               "     --keymap=KEYMAP           Set keymap\n"
-               "     --timezone=TIMEZONE       Set timezone\n"
-               "     --hostname=NAME           Set host name\n"
-               "     --machine-ID=ID           Set machine ID\n"
-               "     --root-password=PASSWORD  Set root password\n"
-               "     --root-password-file=FILE Set root password from file\n"
-               "     --prompt-locale           Prompt the user for locale settings\n"
-               "     --prompt-keymap           Prompt the user for keymap settings\n"
-               "     --prompt-timezone         Prompt the user for timezone\n"
-               "     --prompt-hostname         Prompt the user for hostname\n"
-               "     --prompt-root-password    Prompt the user for root password\n"
-               "     --prompt                  Prompt for all of the above\n"
-               "     --copy-locale             Copy locale from host\n"
-               "     --copy-keymap             Copy keymap from host\n"
-               "     --copy-timezone           Copy timezone from host\n"
-               "     --copy-root-password      Copy root password from host\n"
-               "     --copy                    Copy locale, keymap, timezone, root password\n"
-               "     --setup-machine-id        Generate a new random machine ID\n"
+               "  -h --help                                 Show this help\n"
+               "     --version                              Show package version\n"
+               "     --root=PATH                            Operate on an alternate filesystem root\n"
+               "     --image=PATH                           Operate on an alternate filesystem image\n"
+               "     --locale=LOCALE                        Set primary locale (LANG=)\n"
+               "     --locale-messages=LOCALE               Set message locale (LC_MESSAGES=)\n"
+               "     --keymap=KEYMAP                        Set keymap\n"
+               "     --timezone=TIMEZONE                    Set timezone\n"
+               "     --hostname=NAME                        Set hostname\n"
+               "     --machine-ID=ID                        Set machine ID\n"
+               "     --root-password=PASSWORD               Set root password from plaintext password\n"
+               "     --root-password-file=FILE              Set root password from file\n"
+               "     --root-password-hashed=HASHED_PASSWORD Set root password from hashed password\n"
+               "     --root-shell=SHELL                     Set root shell\n"
+               "     --prompt-locale                        Prompt the user for locale settings\n"
+               "     --prompt-keymap                        Prompt the user for keymap settings\n"
+               "     --prompt-timezone                      Prompt the user for timezone\n"
+               "     --prompt-hostname                      Prompt the user for hostname\n"
+               "     --prompt-root-password                 Prompt the user for root password\n"
+               "     --prompt-root-shell                    Prompt the user for root shell\n"
+               "     --prompt                               Prompt for all of the above\n"
+               "     --copy-locale                          Copy locale from host\n"
+               "     --copy-keymap                          Copy keymap from host\n"
+               "     --copy-timezone                        Copy timezone from host\n"
+               "     --copy-root-password                   Copy root password from host\n"
+               "     --copy-root-shell                      Copy root shell from host\n"
+               "     --copy                                 Copy locale, keymap, timezone, root password\n"
+               "     --setup-machine-id                     Generate a new random machine ID\n"
+               "     --force                                Overwrite existing files\n"
+               "     --delete-root-password                 Delete root password\n"
+               "     --welcome=no                           Disable the welcome text\n"
                "\nSee the %s for details.\n"
                , program_invocation_short_name
                , link
@@ -720,6 +1030,7 @@ static int parse_argv(int argc, char *argv[]) {
         enum {
                 ARG_VERSION = 0x100,
                 ARG_ROOT,
+                ARG_IMAGE,
                 ARG_LOCALE,
                 ARG_LOCALE_MESSAGES,
                 ARG_KEYMAP,
@@ -728,44 +1039,61 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_MACHINE_ID,
                 ARG_ROOT_PASSWORD,
                 ARG_ROOT_PASSWORD_FILE,
+                ARG_ROOT_PASSWORD_HASHED,
+                ARG_ROOT_SHELL,
+                ARG_KERNEL_COMMAND_LINE,
                 ARG_PROMPT,
                 ARG_PROMPT_LOCALE,
                 ARG_PROMPT_KEYMAP,
                 ARG_PROMPT_TIMEZONE,
                 ARG_PROMPT_HOSTNAME,
                 ARG_PROMPT_ROOT_PASSWORD,
+                ARG_PROMPT_ROOT_SHELL,
                 ARG_COPY,
                 ARG_COPY_LOCALE,
                 ARG_COPY_KEYMAP,
                 ARG_COPY_TIMEZONE,
                 ARG_COPY_ROOT_PASSWORD,
+                ARG_COPY_ROOT_SHELL,
                 ARG_SETUP_MACHINE_ID,
+                ARG_FORCE,
+                ARG_DELETE_ROOT_PASSWORD,
+                ARG_WELCOME,
         };
 
         static const struct option options[] = {
-                { "help",                 no_argument,       NULL, 'h'                      },
-                { "version",              no_argument,       NULL, ARG_VERSION              },
-                { "root",                 required_argument, NULL, ARG_ROOT                 },
-                { "locale",               required_argument, NULL, ARG_LOCALE               },
-                { "locale-messages",      required_argument, NULL, ARG_LOCALE_MESSAGES      },
-                { "keymap",               required_argument, NULL, ARG_KEYMAP               },
-                { "timezone",             required_argument, NULL, ARG_TIMEZONE             },
-                { "hostname",             required_argument, NULL, ARG_HOSTNAME             },
-                { "machine-id",           required_argument, NULL, ARG_MACHINE_ID           },
-                { "root-password",        required_argument, NULL, ARG_ROOT_PASSWORD        },
-                { "root-password-file",   required_argument, NULL, ARG_ROOT_PASSWORD_FILE   },
-                { "prompt",               no_argument,       NULL, ARG_PROMPT               },
-                { "prompt-locale",        no_argument,       NULL, ARG_PROMPT_LOCALE        },
-                { "prompt-keymap",        no_argument,       NULL, ARG_PROMPT_KEYMAP        },
-                { "prompt-timezone",      no_argument,       NULL, ARG_PROMPT_TIMEZONE      },
-                { "prompt-hostname",      no_argument,       NULL, ARG_PROMPT_HOSTNAME      },
-                { "prompt-root-password", no_argument,       NULL, ARG_PROMPT_ROOT_PASSWORD },
-                { "copy",                 no_argument,       NULL, ARG_COPY                 },
-                { "copy-locale",          no_argument,       NULL, ARG_COPY_LOCALE          },
-                { "copy-keymap",          no_argument,       NULL, ARG_COPY_KEYMAP          },
-                { "copy-timezone",        no_argument,       NULL, ARG_COPY_TIMEZONE        },
-                { "copy-root-password",   no_argument,       NULL, ARG_COPY_ROOT_PASSWORD   },
-                { "setup-machine-id",     no_argument,       NULL, ARG_SETUP_MACHINE_ID     },
+                { "help",                    no_argument,       NULL, 'h'                         },
+                { "version",                 no_argument,       NULL, ARG_VERSION                 },
+                { "root",                    required_argument, NULL, ARG_ROOT                    },
+                { "image",                   required_argument, NULL, ARG_IMAGE                   },
+                { "locale",                  required_argument, NULL, ARG_LOCALE                  },
+                { "locale-messages",         required_argument, NULL, ARG_LOCALE_MESSAGES         },
+                { "keymap",                  required_argument, NULL, ARG_KEYMAP                  },
+                { "timezone",                required_argument, NULL, ARG_TIMEZONE                },
+                { "hostname",                required_argument, NULL, ARG_HOSTNAME                },
+                { "machine-id",              required_argument, NULL, ARG_MACHINE_ID              },
+                { "root-password",           required_argument, NULL, ARG_ROOT_PASSWORD           },
+                { "root-password-file",      required_argument, NULL, ARG_ROOT_PASSWORD_FILE      },
+                { "root-password-hashed",    required_argument, NULL, ARG_ROOT_PASSWORD_HASHED    },
+                { "root-shell",              required_argument, NULL, ARG_ROOT_SHELL              },
+                { "kernel-command-line",     required_argument, NULL, ARG_KERNEL_COMMAND_LINE     },
+                { "prompt",                  no_argument,       NULL, ARG_PROMPT                  },
+                { "prompt-locale",           no_argument,       NULL, ARG_PROMPT_LOCALE           },
+                { "prompt-keymap",           no_argument,       NULL, ARG_PROMPT_KEYMAP           },
+                { "prompt-timezone",         no_argument,       NULL, ARG_PROMPT_TIMEZONE         },
+                { "prompt-hostname",         no_argument,       NULL, ARG_PROMPT_HOSTNAME         },
+                { "prompt-root-password",    no_argument,       NULL, ARG_PROMPT_ROOT_PASSWORD    },
+                { "prompt-root-shell",       no_argument,       NULL, ARG_PROMPT_ROOT_SHELL       },
+                { "copy",                    no_argument,       NULL, ARG_COPY                    },
+                { "copy-locale",             no_argument,       NULL, ARG_COPY_LOCALE             },
+                { "copy-keymap",             no_argument,       NULL, ARG_COPY_KEYMAP             },
+                { "copy-timezone",           no_argument,       NULL, ARG_COPY_TIMEZONE           },
+                { "copy-root-password",      no_argument,       NULL, ARG_COPY_ROOT_PASSWORD      },
+                { "copy-root-shell",         no_argument,       NULL, ARG_COPY_ROOT_SHELL         },
+                { "setup-machine-id",        no_argument,       NULL, ARG_SETUP_MACHINE_ID        },
+                { "force",                   no_argument,       NULL, ARG_FORCE                   },
+                { "delete-root-password",    no_argument,       NULL, ARG_DELETE_ROOT_PASSWORD    },
+                { "welcome",                 required_argument, NULL, ARG_WELCOME                 },
                 {}
         };
 
@@ -790,11 +1118,13 @@ static int parse_argv(int argc, char *argv[]) {
                                 return r;
                         break;
 
-                case ARG_LOCALE:
-                        if (!locale_is_valid(optarg))
-                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                                       "Locale %s is not valid.", optarg);
+                case ARG_IMAGE:
+                        r = parse_path_argument_and_warn(optarg, false, &arg_image);
+                        if (r < 0)
+                                return r;
+                        break;
 
+                case ARG_LOCALE:
                         r = free_and_strdup(&arg_locale, optarg);
                         if (r < 0)
                                 return log_oom();
@@ -802,10 +1132,6 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_LOCALE_MESSAGES:
-                        if (!locale_is_valid(optarg))
-                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                                       "Locale %s is not valid.", optarg);
-
                         r = free_and_strdup(&arg_locale_messages, optarg);
                         if (r < 0)
                                 return log_oom();
@@ -838,6 +1164,8 @@ static int parse_argv(int argc, char *argv[]) {
                         r = free_and_strdup(&arg_root_password, optarg);
                         if (r < 0)
                                 return log_oom();
+
+                        arg_root_password_is_hashed = false;
                         break;
 
                 case ARG_ROOT_PASSWORD_FILE:
@@ -847,6 +1175,26 @@ static int parse_argv(int argc, char *argv[]) {
                         if (r < 0)
                                 return log_error_errno(r, "Failed to read %s: %m", optarg);
 
+                        arg_root_password_is_hashed = false;
+                        break;
+
+                case ARG_ROOT_PASSWORD_HASHED:
+                        r = free_and_strdup(&arg_root_password, optarg);
+                        if (r < 0)
+                                return log_oom();
+
+                        arg_root_password_is_hashed = true;
+                        break;
+
+                case ARG_ROOT_SHELL:
+                        r = find_shell(optarg, arg_root);
+                        if (r < 0)
+                                return r;
+
+                        r = free_and_strdup(&arg_root_shell, optarg);
+                        if (r < 0)
+                                return log_oom();
+
                         break;
 
                 case ARG_HOSTNAME:
@@ -868,8 +1216,16 @@ static int parse_argv(int argc, char *argv[]) {
 
                         break;
 
+                case ARG_KERNEL_COMMAND_LINE:
+                        r = free_and_strdup(&arg_kernel_cmdline, optarg);
+                        if (r < 0)
+                                return log_oom();
+
+                        break;
+
                 case ARG_PROMPT:
-                        arg_prompt_locale = arg_prompt_keymap = arg_prompt_timezone = arg_prompt_hostname = arg_prompt_root_password = true;
+                        arg_prompt_locale = arg_prompt_keymap = arg_prompt_timezone = arg_prompt_hostname =
+                                arg_prompt_root_password = arg_prompt_root_shell = true;
                         break;
 
                 case ARG_PROMPT_LOCALE:
@@ -892,8 +1248,13 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_prompt_root_password = true;
                         break;
 
+                case ARG_PROMPT_ROOT_SHELL:
+                        arg_prompt_root_shell = true;
+                        break;
+
                 case ARG_COPY:
-                        arg_copy_locale = arg_copy_keymap = arg_copy_timezone = arg_copy_root_password = true;
+                        arg_copy_locale = arg_copy_keymap = arg_copy_timezone = arg_copy_root_password =
+                                arg_copy_root_shell = true;
                         break;
 
                 case ARG_COPY_LOCALE:
@@ -912,14 +1273,33 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_copy_root_password = true;
                         break;
 
-                case ARG_SETUP_MACHINE_ID:
+                case ARG_COPY_ROOT_SHELL:
+                        arg_copy_root_shell = true;
+                        break;
 
+                case ARG_SETUP_MACHINE_ID:
                         r = sd_id128_randomize(&arg_machine_id);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to generate randomized machine ID: %m");
 
                         break;
 
+                case ARG_FORCE:
+                        arg_force = true;
+                        break;
+
+                case ARG_DELETE_ROOT_PASSWORD:
+                        arg_delete_root_password = true;
+                        break;
+
+                case ARG_WELCOME:
+                        r = parse_boolean(optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse --welcome= argument: %s", optarg);
+
+                        arg_welcome = r;
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -927,11 +1307,28 @@ static int parse_argv(int argc, char *argv[]) {
                         assert_not_reached("Unhandled option");
                 }
 
+        /* We check if the specified locale strings are valid down here, so that we can take --root= into
+         * account when looking for the locale files. */
+
+        if (arg_locale && !locale_is_ok(arg_locale))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Locale %s is not installed.", arg_locale);
+        if (arg_locale_messages && !locale_is_ok(arg_locale_messages))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Locale %s is not installed.", arg_locale_messages);
+
+        if (arg_delete_root_password && (arg_copy_root_password || arg_root_password || arg_prompt_root_password))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "--delete-root-password cannot be combined with other root password options");
+
+        if (arg_image && arg_root)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Please specify either --root= or --image=, the combination of both is not supported.");
+
         return 1;
 }
 
 static int run(int argc, char *argv[]) {
-        bool enabled;
+        _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
+        _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
+        _cleanup_(umount_and_rmdir_and_freep) char *unlink_dir = NULL;
         int r;
 
         r = parse_argv(argc, argv);
@@ -942,11 +1339,23 @@ static int run(int argc, char *argv[]) {
 
         umask(0022);
 
-        r = proc_cmdline_get_bool("systemd.firstboot", &enabled);
+        if (!arg_root && !arg_image) {
+                bool enabled;
+
+                /* If we are called without --root=/--image= let's honour the systemd.firstboot kernel
+                 * command line option, because we are called to provision the host with basic settings (as
+                 * opposed to some other file system tree/image) */
+
+                r = proc_cmdline_get_bool("systemd.firstboot", &enabled);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to parse systemd.firstboot= kernel command line argument, ignoring: %m");
+                if (r > 0 && !enabled)
+                        return 0; /* disabled */
+        }
+
+        r = setup_image(&unlink_dir, &loop_device, &decrypted_image);
         if (r < 0)
-                return log_error_errno(r, "Failed to parse systemd.firstboot= kernel command line argument, ignoring: %m");
-        if (r > 0 && !enabled)
-                return 0; /* disabled */
+                return r;
 
         r = process_locale();
         if (r < 0)
@@ -968,7 +1377,11 @@ static int run(int argc, char *argv[]) {
         if (r < 0)
                 return r;
 
-        r = process_root_password();
+        r = process_root_args();
+        if (r < 0)
+                return r;
+
+        r = process_kernel_cmdline();
         if (r < 0)
                 return r;
 
index 7cdfbc0a186874f60fd27374baf646e36b26386b..91581aed3057afaa72d7fa2082275cbed724ceb2 100644 (file)
@@ -35,11 +35,13 @@ typedef enum MountpointFlags {
         AUTOMOUNT = 1 << 2,
         MAKEFS    = 1 << 3,
         GROWFS    = 1 << 4,
+        RWONLY    = 1 << 5,
 } MountpointFlags;
 
 static const char *arg_dest = NULL;
 static const char *arg_dest_late = NULL;
 static bool arg_fstab_enabled = true;
+static bool arg_swap_enabled = true;
 static char *arg_root_what = NULL;
 static char *arg_root_fstype = NULL;
 static char *arg_root_options = NULL;
@@ -98,6 +100,11 @@ static int add_swap(
         assert(what);
         assert(me);
 
+        if (!arg_swap_enabled) {
+                log_info("Swap unit generation disabled on kernel command line, ignoring fstab swap entry for %s.", what);
+                return 0;
+        }
+
         if (access("/proc/swaps", F_OK) < 0) {
                 log_info("Swap not supported, ignoring fstab swap entry for %s.", what);
                 return 0;
@@ -256,10 +263,10 @@ static int write_dependency(
                 res = strv_join(units, " ");
                 if (!res)
                         return log_oom();
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+
+                DISABLE_WARNING_FORMAT_NONLITERAL;
                 fprintf(f, format, res);
-#pragma GCC diagnostic pop
+                REENABLE_WARNING;
         }
 
         return 0;
@@ -307,6 +314,29 @@ static int write_requires_mounts_for(FILE *f, const char *opts) {
         return 0;
 }
 
+static int write_extra_dependencies(FILE *f, const char *opts) {
+        int r;
+
+        assert(f);
+
+        if (opts) {
+                r = write_after(f, opts);
+                if (r < 0)
+                        return r;
+                r = write_requires_after(f, opts);
+                if (r < 0)
+                        return r;
+                r = write_before(f, opts);
+                if (r < 0)
+                        return r;
+                r = write_requires_mounts_for(f, opts);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
 static int add_mount(
                 const char *dest,
                 const char *what,
@@ -399,20 +429,9 @@ static int add_mount(
                 SET_FLAG(flags, NOFAIL, true);
         }
 
-        if (opts) {
-                 r = write_after(f, opts);
-                 if (r < 0)
-                         return r;
-                 r = write_requires_after(f, opts);
-                 if (r < 0)
-                         return r;
-                 r = write_before(f, opts);
-                 if (r < 0)
-                         return r;
-                 r = write_requires_mounts_for(f, opts);
-                 if (r < 0)
-                         return r;
-        }
+        r = write_extra_dependencies(f, opts);
+        if (r < 0)
+                return r;
 
         if (passno != 0) {
                 r = generator_write_fsck_deps(f, dest, what, where, fstype);
@@ -466,6 +485,9 @@ static int add_mount(
         if (r < 0)
                 return r;
 
+        if (flags & RWONLY)
+                fprintf(f, "ReadWriteOnly=yes\n");
+
         r = fflush_and_check(f);
         if (r < 0)
                 return log_error_errno(r, "Failed to write unit file %s: %m", name);
@@ -562,7 +584,7 @@ static int parse_fstab(bool initrd) {
 
         while ((me = getmntent(f))) {
                 _cleanup_free_ char *where = NULL, *what = NULL, *canonical_where = NULL;
-                bool makefs, growfs, noauto, nofail;
+                bool makefs, growfs, noauto, nofail, rwonly;
                 int k;
 
                 if (initrd && !mount_in_initrd(me))
@@ -602,6 +624,7 @@ static int parse_fstab(bool initrd) {
 
                 makefs = fstab_test_option(me->mnt_opts, "x-systemd.makefs\0");
                 growfs = fstab_test_option(me->mnt_opts, "x-systemd.growfs\0");
+                rwonly = fstab_test_option(me->mnt_opts, "x-systemd.rw-only\0");
                 noauto = fstab_test_yes_no_option(me->mnt_opts, "noauto\0" "auto\0");
                 nofail = fstab_test_yes_no_option(me->mnt_opts, "nofail\0" "fail\0");
 
@@ -634,7 +657,7 @@ static int parse_fstab(bool initrd) {
                                       me->mnt_type,
                                       me->mnt_opts,
                                       me->mnt_passno,
-                                      makefs*MAKEFS | growfs*GROWFS | noauto*NOAUTO | nofail*NOFAIL | automount*AUTOMOUNT,
+                                      makefs*MAKEFS | growfs*GROWFS | noauto*NOAUTO | nofail*NOFAIL | automount*AUTOMOUNT | rwonly*RWONLY,
                                       post,
                                       fstab);
                 }
@@ -777,7 +800,7 @@ static int add_volatile_var(void) {
                          "/var",
                          NULL,
                          "tmpfs",
-                         "mode=0755",
+                         "mode=0755" TMPFS_LIMITS_VAR,
                          0,
                          0,
                          SPECIAL_LOCAL_FS_TARGET,
@@ -870,6 +893,14 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
                                 arg_volatile_mode = m;
                 } else
                         arg_volatile_mode = VOLATILE_YES;
+
+        } else if (streq(key, "systemd.swap")) {
+
+                r = value ? parse_boolean(value) : 1;
+                if (r < 0)
+                        log_warning("Failed to parse systemd.swap switch %s. Ignoring.", value);
+                else
+                        arg_swap_enabled = r;
         }
 
         return 0;
index 3194f7aa8833c5bed11dd817aa2b940c06269934..7530d80befe171ea10cabdfdb68c6d7d1bc8873d 100644 (file)
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
-#include <errno.h>
-#include <sched.h>
-#include <sys/mount.h>
-#include <unistd.h>
+#include <stdio.h>
 
 #include "fd-util.h"
 #include "fs-util.h"
 #include "fuzz.h"
-#include "log.h"
-#include "mkdir.h"
-#include "rm-rf.h"
-#include "string-util.h"
 #include "tests.h"
+#include "tmpfile-util.h"
 #include "udev-rules.h"
 
-static struct fakefs {
-        const char *target;
-        bool ignore_mount_error;
-        bool is_mounted;
-} fakefss[] = {
-        { "/sys",                    false, false },
-        { "/dev",                    false, false },
-        { "/run",                    false, false },
-        { "/etc",                    false, false },
-        { UDEVLIBEXECDIR "/rules.d", true, false },
-};
-
-static int setup_mount_namespace(void) {
-        static thread_local bool is_namespaced = false;
-
-        if (is_namespaced)
-                return 1;
-
-        if (unshare(CLONE_NEWNS) < 0)
-                return log_error_errno(errno, "Failed to call unshare(): %m");
-
-        if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0)
-                return log_error_errno(errno, "Failed to mount / as private: %m");
-
-        is_namespaced = true;
-
-        return 1;
-}
-
-static int setup_fake_filesystems(const char *runtime_dir) {
-        for (unsigned i = 0; i < ELEMENTSOF(fakefss); i++) {
-                if (mount(runtime_dir, fakefss[i].target, NULL, MS_BIND, NULL) < 0) {
-                        log_full_errno(fakefss[i].ignore_mount_error ? LOG_DEBUG : LOG_ERR, errno, "Failed to mount %s: %m", fakefss[i].target);
-                        if (!fakefss[i].ignore_mount_error)
-                                return -errno;
-                } else
-                        fakefss[i].is_mounted = true;
-        }
-
-        return 0;
-}
-
-static int cleanup_fake_filesystems(const char *runtime_dir) {
-        for (unsigned i = 0; i < ELEMENTSOF(fakefss); i++) {
-                if (!fakefss[i].is_mounted)
-                        continue;
-
-                if (umount(fakefss[i].target) < 0) {
-                        log_full_errno(fakefss[i].ignore_mount_error ? LOG_DEBUG : LOG_ERR, errno, "Failed to umount %s: %m", fakefss[i].target);
-                        if (!fakefss[i].ignore_mount_error)
-                                return -errno;
-                } else
-                        fakefss[i].is_mounted = false;
-        }
-        return 0;
-}
-
 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
         _cleanup_(udev_rules_freep) UdevRules *rules = NULL;
-        _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL;
-        FILE *f = NULL;
-
-        (void) setup_mount_namespace();
-
-        assert_se(runtime_dir = setup_fake_runtime_dir());
-
-        if (setup_fake_filesystems(runtime_dir) < 0) {
-#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
-                return EXIT_TEST_SKIP;
-#endif
-        }
+        _cleanup_fclose_ FILE *f = NULL;
+        _cleanup_(unlink_tempfilep) char filename[] = "/tmp/fuzz-udev-rules.XXXXXX";
+        int r;
 
         if (!getenv("SYSTEMD_LOG_LEVEL")) {
                 log_set_max_level_realm(LOG_REALM_UDEV, LOG_CRIT);
                 log_set_max_level_realm(LOG_REALM_SYSTEMD, LOG_CRIT);
         }
 
-        assert_se(mkdir_p("/etc/udev/rules.d", 0755) >= 0);
-        f = fopen("/etc/udev/rules.d/fuzz.rules", "we");
-        assert_se(f);
+        assert_se(fmkostemp_safe(filename, "r+", &f) == 0);
         if (size != 0)
                 assert_se(fwrite(data, size, 1, f) == 1);
-        assert_se(fclose(f) == 0);
+        fflush(f);
 
-        assert_se(udev_rules_new(&rules, RESOLVE_NAME_EARLY) == 0);
+        assert_se(rules = udev_rules_new(RESOLVE_NAME_EARLY));
+        r = udev_rules_parse_file(rules, filename);
+        log_info_errno(r, "Parsing %s: %m", filename);
+        assert_se(IN_SET(r,
+                         0,       /* OK */
+                         -ENOBUFS /* line length exceeded */));
 
-        assert_se(cleanup_fake_filesystems(runtime_dir) >= 0);
         return 0;
 }
index d3993cf123f67c93f1a9f041e525a943c1c68295..c0661433a31588a843ae437bb9fa2f9ce4520e88 100644 (file)
@@ -70,10 +70,13 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
         name = strjoina("a.", unit_type_to_string(t));
         assert_se(unit_new_for_name(m, unit_vtable[t]->object_size, name, &u) >= 0);
 
-        (void) config_parse(name, name, f,
-                            UNIT_VTABLE(u)->sections,
-                            config_item_perf_lookup, load_fragment_gperf_lookup,
-                            CONFIG_PARSE_ALLOW_INCLUDE, u);
+        (void) config_parse(
+                        name, name, f,
+                        UNIT_VTABLE(u)->sections,
+                        config_item_perf_lookup, load_fragment_gperf_lookup,
+                        0,
+                        u,
+                        NULL);
 
         g = open_memstream_unlocked(&out, &out_size);
         assert_se(g);
diff --git a/src/fuzz/fuzz-xdg-desktop.c b/src/fuzz/fuzz-xdg-desktop.c
new file mode 100644 (file)
index 0000000..f8a1b5b
--- /dev/null
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fs-util.h"
+#include "rm-rf.h"
+#include "string-util.h"
+#include "strv.h"
+#include "tests.h"
+#include "tmpfile-util.h"
+#include "fuzz.h"
+#include "xdg-autostart-service.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+        _cleanup_(unlink_tempfilep) char name[] = "/tmp/fuzz-xdg-desktop.XXXXXX";
+        _cleanup_close_ int fd = -1;
+        _cleanup_(xdg_autostart_service_freep) XdgAutostartService *service = NULL;
+        _cleanup_(rm_rf_physical_and_freep) char *tmpdir = NULL;
+
+        /* We don't want to fill the logs with messages about parse errors.
+         * Disable most logging if not running standalone */
+        if (!getenv("SYSTEMD_LOG_LEVEL"))
+                log_set_max_level(LOG_CRIT);
+
+        assert_se(mkdtemp_malloc("/tmp/fuzz-xdg-desktop-XXXXXX", &tmpdir) >= 0);
+
+        fd = mkostemp_safe(name);
+        assert_se(fd >= 0);
+        assert_se(write(fd, data, size) == (ssize_t) size);
+
+        assert_se(service = xdg_autostart_service_parse_desktop(name));
+        assert_se(service->name = strdup("fuzz-xdg-desktop.service"));
+        (void) xdg_autostart_service_generate_unit(service, tmpdir);
+
+        return 0;
+}
index 83b1ac11ad182a00845b2b1afa2db568a4f94ee1..1e56526259b20ff3fa48c019406c5e0352b0555f 100644 (file)
@@ -6,6 +6,3 @@
 
 /* The entry point into the fuzzer */
 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
-
-/* https://docs.fuzzbuzz.io/developer-documentation/porting-targets-to-fuzzbuzz/libfuzzer-targets */
-int FuzzerEntrypoint(const uint8_t *data, size_t size);
diff --git a/src/fuzz/fuzzer-entry-point.c b/src/fuzz/fuzzer-entry-point.c
deleted file mode 100644 (file)
index 020c111..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-#include "fuzz.h"
-
-int FuzzerEntrypoint(const uint8_t *data, size_t size) {
-        return LLVMFuzzerTestOneInput(data, size);
-}
index c88812d1de9f7d83a26f7e9c52d19ac875179007..01f119fcd0b374586ba98979b3a87aec77065c77 100644 (file)
@@ -146,4 +146,10 @@ fuzzers += [
         [['src/fuzz/fuzz-time-util.c'],
          [libshared],
          []],
+
+        [['src/fuzz/fuzz-xdg-desktop.c',
+          'src/xdg-autostart-generator/xdg-autostart-service.h',
+          'src/xdg-autostart-generator/xdg-autostart-service.c'],
+         [],
+         []],
 ]
index 2067eeaf89afb403e7f4a3e7d50485da3eb15a39..a9478b9dbd886d2ce3dd96ccf76775e0747d9df3 100644 (file)
@@ -665,7 +665,7 @@ static int enumerate_partitions(dev_t devnum) {
         if (r <= 0)
                 return r;
 
-        r = dissect_image(fd, NULL, 0, DISSECT_IMAGE_GPT_ONLY|DISSECT_IMAGE_NO_UDEV, &m);
+        r = dissect_image(fd, NULL, 0, NULL, DISSECT_IMAGE_GPT_ONLY|DISSECT_IMAGE_NO_UDEV, &m);
         if (r == -ENOPKG) {
                 log_debug_errno(r, "No suitable partition table found, ignoring.");
                 return 0;
index 69ab645484d8377ed0629015d73fc5249c6f9c32..3fd57639f84192d0a329ed8a8e5554cf57d44d0a 100644 (file)
@@ -64,6 +64,12 @@ int suitable_image_path(const char *path) {
                 path_is_absolute(path);
 }
 
+bool supported_fstype(const char *fstype) {
+        /* Limit the set of supported file systems a bit, as protection against little tested kernel file
+         * systems. Also, we only support the resize ioctls for these file systems. */
+        return STR_IN_SET(fstype, "ext4", "btrfs", "xfs");
+}
+
 int split_user_name_realm(const char *t, char **ret_user_name, char **ret_realm) {
         _cleanup_free_ char *user_name = NULL, *realm = NULL;
         const char *c;
@@ -124,6 +130,8 @@ int bus_message_append_secret(sd_bus_message *m, UserRecord *secret) {
         if (r < 0)
                 return r;
 
+        (void) sd_bus_message_sensitive(m);
+
         return sd_bus_message_append(m, "s", formatted);
 }
 
index df20c0af71e57d6a3494442df1917b220aadb88e..6161d4c3d06da2f6ade7533f483dd1d4c40d0b54 100644 (file)
@@ -12,12 +12,14 @@ bool suitable_user_name(const char *name);
 int suitable_realm(const char *realm);
 int suitable_image_path(const char *path);
 
+bool supported_fstype(const char *fstype);
+
 int split_user_name_realm(const char *t, char **ret_user_name, char **ret_realm);
 
 int bus_message_append_secret(sd_bus_message *m, UserRecord *secret);
 
 /* Many of our operations might be slow due to crypto, fsck, recursive chown() and so on. For these
- * operations permit a *very* long time-out */
+ * operations permit a *very* long timeout */
 #define HOME_SLOW_BUS_CALL_TIMEOUT_USEC (2*USEC_PER_MINUTE)
 
 int test_password_one(const char *hashed_password, const char *password);
diff --git a/src/home/homectl-fido2.c b/src/home/homectl-fido2.c
new file mode 100644 (file)
index 0000000..b7b2c1a
--- /dev/null
@@ -0,0 +1,539 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#if HAVE_LIBFIDO2
+#include <fido.h>
+#endif
+
+#include "ask-password-api.h"
+#include "errno-util.h"
+#include "format-table.h"
+#include "hexdecoct.h"
+#include "homectl-fido2.h"
+#include "homectl-pkcs11.h"
+#include "libcrypt-util.h"
+#include "locale-util.h"
+#include "memory-util.h"
+#include "random-util.h"
+#include "strv.h"
+
+#if HAVE_LIBFIDO2
+static int add_fido2_credential_id(
+                JsonVariant **v,
+                const void *cid,
+                size_t cid_size) {
+
+        _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
+        _cleanup_strv_free_ char **l = NULL;
+        _cleanup_free_ char *escaped = NULL;
+        int r;
+
+        assert(v);
+        assert(cid);
+
+        r = base64mem(cid, cid_size, &escaped);
+        if (r < 0)
+                return log_error_errno(r, "Failed to base64 encode FIDO2 credential ID: %m");
+
+        w = json_variant_ref(json_variant_by_key(*v, "fido2HmacCredential"));
+        if (w) {
+                r = json_variant_strv(w, &l);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to parse FIDO2 credential ID list: %m");
+
+                if (strv_contains(l, escaped))
+                        return 0;
+        }
+
+        r = strv_extend(&l, escaped);
+        if (r < 0)
+                return log_oom();
+
+        w = json_variant_unref(w);
+        r = json_variant_new_array_strv(&w, l);
+        if (r < 0)
+                return log_error_errno(r, "Failed to create FIDO2 credential ID JSON: %m");
+
+        r = json_variant_set_field(v, "fido2HmacCredential", w);
+        if (r < 0)
+                return log_error_errno(r, "Failed to update FIDO2 credential ID: %m");
+
+        return 0;
+}
+
+static int add_fido2_salt(
+                JsonVariant **v,
+                const void *cid,
+                size_t cid_size,
+                const void *fido2_salt,
+                size_t fido2_salt_size,
+                const void *secret,
+                size_t secret_size) {
+
+        _cleanup_(json_variant_unrefp) JsonVariant *l = NULL, *w = NULL, *e = NULL;
+        _cleanup_(erase_and_freep) char *base64_encoded = NULL;
+        _cleanup_free_ char *unix_salt = NULL;
+        struct crypt_data cd = {};
+        char *k;
+        int r;
+
+        r = make_salt(&unix_salt);
+        if (r < 0)
+                return log_error_errno(r, "Failed to generate salt: %m");
+
+        /* Before using UNIX hashing on the supplied key we base64 encode it, since crypt_r() and friends
+         * expect a NUL terminated string, and we use a binary key */
+        r = base64mem(secret, secret_size, &base64_encoded);
+        if (r < 0)
+                return log_error_errno(r, "Failed to base64 encode secret key: %m");
+
+        errno = 0;
+        k = crypt_r(base64_encoded, unix_salt, &cd);
+        if (!k)
+                return log_error_errno(errno_or_else(EINVAL), "Failed to UNIX hash secret key: %m");
+
+        r = json_build(&e, JSON_BUILD_OBJECT(
+                                       JSON_BUILD_PAIR("credential", JSON_BUILD_BASE64(cid, cid_size)),
+                                       JSON_BUILD_PAIR("salt", JSON_BUILD_BASE64(fido2_salt, fido2_salt_size)),
+                                       JSON_BUILD_PAIR("hashedPassword", JSON_BUILD_STRING(k))));
+        if (r < 0)
+                return log_error_errno(r, "Failed to build FIDO2 salt JSON key object: %m");
+
+        w = json_variant_ref(json_variant_by_key(*v, "privileged"));
+        l = json_variant_ref(json_variant_by_key(w, "fido2HmacSalt"));
+
+        r = json_variant_append_array(&l, e);
+        if (r < 0)
+                return log_error_errno(r, "Failed append FIDO2 salt: %m");
+
+        r = json_variant_set_field(&w, "fido2HmacSalt", l);
+        if (r < 0)
+                return log_error_errno(r, "Failed to set FDO2 salt: %m");
+
+        r = json_variant_set_field(v, "privileged", w);
+        if (r < 0)
+                return log_error_errno(r, "Failed to update privileged field: %m");
+
+        return 0;
+}
+#endif
+
+#define FIDO2_SALT_SIZE 32
+
+int identity_add_fido2_parameters(
+                JsonVariant **v,
+                const char *device) {
+
+#if HAVE_LIBFIDO2
+        _cleanup_(fido_cbor_info_free) fido_cbor_info_t *di = NULL;
+        _cleanup_(fido_assert_free) fido_assert_t *a = NULL;
+        _cleanup_(erase_and_freep) char *used_pin = NULL;
+        _cleanup_(fido_cred_free) fido_cred_t *c = NULL;
+        _cleanup_(fido_dev_free) fido_dev_t *d = NULL;
+        _cleanup_(erase_and_freep) void *salt = NULL;
+        JsonVariant *un, *realm, *rn;
+        bool found_extension = false;
+        const void *cid, *secret;
+        const char *fido_un;
+        size_t n, cid_size, secret_size;
+        char **e;
+        int r;
+
+        /* Construction is like this: we generate a salt of 32 bytes. We then ask the FIDO2 device to
+         * HMAC-SHA256 it for us with its internal key. The result is the key used by LUKS and account
+         * authentication. LUKS and UNIX password auth all do their own salting before hashing, so that FIDO2
+         * device never sees the volume key.
+         *
+         * S = HMAC-SHA256(I, D)
+         *
+         * with: S → LUKS/account authentication key                                         (never stored)
+         *       I → internal key on FIDO2 device                              (stored in the FIDO2 device)
+         *       D → salt we generate here               (stored in the privileged part of the JSON record)
+         *
+         */
+
+        assert(v);
+        assert(device);
+
+        salt = malloc(FIDO2_SALT_SIZE);
+        if (!salt)
+                return log_oom();
+
+        r = genuine_random_bytes(salt, FIDO2_SALT_SIZE, RANDOM_BLOCK);
+        if (r < 0)
+                return log_error_errno(r, "Failed to generate salt: %m");
+
+        d = fido_dev_new();
+        if (!d)
+                return log_oom();
+
+        r = fido_dev_open(d, device);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to open FIDO2 device %s: %s", device, fido_strerr(r));
+
+        if (!fido_dev_is_fido2(d))
+                return log_error_errno(SYNTHETIC_ERRNO(ENODEV),
+                                       "Specified device %s is not a FIDO2 device.", device);
+
+        di = fido_cbor_info_new();
+        if (!di)
+                return log_oom();
+
+        r = fido_dev_get_cbor_info(d, di);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to get CBOR device info for %s: %s", device, fido_strerr(r));
+
+        e = fido_cbor_info_extensions_ptr(di);
+        n = fido_cbor_info_extensions_len(di);
+
+        for (size_t i = 0; i < n; i++)
+                if (streq(e[i], "hmac-secret")) {
+                        found_extension = true;
+                        break;
+                }
+
+        if (!found_extension)
+                return log_error_errno(SYNTHETIC_ERRNO(ENODEV),
+                                       "Specified device %s is a FIDO2 device, but does not support the required HMAC-SECRET extension.", device);
+
+        c = fido_cred_new();
+        if (!c)
+                return log_oom();
+
+        r = fido_cred_set_extensions(c, FIDO_EXT_HMAC_SECRET);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to enable HMAC-SECRET extension on FIDO2 credential: %s", fido_strerr(r));
+
+        r = fido_cred_set_rp(c, "io.systemd.home", "Home Directory");
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to set FIDO2 credential relying party ID/name: %s", fido_strerr(r));
+
+        r = fido_cred_set_type(c, COSE_ES256);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to set FIDO2 credential type to ES256: %s", fido_strerr(r));
+
+        un = json_variant_by_key(*v, "userName");
+        if (!un)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "userName field of user record is missing");
+        if (!json_variant_is_string(un))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "userName field of user record is not a string");
+
+        realm = json_variant_by_key(*v, "realm");
+        if (realm) {
+                if (!json_variant_is_string(realm))
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                               "realm field of user record is not a string");
+
+                fido_un = strjoina(json_variant_string(un), json_variant_string(realm));
+        } else
+                fido_un = json_variant_string(un);
+
+        rn = json_variant_by_key(*v, "realName");
+        if (rn && !json_variant_is_string(rn))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "realName field of user record is not a string");
+
+        r = fido_cred_set_user(c,
+                               (const unsigned char*) fido_un, strlen(fido_un), /* We pass the user ID and name as the same */
+                               fido_un,
+                               rn ? json_variant_string(rn) : NULL,
+                               NULL /* icon URL */);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to set FIDO2 credential user data: %s", fido_strerr(r));
+
+        r = fido_cred_set_clientdata_hash(c, (const unsigned char[32]) {}, 32);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to set FIDO2 client data hash: %s", fido_strerr(r));
+
+        r = fido_cred_set_rk(c, FIDO_OPT_FALSE);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to turn off FIDO2 resident key option of credential: %s", fido_strerr(r));
+
+        r = fido_cred_set_uv(c, FIDO_OPT_FALSE);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to turn off FIDO2 user verification option of credential: %s", fido_strerr(r));
+
+        log_info("Initializing FIDO2 credential on security token.");
+
+        log_notice("%s%s(Hint: This might require verification of user presence on security token.)",
+                   emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
+                   emoji_enabled() ? " " : "");
+
+        r = fido_dev_make_cred(d, c, NULL);
+        if (r == FIDO_ERR_PIN_REQUIRED) {
+                _cleanup_free_ char *text = NULL;
+
+                if (asprintf(&text, "Please enter security token PIN:") < 0)
+                        return log_oom();
+
+                for (;;) {
+                        _cleanup_(strv_free_erasep) char **pin = NULL;
+                        char **i;
+
+                        r = ask_password_auto(text, "user-home", NULL, "fido2-pin", USEC_INFINITY, 0, &pin);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to acquire user PIN: %m");
+
+                        r = FIDO_ERR_PIN_INVALID;
+                        STRV_FOREACH(i, pin) {
+                                if (isempty(*i)) {
+                                        log_info("PIN may not be empty.");
+                                        continue;
+                                }
+
+                                r = fido_dev_make_cred(d, c, *i);
+                                if (r == FIDO_OK) {
+                                        used_pin = strdup(*i);
+                                        if (!used_pin)
+                                                return log_oom();
+                                        break;
+                                }
+                                if (r != FIDO_ERR_PIN_INVALID)
+                                        break;
+                        }
+
+                        if (r != FIDO_ERR_PIN_INVALID)
+                                break;
+
+                        log_notice("PIN incorrect, please try again.");
+                }
+        }
+        if (r == FIDO_ERR_PIN_AUTH_BLOCKED)
+                return log_notice_errno(SYNTHETIC_ERRNO(EPERM),
+                                        "Token PIN is currently blocked, please remove and reinsert token.");
+        if (r == FIDO_ERR_ACTION_TIMEOUT)
+                return log_error_errno(SYNTHETIC_ERRNO(ENOSTR),
+                                       "Token action timeout. (User didn't interact with token quickly enough.)");
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to generate FIDO2 credential: %s", fido_strerr(r));
+
+        cid = fido_cred_id_ptr(c);
+        if (!cid)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to get FIDO2 credential ID.");
+
+        cid_size = fido_cred_id_len(c);
+
+        a = fido_assert_new();
+        if (!a)
+                return log_oom();
+
+        r = fido_assert_set_extensions(a, FIDO_EXT_HMAC_SECRET);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to enable HMAC-SECRET extension on FIDO2 assertion: %s", fido_strerr(r));
+
+        r = fido_assert_set_hmac_salt(a, salt, FIDO2_SALT_SIZE);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to set salt on FIDO2 assertion: %s", fido_strerr(r));
+
+        r = fido_assert_set_rp(a, "io.systemd.home");
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to set FIDO2 assertion ID: %s", fido_strerr(r));
+
+        r = fido_assert_set_clientdata_hash(a, (const unsigned char[32]) {}, 32);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to set FIDO2 assertion client data hash: %s", fido_strerr(r));
+
+        r = fido_assert_allow_cred(a, cid, cid_size);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to add FIDO2 assertion credential ID: %s", fido_strerr(r));
+
+        r = fido_assert_set_up(a, FIDO_OPT_FALSE);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to turn off FIDO2 assertion user presence: %s", fido_strerr(r));
+
+        log_info("Generating secret key on FIDO2 security token.");
+
+        r = fido_dev_get_assert(d, a, used_pin);
+        if (r == FIDO_ERR_UP_REQUIRED) {
+                r = fido_assert_set_up(a, FIDO_OPT_TRUE);
+                if (r != FIDO_OK)
+                        return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                               "Failed to turn on FIDO2 assertion user presence: %s", fido_strerr(r));
+
+                log_notice("%s%sIn order to allow secret key generation, please verify presence on security token.",
+                           emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
+                           emoji_enabled() ? " " : "");
+
+                r = fido_dev_get_assert(d, a, used_pin);
+        }
+        if (r == FIDO_ERR_ACTION_TIMEOUT)
+                return log_error_errno(SYNTHETIC_ERRNO(ENOSTR),
+                                       "Token action timeout. (User didn't interact with token quickly enough.)");
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to ask token for assertion: %s", fido_strerr(r));
+
+        secret = fido_assert_hmac_secret_ptr(a, 0);
+        if (!secret)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve HMAC secret.");
+
+        secret_size = fido_assert_hmac_secret_len(a, 0);
+
+        r = add_fido2_credential_id(v, cid, cid_size);
+        if (r < 0)
+                return r;
+
+        r = add_fido2_salt(v,
+                           cid,
+                           cid_size,
+                           salt,
+                           FIDO2_SALT_SIZE,
+                           secret,
+                           secret_size);
+        if (r < 0)
+                return r;
+
+        /* If we acquired the PIN also include it in the secret section of the record, so that systemd-homed
+         * can use it if it needs to, given that it likely needs to decrypt the key again to pass to LUKS or
+         * fscrypt. */
+        r = identity_add_token_pin(v, used_pin);
+        if (r < 0)
+                return r;
+
+        return 0;
+#else
+        return log_error_errno(EOPNOTSUPP, "FIDO2 tokens not supported on this build.");
+#endif
+}
+
+int list_fido2_devices(void) {
+#if HAVE_LIBFIDO2
+        _cleanup_(table_unrefp) Table *t = NULL;
+        size_t allocated = 64, found = 0;
+        fido_dev_info_t *di = NULL;
+        int r;
+
+        di = fido_dev_info_new(allocated);
+        if (!di)
+                return log_oom();
+
+        r = fido_dev_info_manifest(di, allocated, &found);
+        if (r == FIDO_ERR_INTERNAL || (r == FIDO_OK && found == 0)) {
+                /* The library returns FIDO_ERR_INTERNAL when no devices are found. I wish it wouldn't. */
+                log_info("No FIDO2 devices found.");
+                r = 0;
+                goto finish;
+        }
+        if (r != FIDO_OK) {
+                r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to enumerate FIDO2 devices: %s", fido_strerr(r));
+                goto finish;
+        }
+
+        t = table_new("path", "manufacturer", "product");
+        if (!t) {
+                r = log_oom();
+                goto finish;
+        }
+
+        for (size_t i = 0; i < found; i++) {
+                const fido_dev_info_t *entry;
+
+                entry = fido_dev_info_ptr(di, i);
+                if (!entry) {
+                        r = log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                            "Failed to get device information for FIDO device %zu.", i);
+                        goto finish;
+                }
+
+                r = table_add_many(
+                                t,
+                                TABLE_PATH, fido_dev_info_path(entry),
+                                TABLE_STRING, fido_dev_info_manufacturer_string(entry),
+                                TABLE_STRING, fido_dev_info_product_string(entry));
+                if (r < 0) {
+                        table_log_add_error(r);
+                        goto finish;
+                }
+        }
+
+        r = table_print(t, stdout);
+        if (r < 0) {
+                log_error_errno(r, "Failed to show device table: %m");
+                goto finish;
+        }
+
+        r = 0;
+
+finish:
+        fido_dev_info_free(&di, allocated);
+        return r;
+#else
+        return log_error_errno(EOPNOTSUPP, "FIDO2 tokens not supported on this build.");
+#endif
+}
+
+int find_fido2_auto(char **ret) {
+#if HAVE_LIBFIDO2
+        _cleanup_free_ char *copy = NULL;
+        size_t di_size = 64, found = 0;
+        const fido_dev_info_t *entry;
+        fido_dev_info_t *di = NULL;
+        const char *path;
+        int r;
+
+        di = fido_dev_info_new(di_size);
+        if (!di)
+                return log_oom();
+
+        r = fido_dev_info_manifest(di, di_size, &found);
+        if (r == FIDO_ERR_INTERNAL || (r == FIDO_OK && found == 0)) {
+                /* The library returns FIDO_ERR_INTERNAL when no devices are found. I wish it wouldn't. */
+                r = log_error_errno(SYNTHETIC_ERRNO(ENODEV), "No FIDO2 devices found.");
+                goto finish;
+        }
+        if (r != FIDO_OK) {
+                r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to enumerate FIDO2 devices: %s", fido_strerr(r));
+                goto finish;
+        }
+        if (found > 1) {
+                r = log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ), "More than one FIDO2 device found.");
+                goto finish;
+        }
+
+        entry = fido_dev_info_ptr(di, 0);
+        if (!entry) {
+                r = log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                    "Failed to get device information for FIDO device 0.");
+                goto finish;
+        }
+
+        path = fido_dev_info_path(entry);
+        if (!path) {
+                r = log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                    "Failed to query FIDO device path.");
+                goto finish;
+        }
+
+        copy = strdup(path);
+        if (!copy) {
+                r = log_oom();
+                goto finish;
+        }
+
+        *ret = TAKE_PTR(copy);
+        r = 0;
+
+finish:
+        fido_dev_info_free(&di, di_size);
+        return r;
+#else
+        return log_error_errno(EOPNOTSUPP, "FIDO2 tokens not supported on this build.");
+#endif
+}
diff --git a/src/home/homectl-fido2.h b/src/home/homectl-fido2.h
new file mode 100644 (file)
index 0000000..0d9faef
--- /dev/null
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "json.h"
+
+int identity_add_fido2_parameters(JsonVariant **v, const char *device);
+
+int list_fido2_devices(void);
+
+int find_fido2_auto(char **ret);
diff --git a/src/home/homectl-pkcs11.c b/src/home/homectl-pkcs11.c
new file mode 100644 (file)
index 0000000..f4253ed
--- /dev/null
@@ -0,0 +1,480 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "errno-util.h"
+#include "format-table.h"
+#include "hexdecoct.h"
+#include "homectl-pkcs11.h"
+#include "libcrypt-util.h"
+#include "memory-util.h"
+#include "openssl-util.h"
+#include "pkcs11-util.h"
+#include "random-util.h"
+#include "strv.h"
+
+struct pkcs11_callback_data {
+        char *pin_used;
+        X509 *cert;
+};
+
+#if HAVE_P11KIT
+static void pkcs11_callback_data_release(struct pkcs11_callback_data *data) {
+        erase_and_free(data->pin_used);
+        X509_free(data->cert);
+}
+
+static int pkcs11_callback(
+                CK_FUNCTION_LIST *m,
+                CK_SESSION_HANDLE session,
+                CK_SLOT_ID slot_id,
+                const CK_SLOT_INFO *slot_info,
+                const CK_TOKEN_INFO *token_info,
+                P11KitUri *uri,
+                void *userdata) {
+
+        _cleanup_(erase_and_freep) char *pin_used = NULL;
+        struct pkcs11_callback_data *data = userdata;
+        CK_OBJECT_HANDLE object;
+        int r;
+
+        assert(m);
+        assert(slot_info);
+        assert(token_info);
+        assert(uri);
+        assert(data);
+
+        /* Called for every token matching our URI */
+
+        r = pkcs11_token_login(m, session, slot_id, token_info, "home directory operation", "user-home", "pkcs11-pin", UINT64_MAX, &pin_used);
+        if (r < 0)
+                return r;
+
+        r = pkcs11_token_find_x509_certificate(m, session, uri, &object);
+        if (r < 0)
+                return r;
+
+        r = pkcs11_token_read_x509_certificate(m, session, object, &data->cert);
+        if (r < 0)
+                return r;
+
+        /* Let's read some random data off the token and write it to the kernel pool before we generate our
+         * random key from it. This way we can claim the quality of the RNG is at least as good as the
+         * kernel's and the token's pool */
+        (void) pkcs11_token_acquire_rng(m, session);
+
+        data->pin_used = TAKE_PTR(pin_used);
+        return 1;
+}
+#endif
+
+static int acquire_pkcs11_certificate(
+                const char *uri,
+                X509 **ret_cert,
+                char **ret_pin_used) {
+
+#if HAVE_P11KIT
+        _cleanup_(pkcs11_callback_data_release) struct pkcs11_callback_data data = {};
+        int r;
+
+        r = pkcs11_find_token(uri, pkcs11_callback, &data);
+        if (r == -EAGAIN) /* pkcs11_find_token() doesn't log about this error, but all others */
+                return log_error_errno(ENXIO, "Specified PKCS#11 token with URI '%s' not found.", uri);
+        if (r < 0)
+                return r;
+
+        *ret_cert = TAKE_PTR(data.cert);
+        *ret_pin_used = TAKE_PTR(data.pin_used);
+
+        return 0;
+#else
+        return log_error_errno(EOPNOTSUPP, "PKCS#11 tokens not supported on this build.");
+#endif
+}
+
+static int encrypt_bytes(
+                EVP_PKEY *pkey,
+                const void *decrypted_key,
+                size_t decrypted_key_size,
+                void **ret_encrypt_key,
+                size_t *ret_encrypt_key_size) {
+
+        _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = NULL;
+        _cleanup_free_ void *b = NULL;
+        size_t l;
+
+        ctx = EVP_PKEY_CTX_new(pkey, NULL);
+        if (!ctx)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to allocate public key context");
+
+        if (EVP_PKEY_encrypt_init(ctx) <= 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to initialize public key context");
+
+        if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to configure PKCS#1 padding");
+
+        if (EVP_PKEY_encrypt(ctx, NULL, &l, decrypted_key, decrypted_key_size) <= 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to determine encrypted key size");
+
+        b = malloc(l);
+        if (!b)
+                return log_oom();
+
+        if (EVP_PKEY_encrypt(ctx, b, &l, decrypted_key, decrypted_key_size) <= 0)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to determine encrypted key size");
+
+        *ret_encrypt_key = TAKE_PTR(b);
+        *ret_encrypt_key_size = l;
+
+        return 0;
+}
+
+static int add_pkcs11_encrypted_key(
+                JsonVariant **v,
+                const char *uri,
+                const void *encrypted_key, size_t encrypted_key_size,
+                const void *decrypted_key, size_t decrypted_key_size) {
+
+        _cleanup_(json_variant_unrefp) JsonVariant *l = NULL, *w = NULL, *e = NULL;
+        _cleanup_(erase_and_freep) char *base64_encoded = NULL;
+        _cleanup_free_ char *salt = NULL;
+        struct crypt_data cd = {};
+        char *k;
+        int r;
+
+        assert(v);
+        assert(uri);
+        assert(encrypted_key);
+        assert(encrypted_key_size > 0);
+        assert(decrypted_key);
+        assert(decrypted_key_size > 0);
+
+        r = make_salt(&salt);
+        if (r < 0)
+                return log_error_errno(r, "Failed to generate salt: %m");
+
+        /* Before using UNIX hashing on the supplied key we base64 encode it, since crypt_r() and friends
+         * expect a NUL terminated string, and we use a binary key */
+        r = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
+        if (r < 0)
+                return log_error_errno(r, "Failed to base64 encode secret key: %m");
+
+        errno = 0;
+        k = crypt_r(base64_encoded, salt, &cd);
+        if (!k)
+                return log_error_errno(errno_or_else(EINVAL), "Failed to UNIX hash secret key: %m");
+
+        r = json_build(&e, JSON_BUILD_OBJECT(
+                                       JSON_BUILD_PAIR("uri", JSON_BUILD_STRING(uri)),
+                                       JSON_BUILD_PAIR("data", JSON_BUILD_BASE64(encrypted_key, encrypted_key_size)),
+                                       JSON_BUILD_PAIR("hashedPassword", JSON_BUILD_STRING(k))));
+        if (r < 0)
+                return log_error_errno(r, "Failed to build encrypted JSON key object: %m");
+
+        w = json_variant_ref(json_variant_by_key(*v, "privileged"));
+        l = json_variant_ref(json_variant_by_key(w, "pkcs11EncryptedKey"));
+
+        r = json_variant_append_array(&l, e);
+        if (r < 0)
+                return log_error_errno(r, "Failed append PKCS#11 encrypted key: %m");
+
+        r = json_variant_set_field(&w, "pkcs11EncryptedKey", l);
+        if (r < 0)
+                return log_error_errno(r, "Failed to set PKCS#11 encrypted key: %m");
+
+        r = json_variant_set_field(v, "privileged", w);
+        if (r < 0)
+                return log_error_errno(r, "Failed to update privileged field: %m");
+
+        return 0;
+}
+
+static int add_pkcs11_token_uri(JsonVariant **v, const char *uri) {
+        _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
+        _cleanup_strv_free_ char **l = NULL;
+        int r;
+
+        assert(v);
+        assert(uri);
+
+        w = json_variant_ref(json_variant_by_key(*v, "pkcs11TokenUri"));
+        if (w) {
+                r = json_variant_strv(w, &l);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to parse PKCS#11 token list: %m");
+
+                if (strv_contains(l, uri))
+                        return 0;
+        }
+
+        r = strv_extend(&l, uri);
+        if (r < 0)
+                return log_oom();
+
+        w = json_variant_unref(w);
+        r = json_variant_new_array_strv(&w, l);
+        if (r < 0)
+                return log_error_errno(r, "Failed to create PKCS#11 token URI JSON: %m");
+
+        r = json_variant_set_field(v, "pkcs11TokenUri", w);
+        if (r < 0)
+                return log_error_errno(r, "Failed to update PKCS#11 token URI list: %m");
+
+        return 0;
+}
+
+int identity_add_token_pin(JsonVariant **v, const char *pin) {
+        _cleanup_(json_variant_unrefp) JsonVariant *w = NULL, *l = NULL;
+        _cleanup_(strv_free_erasep) char **pins = NULL;
+        int r;
+
+        assert(v);
+
+        if (isempty(pin))
+                return 0;
+
+        w = json_variant_ref(json_variant_by_key(*v, "secret"));
+        l = json_variant_ref(json_variant_by_key(w, "tokenPin"));
+
+        r = json_variant_strv(l, &pins);
+        if (r < 0)
+                return log_error_errno(r, "Failed to convert PIN array: %m");
+
+        if (strv_find(pins, pin))
+                return 0;
+
+        r = strv_extend(&pins, pin);
+        if (r < 0)
+                return log_oom();
+
+        strv_uniq(pins);
+
+        l = json_variant_unref(l);
+
+        r = json_variant_new_array_strv(&l, pins);
+        if (r < 0)
+                return log_error_errno(r, "Failed to allocate new PIN array JSON: %m");
+
+        json_variant_sensitive(l);
+
+        r = json_variant_set_field(&w, "tokenPin", l);
+        if (r < 0)
+                return log_error_errno(r, "Failed to update PIN field: %m");
+
+        r = json_variant_set_field(v, "secret", w);
+        if (r < 0)
+                return log_error_errno(r, "Failed to update secret object: %m");
+
+        return 1;
+}
+
+int identity_add_pkcs11_key_data(JsonVariant **v, const char *uri) {
+        _cleanup_(erase_and_freep) void *decrypted_key = NULL, *encrypted_key = NULL;
+        _cleanup_(erase_and_freep) char *pin = NULL;
+        size_t decrypted_key_size, encrypted_key_size;
+        _cleanup_(X509_freep) X509 *cert = NULL;
+        EVP_PKEY *pkey;
+        RSA *rsa;
+        int bits;
+        int r;
+
+        assert(v);
+
+        r = acquire_pkcs11_certificate(uri, &cert, &pin);
+        if (r < 0)
+                return r;
+
+        pkey = X509_get0_pubkey(cert);
+        if (!pkey)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to extract public key from X.509 certificate.");
+
+        if (EVP_PKEY_base_id(pkey) != EVP_PKEY_RSA)
+                return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "X.509 certificate does not refer to RSA key.");
+
+        rsa = EVP_PKEY_get0_RSA(pkey);
+        if (!rsa)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to acquire RSA public key from X.509 certificate.");
+
+        bits = RSA_bits(rsa);
+        log_debug("Bits in RSA key: %i", bits);
+
+        /* We use PKCS#1 padding for the RSA cleartext, hence let's leave some extra space for it, hence only
+         * generate a random key half the size of the RSA length */
+        decrypted_key_size = bits / 8 / 2;
+
+        if (decrypted_key_size < 1)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Uh, RSA key size too short?");
+
+        log_debug("Generating %zu bytes random key.", decrypted_key_size);
+
+        decrypted_key = malloc(decrypted_key_size);
+        if (!decrypted_key)
+                return log_oom();
+
+        r = genuine_random_bytes(decrypted_key, decrypted_key_size, RANDOM_BLOCK);
+        if (r < 0)
+                return log_error_errno(r, "Failed to generate random key: %m");
+
+        r = encrypt_bytes(pkey, decrypted_key, decrypted_key_size, &encrypted_key, &encrypted_key_size);
+        if (r < 0)
+                return log_error_errno(r, "Failed to encrypt key: %m");
+
+        /* Add the token URI to the public part of the record. */
+        r = add_pkcs11_token_uri(v, uri);
+        if (r < 0)
+                return r;
+
+        /* Include the encrypted version of the random key we just generated in the privileged part of the record */
+        r = add_pkcs11_encrypted_key(
+                        v,
+                        uri,
+                        encrypted_key, encrypted_key_size,
+                        decrypted_key, decrypted_key_size);
+        if (r < 0)
+                return r;
+
+        /* If we acquired the PIN also include it in the secret section of the record, so that systemd-homed
+         * can use it if it needs to, given that it likely needs to decrypt the key again to pass to LUKS or
+         * fscrypt. */
+        r = identity_add_token_pin(v, pin);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+#if HAVE_P11KIT
+static int list_callback(
+                CK_FUNCTION_LIST *m,
+                CK_SESSION_HANDLE session,
+                CK_SLOT_ID slot_id,
+                const CK_SLOT_INFO *slot_info,
+                const CK_TOKEN_INFO *token_info,
+                P11KitUri *uri,
+                void *userdata) {
+
+        _cleanup_free_ char *token_uri_string = NULL, *token_label = NULL, *token_manufacturer_id = NULL, *token_model = NULL;
+        _cleanup_(p11_kit_uri_freep) P11KitUri *token_uri = NULL;
+        Table *t = userdata;
+        int uri_result, r;
+
+        assert(slot_info);
+        assert(token_info);
+
+        /* We only care about hardware devices here with a token inserted. Let's filter everything else
+         * out. (Note that the user can explicitly specify non-hardware tokens if they like, but during
+         * enumeration we'll filter those, since software tokens are typically the system certificate store
+         * and such, and it's typically not what people want to bind their home directories to.) */
+        if (!FLAGS_SET(token_info->flags, CKF_HW_SLOT|CKF_TOKEN_PRESENT))
+                return -EAGAIN;
+
+        token_label = pkcs11_token_label(token_info);
+        if (!token_label)
+                return log_oom();
+
+        token_manufacturer_id = pkcs11_token_manufacturer_id(token_info);
+        if (!token_manufacturer_id)
+                return log_oom();
+
+        token_model = pkcs11_token_model(token_info);
+        if (!token_model)
+                return log_oom();
+
+        token_uri = uri_from_token_info(token_info);
+        if (!token_uri)
+                return log_oom();
+
+        uri_result = p11_kit_uri_format(token_uri, P11_KIT_URI_FOR_ANY, &token_uri_string);
+        if (uri_result != P11_KIT_URI_OK)
+                return log_warning_errno(SYNTHETIC_ERRNO(EAGAIN), "Failed to format slot URI: %s", p11_kit_uri_message(uri_result));
+
+        r = table_add_many(
+                        t,
+                        TABLE_STRING, token_uri_string,
+                        TABLE_STRING, token_label,
+                        TABLE_STRING, token_manufacturer_id,
+                        TABLE_STRING, token_model);
+        if (r < 0)
+                return table_log_add_error(r);
+
+        return -EAGAIN; /* keep scanning */
+}
+#endif
+
+int list_pkcs11_tokens(void) {
+#if HAVE_P11KIT
+        _cleanup_(table_unrefp) Table *t = NULL;
+        int r;
+
+        t = table_new("uri", "label", "manufacturer", "model");
+        if (!t)
+                return log_oom();
+
+        r = pkcs11_find_token(NULL, list_callback, t);
+        if (r < 0 && r != -EAGAIN)
+                return r;
+
+        if (table_get_rows(t) <= 1) {
+                log_info("No suitable PKCS#11 tokens found.");
+                return 0;
+        }
+
+        r = table_print(t, stdout);
+        if (r < 0)
+                return log_error_errno(r, "Failed to show device table: %m");
+
+        return 0;
+#else
+        return log_error_errno(EOPNOTSUPP, "PKCS#11 tokens not supported on this build.");
+#endif
+}
+
+#if HAVE_P11KIT
+static int auto_callback(
+                CK_FUNCTION_LIST *m,
+                CK_SESSION_HANDLE session,
+                CK_SLOT_ID slot_id,
+                const CK_SLOT_INFO *slot_info,
+                const CK_TOKEN_INFO *token_info,
+                P11KitUri *uri,
+                void *userdata) {
+
+        _cleanup_(p11_kit_uri_freep) P11KitUri *token_uri = NULL;
+        char **t = userdata;
+        int uri_result;
+
+        assert(slot_info);
+        assert(token_info);
+
+        if (!FLAGS_SET(token_info->flags, CKF_HW_SLOT|CKF_TOKEN_PRESENT))
+                return -EAGAIN;
+
+        if (*t)
+                return log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ),
+                                       "More than one suitable PKCS#11 token found.");
+
+        token_uri = uri_from_token_info(token_info);
+        if (!token_uri)
+                return log_oom();
+
+        uri_result = p11_kit_uri_format(token_uri, P11_KIT_URI_FOR_ANY, t);
+        if (uri_result != P11_KIT_URI_OK)
+                return log_warning_errno(SYNTHETIC_ERRNO(EAGAIN), "Failed to format slot URI: %s", p11_kit_uri_message(uri_result));
+
+        return 0;
+}
+#endif
+
+int find_pkcs11_token_auto(char **ret) {
+#if HAVE_P11KIT
+        int r;
+
+        r = pkcs11_find_token(NULL, auto_callback, ret);
+        if (r == -EAGAIN)
+                return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "No suitable PKCS#11 tokens found.");
+        if (r < 0)
+                return r;
+
+        return 0;
+#else
+        return log_error_errno(EOPNOTSUPP, "PKCS#11 tokens not supported on this build.");
+#endif
+}
diff --git a/src/home/homectl-pkcs11.h b/src/home/homectl-pkcs11.h
new file mode 100644 (file)
index 0000000..0403c73
--- /dev/null
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "json.h"
+
+int identity_add_token_pin(JsonVariant **v, const char *pin);
+
+int identity_add_pkcs11_key_data(JsonVariant **v, const char *token_uri);
+
+int list_pkcs11_tokens(void);
+int find_pkcs11_token_auto(char **ret);
index 8a346d4627f858df286aa4f130a70077503b9fa9..33e262706d29350f6bd63cbaf732595108e3fe04 100644 (file)
@@ -4,26 +4,22 @@
 
 #include "sd-bus.h"
 
-#include "alloc-util.h"
 #include "ask-password-api.h"
 #include "bus-common-errors.h"
 #include "bus-error.h"
-#include "bus-util.h"
+#include "bus-locator.h"
 #include "cgroup-util.h"
 #include "dns-domain.h"
 #include "env-util.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "format-table.h"
-#include "format-util.h"
-#include "fs-util.h"
-#include "hexdecoct.h"
 #include "home-util.h"
-#include "libcrypt-util.h"
+#include "homectl-fido2.h"
+#include "homectl-pkcs11.h"
 #include "locale-util.h"
 #include "main-func.h"
 #include "memory-util.h"
-#include "openssl-util.h"
 #include "pager.h"
 #include "parse-util.h"
 #include "path-util.h"
@@ -31,7 +27,6 @@
 #include "pretty-print.h"
 #include "process-util.h"
 #include "pwquality-util.h"
-#include "random-util.h"
 #include "rlimit-util.h"
 #include "spawn-polkit-agent.h"
 #include "terminal-util.h"
@@ -56,6 +51,7 @@ static char **arg_identity_filter_rlimits = NULL;
 static uint64_t arg_disk_size = UINT64_MAX;
 static uint64_t arg_disk_size_relative = UINT64_MAX;
 static char **arg_pkcs11_token_uri = NULL;
+static char **arg_fido2_device = NULL;
 static bool arg_json = false;
 static JsonFormatFlags arg_json_format_flags = 0;
 static bool arg_and_resize = false;
@@ -73,6 +69,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_rlimits, json_variant_unrefp);
 STATIC_DESTRUCTOR_REGISTER(arg_identity_filter, strv_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_identity_filter_rlimits, strv_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_pkcs11_token_uri, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_fido2_device, strv_freep);
 
 static bool identity_properties_specified(void) {
         return
@@ -83,7 +80,8 @@ static bool identity_properties_specified(void) {
                 !json_variant_is_blank_object(arg_identity_extra_rlimits) ||
                 !strv_isempty(arg_identity_filter) ||
                 !strv_isempty(arg_identity_filter_rlimits) ||
-                !strv_isempty(arg_pkcs11_token_uri);
+                !strv_isempty(arg_pkcs11_token_uri) ||
+                !strv_isempty(arg_fido2_device);
 }
 
 static int acquire_bus(sd_bus **bus) {
@@ -116,15 +114,7 @@ static int list_homes(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return r;
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.home1",
-                        "/org/freedesktop/home1",
-                        "org.freedesktop.home1.Manager",
-                        "ListHomes",
-                        &error,
-                        &reply,
-                        NULL);
+        r = bus_call_method(bus, bus_home_mgr, "ListHomes", &error, &reply, NULL);
         if (r < 0)
                 return log_error_errno(r, "Failed to list homes: %s", bus_error_message(&error, r));
 
@@ -152,12 +142,12 @@ static int list_homes(int argc, char *argv[], void *userdata) {
                                    TABLE_UID, uid,
                                    TABLE_GID, gid);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to add row to table: %m");
+                        return table_log_add_error(r);
 
 
                 r = table_add_cell(table, &cell, TABLE_STRING, state);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to add field to table: %m");
+                        return table_log_add_error(r);
 
                 color = user_record_state_color(state);
                 if (color)
@@ -168,7 +158,7 @@ static int list_homes(int argc, char *argv[], void *userdata) {
                                    TABLE_STRING, home,
                                    TABLE_STRING, strna(empty_to_null(shell)));
                 if (r < 0)
-                        return log_error_errno(r, "Failed to add row to table: %m");
+                        return table_log_add_error(r);
         }
 
         r = sd_bus_message_exit_container(reply);
@@ -178,7 +168,7 @@ static int list_homes(int argc, char *argv[], void *userdata) {
         if (table_get_rows(table) > 1 || arg_json) {
                 r = table_set_sort(table, (size_t) 0, (size_t) -1);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to sort table: %m");
+                        return table_log_sort_error(r);
 
                 table_set_header(table, arg_legend);
 
@@ -187,7 +177,7 @@ static int list_homes(int argc, char *argv[], void *userdata) {
                 else
                         r = table_print(table, NULL);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to show table: %m");
+                        return table_log_print_error(r);
         }
 
         if (arg_legend && !arg_json) {
@@ -244,7 +234,7 @@ static int acquire_existing_password(const char *user_name, UserRecord *hr, bool
         return 0;
 }
 
-static int acquire_pkcs11_pin(const char *user_name, UserRecord *hr) {
+static int acquire_token_pin(const char *user_name, UserRecord *hr) {
         _cleanup_(strv_free_erasep) char **pin = NULL;
         _cleanup_free_ char *question = NULL;
         char *e;
@@ -255,9 +245,9 @@ static int acquire_pkcs11_pin(const char *user_name, UserRecord *hr) {
 
         e = getenv("PIN");
         if (e) {
-                r = user_record_set_pkcs11_pin(hr, STRV_MAKE(e), false);
+                r = user_record_set_token_pin(hr, STRV_MAKE(e), false);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to store PKCS#11 PIN: %m");
+                        return log_error_errno(r, "Failed to store token PIN: %m");
 
                 string_erase(e);
 
@@ -271,11 +261,11 @@ static int acquire_pkcs11_pin(const char *user_name, UserRecord *hr) {
                 return log_oom();
 
         /* We never cache or use cached PINs, since usually there are only very few attempts allowed before the PIN is blocked */
-        r = ask_password_auto(question, "user-home", NULL, "pkcs11-pin", USEC_INFINITY, 0, &pin);
+        r = ask_password_auto(question, "user-home", NULL, "token-pin", USEC_INFINITY, 0, &pin);
         if (r < 0)
                 return log_error_errno(r, "Failed to acquire security token PIN: %m");
 
-        r = user_record_set_pkcs11_pin(hr, pin, false);
+        r = user_record_set_token_pin(hr, pin, false);
         if (r < 0)
                 return log_error_errno(r, "Failed to store security token PIN: %m");
 
@@ -323,26 +313,38 @@ static int handle_generic_user_record_error(
 
         } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PIN_NEEDED)) {
 
-                r = acquire_pkcs11_pin(user_name, hr);
+                r = acquire_token_pin(user_name, hr);
                 if (r < 0)
                         return r;
 
         } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED)) {
 
-                log_notice("Please authenticate physically on security token.");
+                log_notice("%s%sPlease authenticate physically on security token.",
+                           emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
+                           emoji_enabled() ? " " : "");
 
                 r = user_record_set_pkcs11_protected_authentication_path_permitted(hr, true);
                 if (r < 0)
                         return log_error_errno(r, "Failed to set PKCS#11 protected authentication path permitted flag: %m");
 
+        } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED)) {
+
+                log_notice("%s%sAuthentication requires presence verification on security token.",
+                           emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
+                           emoji_enabled() ? " " : "");
+
+                r = user_record_set_fido2_user_presence_permitted(hr, true);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to set FIDO2 user presence permitted flag: %m");
+
         } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PIN_LOCKED))
-                return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Security token PIN is locked, please unlock security token PIN first.");
+                return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Security token PIN is locked, please unlock it first. (Hint: Removal and re-insertion might suffice.)");
 
         else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_BAD_PIN)) {
 
                 log_notice("Security token PIN incorrect, please try again.");
 
-                r = acquire_pkcs11_pin(user_name, hr);
+                r = acquire_token_pin(user_name, hr);
                 if (r < 0)
                         return r;
 
@@ -350,7 +352,7 @@ static int handle_generic_user_record_error(
 
                 log_notice("Security token PIN incorrect, please try again (only a few tries left!).");
 
-                r = acquire_pkcs11_pin(user_name, hr);
+                r = acquire_token_pin(user_name, hr);
                 if (r < 0)
                         return r;
 
@@ -358,7 +360,7 @@ static int handle_generic_user_record_error(
 
                 log_notice("Security token PIN incorrect, please try again (only one try left!).");
 
-                r = acquire_pkcs11_pin(user_name, hr);
+                r = acquire_token_pin(user_name, hr);
                 if (r < 0)
                         return r;
         } else
@@ -387,13 +389,7 @@ static int activate_home(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 *m = NULL;
 
-                        r = sd_bus_message_new_method_call(
-                                        bus,
-                                        &m,
-                                        "org.freedesktop.home1",
-                                        "/org/freedesktop/home1",
-                                        "org.freedesktop.home1.Manager",
-                                        "ActivateHome");
+                        r = bus_message_new_method_call(bus, &m, bus_home_mgr, "ActivateHome");
                         if (r < 0)
                                 return bus_log_create_error(r);
 
@@ -435,13 +431,7 @@ static int deactivate_home(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 *m = NULL;
 
-                r = sd_bus_message_new_method_call(
-                                bus,
-                                &m,
-                                "org.freedesktop.home1",
-                                "/org/freedesktop/home1",
-                                "org.freedesktop.home1.Manager",
-                                "DeactivateHome");
+                r = bus_message_new_method_call(bus, &m, bus_home_mgr, "DeactivateHome");
                 if (r < 0)
                         return bus_log_create_error(r);
 
@@ -548,28 +538,9 @@ static int inspect_home(int argc, char *argv[], void *userdata) {
                                 continue;
                         }
 
-                        r = sd_bus_call_method(
-                                        bus,
-                                        "org.freedesktop.home1",
-                                        "/org/freedesktop/home1",
-                                        "org.freedesktop.home1.Manager",
-                                        "GetUserRecordByName",
-                                        &error,
-                                        &reply,
-                                        "s",
-                                        *i);
-                } else {
-                        r = sd_bus_call_method(
-                                        bus,
-                                        "org.freedesktop.home1",
-                                        "/org/freedesktop/home1",
-                                        "org.freedesktop.home1.Manager",
-                                        "GetUserRecordByUID",
-                                        &error,
-                                        &reply,
-                                        "u",
-                                        (uint32_t) uid);
-                }
+                        r = bus_call_method(bus, bus_home_mgr, "GetUserRecordByName", &error, &reply, "s", *i);
+                } else
+                        r = bus_call_method(bus, bus_home_mgr, "GetUserRecordByUID", &error, &reply, "u", (uint32_t) uid);
 
                 if (r < 0) {
                         log_error_errno(r, "Failed to inspect home: %s", bus_error_message(&error, r));
@@ -643,13 +614,7 @@ static int authenticate_home(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 *m = NULL;
 
-                        r = sd_bus_message_new_method_call(
-                                        bus,
-                                        &m,
-                                        "org.freedesktop.home1",
-                                        "/org/freedesktop/home1",
-                                        "org.freedesktop.home1.Manager",
-                                        "AuthenticateHome");
+                        r = bus_message_new_method_call(bus, &m, bus_home_mgr, "AuthenticateHome");
                         if (r < 0)
                                 return bus_log_create_error(r);
 
@@ -934,336 +899,6 @@ static int add_disposition(JsonVariant **v) {
         return 1;
 }
 
-struct pkcs11_callback_data {
-        char *pin_used;
-        X509 *cert;
-};
-
-static void pkcs11_callback_data_release(struct pkcs11_callback_data *data) {
-        erase_and_free(data->pin_used);
-        X509_free(data->cert);
-}
-
-#if HAVE_P11KIT
-static int pkcs11_callback(
-                CK_FUNCTION_LIST *m,
-                CK_SESSION_HANDLE session,
-                CK_SLOT_ID slot_id,
-                const CK_SLOT_INFO *slot_info,
-                const CK_TOKEN_INFO *token_info,
-                P11KitUri *uri,
-                void *userdata) {
-
-        _cleanup_(erase_and_freep) char *pin_used = NULL;
-        struct pkcs11_callback_data *data = userdata;
-        CK_OBJECT_HANDLE object;
-        int r;
-
-        assert(m);
-        assert(slot_info);
-        assert(token_info);
-        assert(uri);
-        assert(data);
-
-        /* Called for every token matching our URI */
-
-        r = pkcs11_token_login(m, session, slot_id, token_info, "home directory operation", "user-home", "pkcs11-pin", UINT64_MAX, &pin_used);
-        if (r < 0)
-                return r;
-
-        r = pkcs11_token_find_x509_certificate(m, session, uri, &object);
-        if (r < 0)
-                return r;
-
-        r = pkcs11_token_read_x509_certificate(m, session, object, &data->cert);
-        if (r < 0)
-                return r;
-
-        /* Let's read some random data off the token and write it to the kernel pool before we generate our
-         * random key from it. This way we can claim the quality of the RNG is at least as good as the
-         * kernel's and the token's pool */
-        (void) pkcs11_token_acquire_rng(m, session);
-
-        data->pin_used = TAKE_PTR(pin_used);
-        return 1;
-}
-#endif
-
-static int acquire_pkcs11_certificate(
-                const char *uri,
-                X509 **ret_cert,
-                char **ret_pin_used) {
-
-#if HAVE_P11KIT
-        _cleanup_(pkcs11_callback_data_release) struct pkcs11_callback_data data = {};
-        int r;
-
-        r = pkcs11_find_token(uri, pkcs11_callback, &data);
-        if (r == -EAGAIN) /* pkcs11_find_token() doesn't log about this error, but all others */
-                return log_error_errno(ENXIO, "Specified PKCS#11 token with URI '%s' not found.", uri);
-        if (r < 0)
-                return r;
-
-        *ret_cert = TAKE_PTR(data.cert);
-        *ret_pin_used = TAKE_PTR(data.pin_used);
-
-        return 0;
-#else
-        return log_error_errno(EOPNOTSUPP, "PKCS#11 tokens not supported on this build.");
-#endif
-}
-
-static int encrypt_bytes(
-                EVP_PKEY *pkey,
-                const void *decrypted_key,
-                size_t decrypted_key_size,
-                void **ret_encrypt_key,
-                size_t *ret_encrypt_key_size) {
-
-        _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = NULL;
-        _cleanup_free_ void *b = NULL;
-        size_t l;
-
-        ctx = EVP_PKEY_CTX_new(pkey, NULL);
-        if (!ctx)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to allocate public key context");
-
-        if (EVP_PKEY_encrypt_init(ctx) <= 0)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to initialize public key context");
-
-        if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to configure PKCS#1 padding");
-
-        if (EVP_PKEY_encrypt(ctx, NULL, &l, decrypted_key, decrypted_key_size) <= 0)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to determine encrypted key size");
-
-        b = malloc(l);
-        if (!b)
-                return log_oom();
-
-        if (EVP_PKEY_encrypt(ctx, b, &l, decrypted_key, decrypted_key_size) <= 0)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to determine encrypted key size");
-
-        *ret_encrypt_key = TAKE_PTR(b);
-        *ret_encrypt_key_size = l;
-
-        return 0;
-}
-
-static int add_pkcs11_pin(JsonVariant **v, const char *pin) {
-        _cleanup_(json_variant_unrefp) JsonVariant *w = NULL, *l = NULL;
-        _cleanup_(strv_free_erasep) char **pins = NULL;
-        int r;
-
-        assert(v);
-
-        if (isempty(pin))
-                return 0;
-
-        w = json_variant_ref(json_variant_by_key(*v, "secret"));
-        l = json_variant_ref(json_variant_by_key(w, "pkcs11Pin"));
-
-        r = json_variant_strv(l, &pins);
-        if (r < 0)
-                return log_error_errno(r, "Failed to convert PIN array: %m");
-
-        if (strv_find(pins, pin))
-                return 0;
-
-        r = strv_extend(&pins, pin);
-        if (r < 0)
-                return log_oom();
-
-        strv_uniq(pins);
-
-        l = json_variant_unref(l);
-
-        r = json_variant_new_array_strv(&l, pins);
-        if (r < 0)
-                return log_error_errno(r, "Failed to allocate new PIN array JSON: %m");
-
-        json_variant_sensitive(l);
-
-        r = json_variant_set_field(&w, "pkcs11Pin", l);
-        if (r < 0)
-                return log_error_errno(r, "Failed to update PIN field: %m");
-
-        r = json_variant_set_field(v, "secret", w);
-        if (r < 0)
-                return log_error_errno(r, "Failed to update secret object: %m");
-
-        return 1;
-}
-
-static int add_pkcs11_encrypted_key(
-                JsonVariant **v,
-                const char *uri,
-                const void *encrypted_key, size_t encrypted_key_size,
-                const void *decrypted_key, size_t decrypted_key_size) {
-
-        _cleanup_(json_variant_unrefp) JsonVariant *l = NULL, *w = NULL, *e = NULL;
-        _cleanup_(erase_and_freep) char *base64_encoded = NULL;
-        _cleanup_free_ char *salt = NULL;
-        struct crypt_data cd = {};
-        char *k;
-        int r;
-
-        assert(v);
-        assert(uri);
-        assert(encrypted_key);
-        assert(encrypted_key_size > 0);
-        assert(decrypted_key);
-        assert(decrypted_key_size > 0);
-
-        r = make_salt(&salt);
-        if (r < 0)
-                return log_error_errno(r, "Failed to generate salt: %m");
-
-        /* Before using UNIX hashing on the supplied key we base64 encode it, since crypt_r() and friends
-         * expect a NUL terminated string, and we use a binary key */
-        r = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
-        if (r < 0)
-                return log_error_errno(r, "Failed to base64 encode secret key: %m");
-
-        errno = 0;
-        k = crypt_r(base64_encoded, salt, &cd);
-        if (!k)
-                return log_error_errno(errno_or_else(EINVAL), "Failed to UNIX hash secret key: %m");
-
-        r = json_build(&e, JSON_BUILD_OBJECT(
-                                       JSON_BUILD_PAIR("uri", JSON_BUILD_STRING(uri)),
-                                       JSON_BUILD_PAIR("data", JSON_BUILD_BASE64(encrypted_key, encrypted_key_size)),
-                                       JSON_BUILD_PAIR("hashedPassword", JSON_BUILD_STRING(k))));
-        if (r < 0)
-                return log_error_errno(r, "Failed to build encrypted JSON key object: %m");
-
-        w = json_variant_ref(json_variant_by_key(*v, "privileged"));
-        l = json_variant_ref(json_variant_by_key(w, "pkcs11EncryptedKey"));
-
-        r = json_variant_append_array(&l, e);
-        if (r < 0)
-                return log_error_errno(r, "Failed append PKCS#11 encrypted key: %m");
-
-        r = json_variant_set_field(&w, "pkcs11EncryptedKey", l);
-        if (r < 0)
-                return log_error_errno(r, "Failed to set PKCS#11 encrypted key: %m");
-
-        r = json_variant_set_field(v, "privileged", w);
-        if (r < 0)
-                return log_error_errno(r, "Failed to update privileged field: %m");
-
-        return 0;
-}
-
-static int add_pkcs11_token_uri(JsonVariant **v, const char *uri) {
-        _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
-        _cleanup_strv_free_ char **l = NULL;
-        int r;
-
-        assert(v);
-        assert(uri);
-
-        w = json_variant_ref(json_variant_by_key(*v, "pkcs11TokenUri"));
-        if (w) {
-                r = json_variant_strv(w, &l);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to parse PKCS#11 token list: %m");
-
-                if (strv_contains(l, uri))
-                        return 0;
-        }
-
-        r = strv_extend(&l, uri);
-        if (r < 0)
-                return log_oom();
-
-        w = json_variant_unref(w);
-        r = json_variant_new_array_strv(&w, l);
-        if (r < 0)
-                return log_error_errno(r, "Failed to create PKCS#11 token URI JSON: %m");
-
-        r = json_variant_set_field(v, "pkcs11TokenUri", w);
-        if (r < 0)
-                return log_error_errno(r, "Failed to update PKCS#11 token URI list: %m");
-
-        return 0;
-}
-
-static int add_pkcs11_key_data(JsonVariant **v, const char *uri) {
-        _cleanup_(erase_and_freep) void *decrypted_key = NULL, *encrypted_key = NULL;
-        _cleanup_(erase_and_freep) char *pin = NULL;
-        size_t decrypted_key_size, encrypted_key_size;
-        _cleanup_(X509_freep) X509 *cert = NULL;
-        EVP_PKEY *pkey;
-        RSA *rsa;
-        int bits;
-        int r;
-
-        assert(v);
-
-        r = acquire_pkcs11_certificate(uri, &cert, &pin);
-        if (r < 0)
-                return r;
-
-        pkey = X509_get0_pubkey(cert);
-        if (!pkey)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to extract public key from X.509 certificate.");
-
-        if (EVP_PKEY_base_id(pkey) != EVP_PKEY_RSA)
-                return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "X.509 certificate does not refer to RSA key.");
-
-        rsa = EVP_PKEY_get0_RSA(pkey);
-        if (!rsa)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to acquire RSA public key from X.509 certificate.");
-
-        bits = RSA_bits(rsa);
-        log_debug("Bits in RSA key: %i", bits);
-
-        /* We use PKCS#1 padding for the RSA cleartext, hence let's leave some extra space for it, hence only
-         * generate a random key half the size of the RSA length */
-        decrypted_key_size = bits / 8 / 2;
-
-        if (decrypted_key_size < 1)
-                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Uh, RSA key size too short?");
-
-        log_debug("Generating %zu bytes random key.", decrypted_key_size);
-
-        decrypted_key = malloc(decrypted_key_size);
-        if (!decrypted_key)
-                return log_oom();
-
-        r = genuine_random_bytes(decrypted_key, decrypted_key_size, RANDOM_BLOCK);
-        if (r < 0)
-                return log_error_errno(r, "Failed to generate random key: %m");
-
-        r = encrypt_bytes(pkey, decrypted_key, decrypted_key_size, &encrypted_key, &encrypted_key_size);
-        if (r < 0)
-                return log_error_errno(r, "Failed to encrypt key: %m");
-
-        /* Add the token URI to the public part of the record. */
-        r = add_pkcs11_token_uri(v, uri);
-        if (r < 0)
-                return r;
-
-        /* Include the encrypted version of the random key we just generated in the privileged part of the record */
-        r = add_pkcs11_encrypted_key(
-                        v,
-                        uri,
-                        encrypted_key, encrypted_key_size,
-                        decrypted_key, decrypted_key_size);
-        if (r < 0)
-                return r;
-
-        /* If we acquired the PIN also include it in the secret section of the record, so that systemd-homed
-         * can use it if it needs to, given that it likely needs to decrypt the key again to pass to LUKS or
-         * fscrypt. */
-        r = add_pkcs11_pin(v, pin);
-        if (r < 0)
-                return r;
-
-        return 0;
-}
-
 static int acquire_new_home_record(UserRecord **ret) {
         _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
         _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
@@ -1291,7 +926,13 @@ static int acquire_new_home_record(UserRecord **ret) {
                 return r;
 
         STRV_FOREACH(i, arg_pkcs11_token_uri) {
-                r = add_pkcs11_key_data(&v, *i);
+                r = identity_add_pkcs11_key_data(&v, *i);
+                if (r < 0)
+                        return r;
+        }
+
+        STRV_FOREACH(i, arg_fido2_device) {
+                r = identity_add_fido2_parameters(&v, *i);
                 if (r < 0)
                         return r;
         }
@@ -1425,7 +1066,7 @@ static int create_home(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return r;
 
-        /* Remember the original hashed paswords before we add our own, so that we can return to them later,
+        /* Remember the original hashed passwords before we add our own, so that we can return to them later,
          * should the entered password turn out not to be acceptable. */
         original_hashed_passwords = strv_copy(hr->hashed_password);
         if (!original_hashed_passwords)
@@ -1468,43 +1109,42 @@ static int create_home(int argc, char *argv[], void *userdata) {
 
                 r = json_variant_format(hr->json, 0, &formatted);
                 if (r < 0)
-                        return r;
+                        return log_error_errno(r, "Failed to format user record: %m");
 
-                r = sd_bus_message_new_method_call(
-                                bus,
-                                &m,
-                                "org.freedesktop.home1",
-                                "/org/freedesktop/home1",
-                                "org.freedesktop.home1.Manager",
-                                "CreateHome");
+                r = bus_message_new_method_call(bus, &m, bus_home_mgr, "CreateHome");
                 if (r < 0)
                         return bus_log_create_error(r);
 
+                (void) sd_bus_message_sensitive(m);
+
                 r = sd_bus_message_append(m, "s", formatted);
                 if (r < 0)
                         return bus_log_create_error(r);
 
                 r = sd_bus_call(bus, m, HOME_SLOW_BUS_CALL_TIMEOUT_USEC, &error, NULL);
                 if (r < 0) {
-                        if (!sd_bus_error_has_name(&error, BUS_ERROR_LOW_PASSWORD_QUALITY))
-                                return log_error_errno(r, "Failed to create user home: %s", bus_error_message(&error, r));
-
-                        log_error_errno(r, "%s", bus_error_message(&error, r));
-                        log_info("(Use --enforce-password-policy=no to turn off password quality checks for this account.)");
-                } else
-                        break; /* done */
+                        if (sd_bus_error_has_name(&error, BUS_ERROR_LOW_PASSWORD_QUALITY)) {
+                                log_error_errno(r, "%s", bus_error_message(&error, r));
+                                log_info("(Use --enforce-password-policy=no to turn off password quality checks for this account.)");
 
-                r = user_record_set_hashed_password(hr, original_hashed_passwords);
-                if (r < 0)
-                        return r;
+                                r = user_record_set_hashed_password(hr, original_hashed_passwords);
+                                if (r < 0)
+                                        return r;
 
-                r = acquire_new_password(hr->user_name, hr, /* suggest = */ false);
-                if (r < 0)
-                        return r;
+                                r = acquire_new_password(hr->user_name, hr, /* suggest = */ false);
+                                if (r < 0)
+                                        return r;
 
-                r = user_record_make_hashed_password(hr, hr->password, /* extend = */ true);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to hash passwords: %m");
+                                r = user_record_make_hashed_password(hr, hr->password, /* extend = */ true);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to hash passwords: %m");
+                        } else {
+                                r = handle_generic_user_record_error(hr->user_name, hr, &error, r, false);
+                                if (r < 0)
+                                        return r;
+                        }
+                } else
+                        break; /* done */
         }
 
         return 0;
@@ -1525,13 +1165,7 @@ static int remove_home(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 *m = NULL;
 
-                r = sd_bus_message_new_method_call(
-                                bus,
-                                &m,
-                                "org.freedesktop.home1",
-                                "/org/freedesktop/home1",
-                                "org.freedesktop.home1.Manager",
-                                "RemoveHome");
+                r = bus_message_new_method_call(bus, &m, bus_home_mgr, "RemoveHome");
                 if (r < 0)
                         return bus_log_create_error(r);
 
@@ -1594,16 +1228,7 @@ static int acquire_updated_home_record(
                 if (!identity_properties_specified())
                         return log_error_errno(SYNTHETIC_ERRNO(EALREADY), "No field to change specified.");
 
-                r = sd_bus_call_method(
-                                bus,
-                                "org.freedesktop.home1",
-                                "/org/freedesktop/home1",
-                                "org.freedesktop.home1.Manager",
-                                "GetUserRecordByName",
-                                &error,
-                                &reply,
-                                "s",
-                                username);
+                r = bus_call_method(bus, bus_home_mgr, "GetUserRecordByName", &error, &reply, "s", username);
                 if (r < 0)
                         return log_error_errno(r, "Failed to acquire user home record: %s", bus_error_message(&error, r));
 
@@ -1630,14 +1255,20 @@ static int acquire_updated_home_record(
                 return r;
 
         STRV_FOREACH(i, arg_pkcs11_token_uri) {
-                r = add_pkcs11_key_data(&json, *i);
+                r = identity_add_pkcs11_key_data(&json, *i);
+                if (r < 0)
+                        return r;
+        }
+
+        STRV_FOREACH(i, arg_fido2_device) {
+                r = identity_add_fido2_parameters(&json, *i);
                 if (r < 0)
                         return r;
         }
 
         /* If the user supplied a full record, then add in lastChange, but do not override. Otherwise always
          * override. */
-        r = update_last_change(&json, !!arg_pkcs11_token_uri, !arg_identity);
+        r = update_last_change(&json, arg_pkcs11_token_uri || arg_fido2_device, !arg_identity);
         if (r < 0)
                 return r;
 
@@ -1656,6 +1287,26 @@ static int acquire_updated_home_record(
         return 0;
 }
 
+static int home_record_reset_human_interaction_permission(UserRecord *hr) {
+        int r;
+
+        assert(hr);
+
+        /* When we execute multiple operations one after the other, let's reset the permission to ask the
+         * user each time, so that if interaction is necessary we will be told so again and thus can print a
+         * nice message to the user, telling the user so. */
+
+        r = user_record_set_pkcs11_protected_authentication_path_permitted(hr, -1);
+        if (r < 0)
+                return log_error_errno(r, "Failed to reset PKCS#11 protected authentication path permission flag: %m");
+
+        r = user_record_set_fido2_user_presence_permitted(hr, -1);
+        if (r < 0)
+                return log_error_errno(r, "Failed to reset FIDO2 user presence permission flag: %m");
+
+        return 0;
+}
+
 static int update_home(int argc, char *argv[], void *userdata) {
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
@@ -1684,24 +1335,26 @@ static int update_home(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return r;
 
+        /* If we do multiple operations, let's output things more verbosely, since otherwise the repeated
+         * authentication might be confusing. */
+
+        if (arg_and_resize || arg_and_change_password)
+                log_info("Updating home directory.");
+
         for (;;) {
                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
                 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
                 _cleanup_free_ char *formatted = NULL;
 
-                r = sd_bus_message_new_method_call(
-                                bus,
-                                &m,
-                                "org.freedesktop.home1",
-                                "/org/freedesktop/home1",
-                                "org.freedesktop.home1.Manager",
-                                "UpdateHome");
+                r = bus_message_new_method_call(bus, &m, bus_home_mgr, "UpdateHome");
                 if (r < 0)
                         return bus_log_create_error(r);
 
                 r = json_variant_format(hr->json, 0, &formatted);
                 if (r < 0)
-                        return r;
+                        return log_error_errno(r, "Failed to format user record: %m");
+
+                (void) sd_bus_message_sensitive(m);
 
                 r = sd_bus_message_append(m, "s", formatted);
                 if (r < 0)
@@ -1723,20 +1376,17 @@ static int update_home(int argc, char *argv[], void *userdata) {
                         break;
         }
 
+        if (arg_and_resize)
+                log_info("Resizing home.");
+
+        (void) home_record_reset_human_interaction_permission(hr);
+
         /* Also sync down disk size to underlying LUKS/fscrypt/quota */
         while (arg_and_resize) {
                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
                 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
 
-                log_debug("Resizing");
-
-                r = sd_bus_message_new_method_call(
-                                bus,
-                                &m,
-                                "org.freedesktop.home1",
-                                "/org/freedesktop/home1",
-                                "org.freedesktop.home1.Manager",
-                                "ResizeHome");
+                r = bus_message_new_method_call(bus, &m, bus_home_mgr, "ResizeHome");
                 if (r < 0)
                         return bus_log_create_error(r);
 
@@ -1762,20 +1412,17 @@ static int update_home(int argc, char *argv[], void *userdata) {
                         break;
         }
 
+        if (arg_and_change_password)
+                log_info("Synchronizing passwords and encryption keys.");
+
+        (void) home_record_reset_human_interaction_permission(hr);
+
         /* Also sync down passwords to underlying LUKS/fscrypt */
         while (arg_and_change_password) {
                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
                 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
 
-                log_debug("Propagating password");
-
-                r = sd_bus_message_new_method_call(
-                                bus,
-                                &m,
-                                "org.freedesktop.home1",
-                                "/org/freedesktop/home1",
-                                "org.freedesktop.home1.Manager",
-                                "ChangePasswordHome");
+                r = bus_message_new_method_call(bus, &m, bus_home_mgr, "ChangePasswordHome");
                 if (r < 0)
                         return bus_log_create_error(r);
 
@@ -1812,6 +1459,8 @@ static int passwd_home(int argc, char *argv[], void *userdata) {
 
         if (arg_pkcs11_token_uri)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "To change the PKCS#11 security token use 'homectl update --pkcs11-token-uri=…'.");
+        if (arg_fido2_device)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "To change the FIDO2 security token use 'homectl update --fido2-device=…'.");
         if (identity_properties_specified())
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "The 'passwd' verb does not permit changing other record properties at the same time.");
 
@@ -1847,13 +1496,7 @@ static int passwd_home(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 *m = NULL;
 
-                r = sd_bus_message_new_method_call(
-                                bus,
-                                &m,
-                                "org.freedesktop.home1",
-                                "/org/freedesktop/home1",
-                                "org.freedesktop.home1.Manager",
-                                "ChangePasswordHome");
+                r = bus_message_new_method_call(bus, &m, bus_home_mgr, "ChangePasswordHome");
                 if (r < 0)
                         return bus_log_create_error(r);
 
@@ -1932,13 +1575,7 @@ static int resize_home(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 *m = NULL;
 
-                r = sd_bus_message_new_method_call(
-                                bus,
-                                &m,
-                                "org.freedesktop.home1",
-                                "/org/freedesktop/home1",
-                                "org.freedesktop.home1.Manager",
-                                "ResizeHome");
+                r = bus_message_new_method_call(bus, &m, bus_home_mgr, "ResizeHome");
                 if (r < 0)
                         return bus_log_create_error(r);
 
@@ -1975,13 +1612,7 @@ static int lock_home(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 *m = NULL;
 
-                r = sd_bus_message_new_method_call(
-                                bus,
-                                &m,
-                                "org.freedesktop.home1",
-                                "/org/freedesktop/home1",
-                                "org.freedesktop.home1.Manager",
-                                "LockHome");
+                r = bus_message_new_method_call(bus, &m, bus_home_mgr, "LockHome");
                 if (r < 0)
                         return bus_log_create_error(r);
 
@@ -2020,13 +1651,7 @@ static int unlock_home(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 *m = NULL;
 
-                        r = sd_bus_message_new_method_call(
-                                        bus,
-                                        &m,
-                                        "org.freedesktop.home1",
-                                        "/org/freedesktop/home1",
-                                        "org.freedesktop.home1.Manager",
-                                        "UnlockHome");
+                        r = bus_message_new_method_call(bus, &m, bus_home_mgr, "UnlockHome");
                         if (r < 0)
                                 return bus_log_create_error(r);
 
@@ -2089,13 +1714,7 @@ static int with_home(int argc, char *argv[], void *userdata) {
                 return log_oom();
 
         for (;;) {
-                r = sd_bus_message_new_method_call(
-                                bus,
-                                &m,
-                                "org.freedesktop.home1",
-                                "/org/freedesktop/home1",
-                                "org.freedesktop.home1.Manager",
-                                "AcquireHome");
+                r = bus_message_new_method_call(bus, &m, bus_home_mgr, "AcquireHome");
                 if (r < 0)
                         return bus_log_create_error(r);
 
@@ -2135,16 +1754,7 @@ static int with_home(int argc, char *argv[], void *userdata) {
                 }
         }
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.home1",
-                        "/org/freedesktop/home1",
-                        "org.freedesktop.home1.Manager",
-                        "GetHomeByName",
-                        &error,
-                        &reply,
-                        "s",
-                        argv[1]);
+        r = bus_call_method(bus, bus_home_mgr, "GetHomeByName", &error, &reply, "s", argv[1]);
         if (r < 0)
                 return log_error_errno(r, "Failed to inspect home: %s", bus_error_message(&error, r));
 
@@ -2171,13 +1781,7 @@ static int with_home(int argc, char *argv[], void *userdata) {
         /* Close the fd that pings the home now. */
         acquired_fd = safe_close(acquired_fd);
 
-        r = sd_bus_message_new_method_call(
-                        bus,
-                        &m,
-                        "org.freedesktop.home1",
-                        "/org/freedesktop/home1",
-                        "org.freedesktop.home1.Manager",
-                        "ReleaseHome");
+        r = bus_message_new_method_call(bus, &m, bus_home_mgr, "ReleaseHome");
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -2206,13 +1810,7 @@ static int lock_all_homes(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return r;
 
-        r = sd_bus_message_new_method_call(
-                        bus,
-                        &m,
-                        "org.freedesktop.home1",
-                        "/org/freedesktop/home1",
-                        "org.freedesktop.home1.Manager",
-                        "LockAllHomes");
+        r = bus_message_new_method_call(bus, &m, bus_home_mgr, "LockAllHomes");
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -2313,6 +1911,8 @@ static int help(int argc, char *argv[], void *userdata) {
                "                              Specify SSH public keys\n"
                "     --pkcs11-token-uri=URI   URI to PKCS#11 security token containing\n"
                "                              private key and matching X.509 certificate\n"
+               "     --fido2-device=PATH      Path to FIDO2 hidraw device with hmac-secret\n"
+               "                              extension\n"
                "\n%4$sAccount Management User Record Properties:%5$s\n"
                "     --locked=BOOL            Set locked account state\n"
                "     --not-before=TIMESTAMP   Do not allow logins before\n"
@@ -2357,6 +1957,9 @@ static int help(int argc, char *argv[], void *userdata) {
                "     --fs-type=TYPE           File system type to use in case of luks\n"
                "                              storage (ext4, xfs, btrfs)\n"
                "     --luks-discard=BOOL      Whether to use 'discard' feature of file system\n"
+               "                              when activated (mounted)\n"
+               "     --luks-offline-discard=BOOL\n"
+               "                              Whether to trim file on logout\n"
                "     --luks-cipher=CIPHER     Cipher to use for LUKS encryption\n"
                "     --luks-cipher-mode=MODE  Cipher mode to use for LUKS encryption\n"
                "     --luks-volume-key-size=BITS\n"
@@ -2410,6 +2013,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_IMAGE_PATH,
                 ARG_UMASK,
                 ARG_LUKS_DISCARD,
+                ARG_LUKS_OFFLINE_DISCARD,
                 ARG_JSON,
                 ARG_SETENV,
                 ARG_TIMEZONE,
@@ -2455,6 +2059,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_EXPORT_FORMAT,
                 ARG_AUTO_LOGIN,
                 ARG_PKCS11_TOKEN_URI,
+                ARG_FIDO2_DEVICE,
                 ARG_AND_RESIZE,
                 ARG_AND_CHANGE_PASSWORD,
         };
@@ -2503,6 +2108,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "image-path",                  required_argument, NULL, ARG_IMAGE_PATH                  },
                 { "fs-type",                     required_argument, NULL, ARG_FS_TYPE                     },
                 { "luks-discard",                required_argument, NULL, ARG_LUKS_DISCARD                },
+                { "luks-offline-discard",        required_argument, NULL, ARG_LUKS_OFFLINE_DISCARD        },
                 { "luks-cipher",                 required_argument, NULL, ARG_LUKS_CIPHER                 },
                 { "luks-cipher-mode",            required_argument, NULL, ARG_LUKS_CIPHER_MODE            },
                 { "luks-volume-key-size",        required_argument, NULL, ARG_LUKS_VOLUME_KEY_SIZE        },
@@ -2531,6 +2137,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "json",                        required_argument, NULL, ARG_JSON                        },
                 { "export-format",               required_argument, NULL, ARG_EXPORT_FORMAT               },
                 { "pkcs11-token-uri",            required_argument, NULL, ARG_PKCS11_TOKEN_URI            },
+                { "fido2-device",                required_argument, NULL, ARG_FIDO2_DEVICE                },
                 { "and-resize",                  required_argument, NULL, ARG_AND_RESIZE                  },
                 { "and-change-password",         required_argument, NULL, ARG_AND_CHANGE_PASSWORD         },
                 {}
@@ -2896,7 +2503,7 @@ static int parse_argv(int argc, char *argv[]) {
 
                         r = json_variant_set_field(&arg_identity_extra, "environment", ne);
                         if (r < 0)
-                                return log_error_errno(r, "Failed to set environent list: %m");
+                                return log_error_errno(r, "Failed to set environment list: %m");
 
                         break;
                 }
@@ -2932,6 +2539,9 @@ static int parse_argv(int argc, char *argv[]) {
                         if (!locale_is_valid(optarg))
                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Locale '%s' is not valid.", optarg);
 
+                        if (locale_is_installed(optarg) <= 0)
+                                log_warning("Locale '%s' is not installed, accepting anyway.", optarg);
+
                         r = json_variant_set_field_string(&arg_identity_extra, "preferredLanguage", optarg);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to set preferredLanguage field: %m");
@@ -3072,6 +2682,25 @@ static int parse_argv(int argc, char *argv[]) {
 
                         break;
 
+                case ARG_LUKS_OFFLINE_DISCARD:
+                        if (isempty(optarg)) {
+                                r = drop_from_identity("luksOfflineDiscard");
+                                if (r < 0)
+                                        return r;
+
+                                break;
+                        }
+
+                        r = parse_boolean(optarg);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse --luks-offline-discard= parameter: %s", optarg);
+
+                        r = json_variant_set_field_boolean(&arg_identity_extra, "luksOfflineDiscard", r);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to set offline discard field: %m");
+
+                        break;
+
                 case ARG_LUKS_VOLUME_KEY_SIZE:
                 case ARG_LUKS_PBKDF_PARALLEL_THREADS:
                 case ARG_RATE_LIMIT_BURST: {
@@ -3469,6 +3098,9 @@ static int parse_argv(int argc, char *argv[]) {
                 case ARG_PKCS11_TOKEN_URI: {
                         const char *p;
 
+                        if (streq(optarg, "list"))
+                                return list_pkcs11_tokens();
+
                         /* If --pkcs11-token-uri= is specified we always drop everything old */
                         FOREACH_STRING(p, "pkcs11TokenUri", "pkcs11EncryptedKey") {
                                 r = drop_from_identity(p);
@@ -3481,10 +3113,19 @@ static int parse_argv(int argc, char *argv[]) {
                                 break;
                         }
 
-                        if (!pkcs11_uri_valid(optarg))
-                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Not a valid PKCS#11 URI: %s", optarg);
+                        if (streq(optarg, "auto")) {
+                                _cleanup_free_ char *found = NULL;
+
+                                r = find_pkcs11_token_auto(&found);
+                                if (r < 0)
+                                        return r;
+                                r = strv_consume(&arg_pkcs11_token_uri, TAKE_PTR(found));
+                        } else {
+                                if (!pkcs11_uri_valid(optarg))
+                                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Not a valid PKCS#11 URI: %s", optarg);
 
-                        r = strv_extend(&arg_pkcs11_token_uri, optarg);
+                                r = strv_extend(&arg_pkcs11_token_uri, optarg);
+                        }
                         if (r < 0)
                                 return r;
 
@@ -3492,6 +3133,41 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
                 }
 
+                case ARG_FIDO2_DEVICE: {
+                        const char *p;
+
+                        if (streq(optarg, "list"))
+                                return list_fido2_devices();
+
+                        FOREACH_STRING(p, "fido2HmacCredential", "fido2HmacSalt") {
+                                r = drop_from_identity(p);
+                                if (r < 0)
+                                        return r;
+                        }
+
+                        if (isempty(optarg)) {
+                                arg_fido2_device = strv_free(arg_fido2_device);
+                                break;
+                        }
+
+                        if (streq(optarg, "auto")) {
+                                _cleanup_free_ char *found = NULL;
+
+                                r = find_fido2_auto(&found);
+                                if (r < 0)
+                                        return r;
+
+                                r = strv_consume(&arg_fido2_device, TAKE_PTR(found));
+                        } else
+                                r = strv_extend(&arg_fido2_device, optarg);
+
+                        if (r < 0)
+                                return r;
+
+                        strv_uniq(arg_fido2_device);
+                        break;
+                }
+
                 case 'j':
                         arg_json = true;
                         arg_json_format_flags = JSON_FORMAT_PRETTY_AUTO|JSON_FORMAT_COLOR_AUTO;
@@ -3562,7 +3238,7 @@ static int parse_argv(int argc, char *argv[]) {
                 }
         }
 
-        if (!strv_isempty(arg_pkcs11_token_uri))
+        if (!strv_isempty(arg_pkcs11_token_uri) || !strv_isempty(arg_fido2_device))
                 arg_and_change_password = true;
 
         if (arg_disk_size != UINT64_MAX || arg_disk_size_relative != UINT64_MAX)
@@ -3593,9 +3269,7 @@ static int run(int argc, char *argv[]) {
 
         int r;
 
-        log_show_color(true);
-        log_parse_environment();
-        log_open();
+        log_setup_cli();
 
         r = parse_argv(argc, argv);
         if (r <= 0)
diff --git a/src/home/homed-conf.c b/src/home/homed-conf.c
new file mode 100644 (file)
index 0000000..df3a173
--- /dev/null
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "conf-parser.h"
+#include "def.h"
+#include "home-util.h"
+#include "homed-conf.h"
+
+int manager_parse_config_file(Manager *m) {
+        int r;
+
+        assert(m);
+
+        r = config_parse_many_nulstr(
+                        PKGSYSCONFDIR "/homed.conf",
+                        CONF_PATHS_NULSTR("systemd/homed.conf.d"),
+                        "Home\0",
+                        config_item_perf_lookup, homed_gperf_lookup,
+                        CONFIG_PARSE_WARN,
+                        m,
+                        NULL);
+        if (r < 0)
+                return r;
+
+        return 0;
+
+}
+
+DEFINE_CONFIG_PARSE_ENUM(config_parse_default_storage, user_storage, UserStorage, "Failed to parse default storage setting");
+
+int config_parse_default_file_system_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) {
+
+        char **s = data;
+
+        assert(rvalue);
+        assert(s);
+
+        if (!isempty(rvalue) && !supported_fstype(rvalue)) {
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Unsupported file system, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        return free_and_strdup_warn(s, empty_to_null(rvalue));
+
+}
diff --git a/src/home/homed-conf.h b/src/home/homed-conf.h
new file mode 100644 (file)
index 0000000..00eb3fd
--- /dev/null
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "conf-parser.h"
+#include "homed-manager.h"
+
+int manager_parse_config_file(Manager *m);
+
+const struct ConfigPerfItem* homed_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
+
+CONFIG_PARSER_PROTOTYPE(config_parse_default_storage);
+CONFIG_PARSER_PROTOTYPE(config_parse_default_file_system_type);
diff --git a/src/home/homed-gperf.gperf b/src/home/homed-gperf.gperf
new file mode 100644 (file)
index 0000000..970da5f
--- /dev/null
@@ -0,0 +1,21 @@
+%{
+#if __GNUC__ >= 7
+_Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"")
+#endif
+#include <stddef.h>
+#include "conf-parser.h"
+#include "homed-conf.h"
+%}
+struct ConfigPerfItem;
+%null_strings
+%language=ANSI-C
+%define slot-name section_and_lvalue
+%define hash-function-name homed_gperf_hash
+%define lookup-function-name homed_gperf_lookup
+%readonly-tables
+%omit-struct-type
+%struct-type
+%includes
+%%
+Home.DefaultStorage,        config_parse_default_storage,          0, offsetof(Manager, default_storage)
+Home.DefaultFileSystemType, config_parse_default_file_system_type, 0, offsetof(Manager, default_file_system_type)
index 6b4fa58a6f62b1862667b39fb6e582703455f122..6be361a5aace96b4847e288c4b78a78b428e7ae2 100644 (file)
@@ -712,38 +712,13 @@ int bus_home_method_release(
 /* We map a uid_t as uint32_t bus property, let's ensure this is safe. */
 assert_cc(sizeof(uid_t) == sizeof(uint32_t));
 
-const sd_bus_vtable home_vtable[] = {
-        SD_BUS_VTABLE_START(0),
-        SD_BUS_PROPERTY("UserName", "s", NULL, offsetof(Home, user_name), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("UID", "u", NULL, offsetof(Home, uid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
-        SD_BUS_PROPERTY("UnixRecord", "(suusss)", property_get_unix_record, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
-        SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0),
-        SD_BUS_PROPERTY("UserRecord", "(sb)", property_get_user_record, 0, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION|SD_BUS_VTABLE_SENSITIVE),
-        SD_BUS_METHOD("Activate", "s", NULL, bus_home_method_activate, SD_BUS_VTABLE_SENSITIVE),
-        SD_BUS_METHOD("Deactivate", NULL, NULL, bus_home_method_deactivate, 0),
-        SD_BUS_METHOD("Unregister", NULL, NULL, bus_home_method_unregister, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("Realize", "s", NULL, bus_home_method_realize, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
-        SD_BUS_METHOD("Remove", NULL, NULL, bus_home_method_remove, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("Fixate", "s", NULL, bus_home_method_fixate, SD_BUS_VTABLE_SENSITIVE),
-        SD_BUS_METHOD("Authenticate", "s", NULL, bus_home_method_authenticate, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
-        SD_BUS_METHOD("Update", "s", NULL, bus_home_method_update, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
-        SD_BUS_METHOD("Resize", "ts", NULL, bus_home_method_resize, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
-        SD_BUS_METHOD("ChangePassword", "ss", NULL, bus_home_method_change_password, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
-        SD_BUS_METHOD("Lock", NULL, NULL, bus_home_method_lock, 0),
-        SD_BUS_METHOD("Unlock", "s", NULL, bus_home_method_unlock, SD_BUS_VTABLE_SENSITIVE),
-        SD_BUS_METHOD("Acquire", "sb", "h", bus_home_method_acquire, SD_BUS_VTABLE_SENSITIVE),
-        SD_BUS_METHOD("Ref", "b", "h", bus_home_method_ref, 0),
-        SD_BUS_METHOD("Release", NULL, NULL, bus_home_method_release, 0),
-        SD_BUS_VTABLE_END
-};
-
 int bus_home_path(Home *h, char **ret) {
         assert(ret);
 
         return sd_bus_path_encode("/org/freedesktop/home1/home", h->user_name, ret);
 }
 
-int bus_home_object_find(
+static int bus_home_object_find(
                 sd_bus *bus,
                 const char *path,
                 const char *interface,
@@ -772,7 +747,7 @@ int bus_home_object_find(
         return 1;
 }
 
-int bus_home_node_enumerator(
+static int bus_home_node_enumerator(
                 sd_bus *bus,
                 const char *path,
                 void *userdata,
@@ -802,6 +777,107 @@ int bus_home_node_enumerator(
         return 1;
 }
 
+const sd_bus_vtable home_vtable[] = {
+        SD_BUS_VTABLE_START(0),
+
+        SD_BUS_PROPERTY("UserName", "s",
+                        NULL, offsetof(Home, user_name),
+                        SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("UID", "u",
+                        NULL, offsetof(Home, uid),
+                        SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("UnixRecord", "(suusss)",
+                        property_get_unix_record, 0,
+                        SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("State", "s",
+                        property_get_state, 0,
+                        0),
+        SD_BUS_PROPERTY("UserRecord", "(sb)",
+                        property_get_user_record, 0,
+                        SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION|SD_BUS_VTABLE_SENSITIVE),
+
+        SD_BUS_METHOD_WITH_NAMES("Activate",
+                                 "s",
+                                 SD_BUS_PARAM(secret),
+                                 NULL,,
+                                 bus_home_method_activate,
+                                 SD_BUS_VTABLE_SENSITIVE),
+        SD_BUS_METHOD("Deactivate", NULL, NULL, bus_home_method_deactivate, 0),
+        SD_BUS_METHOD("Unregister", NULL, NULL, bus_home_method_unregister, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("Realize",
+                                 "s",
+                                 SD_BUS_PARAM(secret),
+                                 NULL,,
+                                 bus_home_method_realize,
+                                 SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
+
+        SD_BUS_METHOD("Remove", NULL, NULL, bus_home_method_remove, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("Fixate",
+                                 "s",
+                                 SD_BUS_PARAM(secret),
+                                 NULL,,
+                                 bus_home_method_fixate,
+                                 SD_BUS_VTABLE_SENSITIVE),
+        SD_BUS_METHOD_WITH_NAMES("Authenticate",
+                                 "s",
+                                 SD_BUS_PARAM(secret),
+                                 NULL,,
+                                 bus_home_method_authenticate,
+                                 SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
+        SD_BUS_METHOD_WITH_NAMES("Update",
+                                 "s",
+                                 SD_BUS_PARAM(user_record),
+                                 NULL,,
+                                 bus_home_method_update,
+                                 SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
+        SD_BUS_METHOD_WITH_NAMES("Resize",
+                                 "ts",
+                                 SD_BUS_PARAM(size)
+                                 SD_BUS_PARAM(secret),
+                                 NULL,,
+                                 bus_home_method_resize,
+                                 SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
+        SD_BUS_METHOD_WITH_NAMES("ChangePassword",
+                                 "ss",
+                                 SD_BUS_PARAM(new_secret)
+                                 SD_BUS_PARAM(old_secret),
+                                 NULL,,
+                                 bus_home_method_change_password,
+                                 SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
+        SD_BUS_METHOD("Lock", NULL, NULL, bus_home_method_lock, 0),
+        SD_BUS_METHOD_WITH_NAMES("Unlock",
+                                 "s",
+                                 SD_BUS_PARAM(secret),
+                                 NULL,,
+                                 bus_home_method_unlock,
+                                 SD_BUS_VTABLE_SENSITIVE),
+        SD_BUS_METHOD_WITH_NAMES("Acquire",
+                                 "sb",
+                                 SD_BUS_PARAM(secret)
+                                 SD_BUS_PARAM(please_suspend),
+                                 "h",
+                                 SD_BUS_PARAM(send_fd),
+                                 bus_home_method_acquire,
+                                 SD_BUS_VTABLE_SENSITIVE),
+        SD_BUS_METHOD_WITH_NAMES("Ref",
+                                 "b",
+                                 SD_BUS_PARAM(please_suspend),
+                                 "h",
+                                 SD_BUS_PARAM(send_fd),
+                                 bus_home_method_ref,
+                                 0),
+        SD_BUS_METHOD("Release", NULL, NULL, bus_home_method_release, 0),
+        SD_BUS_VTABLE_END
+};
+
+const BusObjectImplementation home_object = {
+        "/org/freedesktop/home1/home",
+        "org.freedesktop.home1.Home",
+        .fallback_vtables = BUS_FALLBACK_VTABLES({home_vtable, bus_home_object_find}),
+        .node_enumerator = bus_home_node_enumerator,
+        .manager = true,
+};
+
 static int on_deferred_change(sd_event_source *s, void *userdata) {
         _cleanup_free_ char *path = NULL;
         Home *h = userdata;
index 20516b120535015bd49af97e5901c10b0942de2f..8ac7ff9995478d3f825e266d4fadcdf58e26d750 100644 (file)
@@ -3,6 +3,7 @@
 
 #include "sd-bus.h"
 
+#include "bus-object.h"
 #include "homed-home.h"
 
 int bus_home_client_is_trusted(Home *h, sd_bus_message *message);
@@ -25,12 +26,9 @@ int bus_home_method_acquire(sd_bus_message *message, void *userdata, sd_bus_erro
 int bus_home_method_ref(sd_bus_message *message, void *userdata, sd_bus_error *error);
 int bus_home_method_release(sd_bus_message *message, void *userdata, sd_bus_error *error);
 
-extern const sd_bus_vtable home_vtable[];
+extern const BusObjectImplementation home_object;
 
 int bus_home_path(Home *h, char **ret);
 
-int bus_home_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error);
-int bus_home_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error);
-
 int bus_home_emit_change(Home *h);
 int bus_home_emit_remove(Home *h);
index 3ec47ee5da7ad71a3ff0411d4d49829f5636f0c4..f0c157cb7d882a117e8d1923aaaea6b41aa0c7ac 100644 (file)
@@ -292,7 +292,7 @@ int home_save_record(Home *h) {
 
         fn = strjoina("/var/lib/systemd/home/", h->user_name, ".identity");
 
-        r = write_string_file(fn, text, WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MODE_0600);
+        r = write_string_file(fn, text, WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MODE_0600|WRITE_STRING_FILE_SYNC);
         if (r < 0)
                 return r;
 
@@ -360,12 +360,10 @@ static int home_parse_worker_stdout(int _fd, UserRecord **ret) {
         if (lseek(fd, SEEK_SET, 0) == (off_t) -1)
                 return log_error_errno(errno, "Failed to seek to beginning of memfd: %m");
 
-        f = fdopen(fd, "r");
+        f = take_fdopen(&fd, "r");
         if (!f)
                 return log_error_errno(errno, "Failed to reopen memfd: %m");
 
-        TAKE_FD(fd);
-
         if (DEBUG_LOGGING) {
                 _cleanup_free_ char *text = NULL;
 
@@ -438,9 +436,9 @@ static int convert_worker_errno(Home *h, int e, sd_bus_error *error) {
         switch (e) {
 
         case -EMSGSIZE:
-                return sd_bus_error_setf(error, BUS_ERROR_BAD_HOME_SIZE, "File systems of this type cannot be shrinked");
+                return sd_bus_error_setf(error, BUS_ERROR_BAD_HOME_SIZE, "File systems of this type cannot be shrunk");
         case -ETXTBSY:
-                return sd_bus_error_setf(error, BUS_ERROR_BAD_HOME_SIZE, "File systems of this type can only be shrinked offline");
+                return sd_bus_error_setf(error, BUS_ERROR_BAD_HOME_SIZE, "File systems of this type can only be shrunk offline");
         case -ERANGE:
                 return sd_bus_error_setf(error, BUS_ERROR_BAD_HOME_SIZE, "File system size too small");
         case -ENOLINK:
@@ -459,6 +457,10 @@ static int convert_worker_errno(Home *h, int e, sd_bus_error *error) {
                 return sd_bus_error_setf(error, BUS_ERROR_TOKEN_PIN_NEEDED, "PIN for security token required.");
         case -ERFKILL:
                 return sd_bus_error_setf(error, BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED, "Security token requires protected authentication path.");
+        case -EMEDIUMTYPE:
+                return sd_bus_error_setf(error, BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED, "Security token requires user presence.");
+        case -ENOSTR:
+                return sd_bus_error_setf(error, BUS_ERROR_TOKEN_ACTION_TIMEOUT, "Token action timeout. (User was supposed to verify presence or similar, by interacting with the token, and didn't do that in time.)");
         case -EOWNERDEAD:
                 return sd_bus_error_setf(error, BUS_ERROR_TOKEN_PIN_LOCKED, "PIN of security token locked.");
         case -ENOLCK:
@@ -473,6 +475,8 @@ static int convert_worker_errno(Home *h, int e, sd_bus_error *error) {
                 return sd_bus_error_setf(error, BUS_ERROR_HOME_NOT_ACTIVE, "Home %s is currently not active", h->user_name);
         case -ENOSPC:
                 return sd_bus_error_setf(error, BUS_ERROR_NO_DISK_SPACE, "Not enough disk space for home %s", h->user_name);
+        case -EKEYREVOKED:
+                return sd_bus_error_setf(error, BUS_ERROR_HOME_CANT_AUTHENTICATE, "Home %s has no password or other authentication mechanism defined.", h->user_name);
         }
 
         return 0;
@@ -1004,6 +1008,8 @@ static int home_start_work(Home *h, const char *verb, UserRecord *hr, UserRecord
         if (r < 0)
                 return r;
         if (r == 0) {
+                const char *homework;
+
                 /* Child */
 
                 if (setenv("NOTIFY_SOCKET", "/run/systemd/home/notify", 1) < 0) {
@@ -1011,6 +1017,18 @@ static int home_start_work(Home *h, const char *verb, UserRecord *hr, UserRecord
                         _exit(EXIT_FAILURE);
                 }
 
+                if (h->manager->default_storage >= 0)
+                        if (setenv("SYSTEMD_HOME_DEFAULT_STORAGE", user_storage_to_string(h->manager->default_storage), 1) < 0) {
+                                log_error_errno(errno, "Failed to set $SYSTEMD_HOME_DEFAULT_STORAGE: %m");
+                                _exit(EXIT_FAILURE);
+                        }
+
+                if (h->manager->default_file_system_type)
+                        if (setenv("SYSTEMD_HOME_DEFAULT_FILE_SYSTEM_TYPE", h->manager->default_file_system_type, 1) < 0) {
+                                log_error_errno(errno, "Failed to set $SYSTEMD_HOME_DEFAULT_FILE_SYSTEM_TYPE: %m");
+                                _exit(EXIT_FAILURE);
+                        }
+
                 r = rearrange_stdio(stdin_fd, stdout_fd, STDERR_FILENO);
                 if (r < 0) {
                         log_error_errno(r, "Failed to rearrange stdin/stdout/stderr: %m");
@@ -1019,7 +1037,11 @@ static int home_start_work(Home *h, const char *verb, UserRecord *hr, UserRecord
 
                 stdin_fd = stdout_fd = -1; /* have been invalidated by rearrange_stdio() */
 
-                execl(SYSTEMD_HOMEWORK_PATH, SYSTEMD_HOMEWORK_PATH, verb, NULL);
+                /* Allow overriding the homework path via an environment variable, to make debugging
+                 * easier. */
+                homework = getenv("SYSTEMD_HOMEWORK_PATH") ?: SYSTEMD_HOMEWORK_PATH;
+
+                execl(homework, homework, verb, NULL);
                 log_error_errno(errno, "Failed to invoke " SYSTEMD_HOMEWORK_PATH ": %m");
                 _exit(EXIT_FAILURE);
         }
@@ -1339,7 +1361,13 @@ static int user_record_extend_with_binding(UserRecord *hr, UserRecord *with_bind
         return 0;
 }
 
-static int home_update_internal(Home *h, const char *verb, UserRecord *hr, UserRecord *secret, sd_bus_error *error) {
+static int home_update_internal(
+                Home *h,
+                const char *verb,
+                UserRecord *hr,
+                UserRecord *secret,
+                sd_bus_error *error) {
+
         _cleanup_(user_record_unrefp) UserRecord *new_hr = NULL, *saved_secret = NULL, *signed_hr = NULL;
         int r, c;
 
@@ -1523,7 +1551,7 @@ static int home_may_change_password(
 
         r = user_record_test_password_change_required(h->record);
         if (IN_SET(r, -EKEYREVOKED, -EOWNERDEAD, -EKEYEXPIRED))
-                return 0; /* expired in some form, but chaning is allowed */
+                return 0; /* expired in some form, but changing is allowed */
         if (IN_SET(r, -EKEYREJECTED, -EROFS))
                 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Expiration settings of account %s do not allow changing of password.", h->user_name);
         if (r < 0)
@@ -1750,7 +1778,7 @@ void home_process_notify(Home *h, char **l) {
 
         r = safe_atoi(e, &error);
         if (r < 0) {
-                log_debug_errno(r, "Failed to parse receieved error number, ignoring: %s", e);
+                log_debug_errno(r, "Failed to parse received error number, ignoring: %s", e);
                 return;
         }
         if (error <= 0) {
@@ -2348,7 +2376,7 @@ static int home_dispatch_release(Home *h, Operation *o) {
                 case HOME_UNFIXATED:
                 case HOME_ABSENT:
                 case HOME_INACTIVE:
-                        r = 0; /* done */
+                        r = 1; /* done */
                         break;
 
                 case HOME_LOCKED:
@@ -2368,7 +2396,7 @@ static int home_dispatch_release(Home *h, Operation *o) {
 
         assert(!h->current_operation);
 
-        if (r <= 0) /* failure or completed */
+        if (r != 0) /* failure or completed */
                 operation_result(o, r, &error);
         else /* ongoing */
                 h->current_operation = operation_ref(o);
@@ -2390,12 +2418,12 @@ static int home_dispatch_lock_all(Home *h, Operation *o) {
         case HOME_ABSENT:
         case HOME_INACTIVE:
                 log_info("Home %s is not active, no locking necessary.", h->user_name);
-                r = 0; /* done */
+                r = 1; /* done */
                 break;
 
         case HOME_LOCKED:
                 log_info("Home %s is already locked.", h->user_name);
-                r = 0; /* done */
+                r = 1; /* done */
                 break;
 
         case HOME_ACTIVE:
index 34a7b4945233223ba0037fab039856053859046c..ce6919a1a7f01cd098d54dbf9e27cbec6eae9184 100644 (file)
@@ -600,44 +600,210 @@ static int method_lock_all_homes(sd_bus_message *message, void *userdata, sd_bus
         return sd_bus_reply_method_return(message, NULL);
 }
 
-const sd_bus_vtable manager_vtable[] = {
+static const sd_bus_vtable manager_vtable[] = {
         SD_BUS_VTABLE_START(0),
 
         SD_BUS_PROPERTY("AutoLogin", "a(sso)", property_get_auto_login, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
 
-        SD_BUS_METHOD("GetHomeByName", "s", "usussso", method_get_home_by_name, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("GetHomeByUID", "u", "ssussso", method_get_home_by_uid, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("GetUserRecordByName", "s", "sbo", method_get_user_record_by_name, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
-        SD_BUS_METHOD("GetUserRecordByUID", "u", "sbo", method_get_user_record_by_uid, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
-        SD_BUS_METHOD("ListHomes", NULL, "a(susussso)", method_list_homes, SD_BUS_VTABLE_UNPRIVILEGED),
-
-        /* The following methods directly execute an operation on a home area, without ref-counting, queing
+        SD_BUS_METHOD_WITH_NAMES("GetHomeByName",
+                                 "s",
+                                 SD_BUS_PARAM(user_name),
+                                 "usussso",
+                                 SD_BUS_PARAM(uid)
+                                 SD_BUS_PARAM(home_state)
+                                 SD_BUS_PARAM(gid)
+                                 SD_BUS_PARAM(real_name)
+                                 SD_BUS_PARAM(home_directory)
+                                 SD_BUS_PARAM(shell)
+                                 SD_BUS_PARAM(bus_path),
+                                 method_get_home_by_name,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("GetHomeByUID",
+                                 "u",
+                                 SD_BUS_PARAM(uid),
+                                 "ssussso",
+                                 SD_BUS_PARAM(user_name)
+                                 SD_BUS_PARAM(home_state)
+                                 SD_BUS_PARAM(gid)
+                                 SD_BUS_PARAM(real_name)
+                                 SD_BUS_PARAM(home_directory)
+                                 SD_BUS_PARAM(shell)
+                                 SD_BUS_PARAM(bus_path),
+                                 method_get_home_by_uid,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("GetUserRecordByName",
+                                 "s",
+                                 SD_BUS_PARAM(user_name),
+                                 "sbo",
+                                 SD_BUS_PARAM(user_record)
+                                 SD_BUS_PARAM(incomplete)
+                                 SD_BUS_PARAM(bus_path),
+                                 method_get_user_record_by_name,
+                                 SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
+        SD_BUS_METHOD_WITH_NAMES("GetUserRecordByUID",
+                                 "u",
+                                 SD_BUS_PARAM(uid),
+                                 "sbo",
+                                 SD_BUS_PARAM(user_record)
+                                 SD_BUS_PARAM(incomplete)
+                                 SD_BUS_PARAM(bus_path),
+                                 method_get_user_record_by_uid,
+                                 SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
+        SD_BUS_METHOD_WITH_NAMES("ListHomes",
+                                 NULL,,
+                                 "a(susussso)",
+                                 SD_BUS_PARAM(home_areas),
+                                 method_list_homes,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+
+        /* The following methods directly execute an operation on a home area, without ref-counting, queueing
          * or anything, and are accessible through homectl. */
-        SD_BUS_METHOD("ActivateHome", "ss", NULL, method_activate_home, SD_BUS_VTABLE_SENSITIVE),
-        SD_BUS_METHOD("DeactivateHome", "s", NULL, method_deactivate_home, 0),
-        SD_BUS_METHOD("RegisterHome", "s", NULL, method_register_home, SD_BUS_VTABLE_UNPRIVILEGED),                                  /* Add JSON record to homed, but don't create actual $HOME */
-        SD_BUS_METHOD("UnregisterHome", "s", NULL, method_unregister_home, SD_BUS_VTABLE_UNPRIVILEGED),                              /* Remove JSON record from homed, but don't remove actual $HOME  */
-        SD_BUS_METHOD("CreateHome", "s", NULL, method_create_home, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),              /* Add JSON record, and create $HOME for it */
-        SD_BUS_METHOD("RealizeHome", "ss", NULL, method_realize_home, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),           /* Create $HOME for already registered JSON entry */
-        SD_BUS_METHOD("RemoveHome", "s", NULL, method_remove_home, SD_BUS_VTABLE_UNPRIVILEGED),                                      /* Remove JSON record and remove $HOME */
-        SD_BUS_METHOD("FixateHome", "ss", NULL, method_fixate_home, SD_BUS_VTABLE_SENSITIVE),                                        /* Investigate $HOME and propagate contained JSON record into our database */
-        SD_BUS_METHOD("AuthenticateHome", "ss", NULL, method_authenticate_home, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE), /* Just check credentials */
-        SD_BUS_METHOD("UpdateHome", "s", NULL, method_update_home, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),              /* Update JSON record of existing user */
-        SD_BUS_METHOD("ResizeHome", "sts", NULL, method_resize_home, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
-        SD_BUS_METHOD("ChangePasswordHome", "sss", NULL, method_change_password_home, SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
-        SD_BUS_METHOD("LockHome", "s", NULL, method_lock_home, 0),                                                                   /* Prepare active home for system suspend: flush out passwords, suspend access */
-        SD_BUS_METHOD("UnlockHome", "ss", NULL, method_unlock_home, SD_BUS_VTABLE_SENSITIVE),                                        /* Make $HOME usable after system resume again */
-
-        /* The following methods implement ref-counted activation, and are what the PAM module calls (and
-         * what "homectl with" runs). In contrast to the methods above which fail if an operation is already
-         * being executed on a home directory, these ones will queue the request, and are thus more
-         * reliable. Moreover, they are a bit smarter: AcquireHome() will fixate, activate, unlock, or
-         * authenticate depending on the state of the home, so that the end result is always the same
-         * (i.e. the home directory is accessible), and we always validate the specified passwords. RefHome()
-         * will not authenticate, and thus only works if home is already active. */
-        SD_BUS_METHOD("AcquireHome", "ssb", "h", method_acquire_home, SD_BUS_VTABLE_SENSITIVE),
-        SD_BUS_METHOD("RefHome", "sb", "h", method_ref_home, 0),
-        SD_BUS_METHOD("ReleaseHome", "s", NULL, method_release_home, 0),
+        SD_BUS_METHOD_WITH_NAMES("ActivateHome",
+                                 "ss",
+                                 SD_BUS_PARAM(user_name)
+                                 SD_BUS_PARAM(secret),
+                                 NULL,,
+                                 method_activate_home,
+                                 SD_BUS_VTABLE_SENSITIVE),
+        SD_BUS_METHOD_WITH_NAMES("DeactivateHome",
+                                 "s",
+                                 SD_BUS_PARAM(user_name),
+                                 NULL,,
+                                 method_deactivate_home,
+                                 0),
+
+        /* Add the JSON record to homed, but don't create actual $HOME */
+        SD_BUS_METHOD_WITH_NAMES("RegisterHome",
+                                 "s",
+                                 SD_BUS_PARAM(user_record),
+                                 NULL,,
+                                 method_register_home,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+
+        /* Remove the JSON record from homed, but don't remove actual $HOME  */
+        SD_BUS_METHOD_WITH_NAMES("UnregisterHome",
+                                 "s",
+                                 SD_BUS_PARAM(user_name),
+                                 NULL,,
+                                 method_unregister_home,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+
+        /* Add JSON record, and create $HOME for it */
+        SD_BUS_METHOD_WITH_NAMES("CreateHome",
+                                 "s",
+                                 SD_BUS_PARAM(user_record),
+                                 NULL,,
+                                 method_create_home,
+                                 SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
+
+        /* Create $HOME for already registered JSON entry */
+        SD_BUS_METHOD_WITH_NAMES("RealizeHome",
+                                 "ss",
+                                 SD_BUS_PARAM(user_name)
+                                 SD_BUS_PARAM(secret),
+                                 NULL,,
+                                 method_realize_home,
+                                 SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
+
+        /* Remove the JSON record and remove $HOME */
+        SD_BUS_METHOD_WITH_NAMES("RemoveHome",
+                                 "s",
+                                 SD_BUS_PARAM(user_name),
+                                 NULL,,
+                                 method_remove_home,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+
+        /* Investigate $HOME and propagate contained JSON record into our database */
+        SD_BUS_METHOD_WITH_NAMES("FixateHome",
+                                 "ss",
+                                 SD_BUS_PARAM(user_name)
+                                 SD_BUS_PARAM(secret),
+                                 NULL,,
+                                 method_fixate_home,
+                                 SD_BUS_VTABLE_SENSITIVE),
+
+        /* Just check credentials */
+        SD_BUS_METHOD_WITH_NAMES("AuthenticateHome",
+                                 "ss",
+                                 SD_BUS_PARAM(user_name)
+                                 SD_BUS_PARAM(secret),
+                                 NULL,,
+                                 method_authenticate_home,
+                                 SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
+
+        /* Update the JSON record of existing user */
+        SD_BUS_METHOD_WITH_NAMES("UpdateHome",
+                                 "s",
+                                 SD_BUS_PARAM(user_record),
+                                 NULL,,
+                                 method_update_home,
+                                 SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
+
+        SD_BUS_METHOD_WITH_NAMES("ResizeHome",
+                                 "sts",
+                                 SD_BUS_PARAM(user_name)
+                                 SD_BUS_PARAM(size)
+                                 SD_BUS_PARAM(secret),
+                                 NULL,,
+                                 method_resize_home,
+                                 SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
+
+        SD_BUS_METHOD_WITH_NAMES("ChangePasswordHome",
+                                 "sss",
+                                 SD_BUS_PARAM(user_name)
+                                 SD_BUS_PARAM(new_secret)
+                                 SD_BUS_PARAM(old_secret),
+                                 NULL,,
+                                 method_change_password_home,
+                                 SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
+
+        /* Prepare active home for system suspend: flush out passwords, suspend access */
+        SD_BUS_METHOD_WITH_NAMES("LockHome",
+                                 "s",
+                                 SD_BUS_PARAM(user_name),
+                                 NULL,,
+                                 method_lock_home,
+                                 0),
+
+        /* Make $HOME usable after system resume again */
+        SD_BUS_METHOD_WITH_NAMES("UnlockHome",
+                                 "ss",
+                                 SD_BUS_PARAM(user_name)
+                                 SD_BUS_PARAM(secret),
+                                 NULL,,
+                                 method_unlock_home,
+                                 SD_BUS_VTABLE_SENSITIVE),
+
+        /* The following methods implement ref-counted activation, and are what the PAM module and "homectl
+         * with" use. In contrast to the methods above which fail if an operation is already being executed
+         * on a home directory, these ones will queue the request, and are thus more reliable. Moreover,
+         * they are a bit smarter: AcquireHome() will fixate, activate, unlock, or authenticate depending on
+         * the state of the home area, so that the end result is always the same (i.e. the home directory is
+         * accessible), and we always validate the specified passwords. RefHome() will not authenticate, and
+         * thus only works if the home area is already active. */
+        SD_BUS_METHOD_WITH_NAMES("AcquireHome",
+                                 "ssb",
+                                 SD_BUS_PARAM(user_name)
+                                 SD_BUS_PARAM(secret)
+                                 SD_BUS_PARAM(please_suspend),
+                                 "h",
+                                 SD_BUS_PARAM(send_fd),
+                                 method_acquire_home,
+                                 SD_BUS_VTABLE_UNPRIVILEGED|SD_BUS_VTABLE_SENSITIVE),
+        SD_BUS_METHOD_WITH_NAMES("RefHome",
+                                 "sb",
+                                 SD_BUS_PARAM(user_name)
+                                 SD_BUS_PARAM(please_suspend),
+                                 "h",
+                                 SD_BUS_PARAM(send_fd),
+                                 method_ref_home,
+                                 0),
+        SD_BUS_METHOD_WITH_NAMES("ReleaseHome",
+                                 "s",
+                                 SD_BUS_PARAM(user_name),
+                                 NULL,,
+                                 method_release_home,
+                                 0),
 
         /* An operation that acts on all homes that allow it */
         SD_BUS_METHOD("LockAllHomes", NULL, NULL, method_lock_all_homes, 0),
@@ -645,6 +811,13 @@ const sd_bus_vtable manager_vtable[] = {
         SD_BUS_VTABLE_END
 };
 
+const BusObjectImplementation manager_object = {
+        "/org/freedesktop/home1",
+        "org.freedesktop.home1.Manager",
+        .vtables = BUS_VTABLES(manager_vtable),
+        .children = BUS_IMPLEMENTATIONS(&home_object),
+};
+
 static int on_deferred_auto_login(sd_event_source *s, void *userdata) {
         Manager *m = userdata;
         int r;
index 40e1cc3d86d60b8165d494a1d44afcd3358cd465..93bef9df8eb905332cc7c6b29e62d5bf6dd7ffe4 100644 (file)
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
-#include "sd-bus.h"
+#include "bus-util.h"
 
-extern const sd_bus_vtable manager_vtable[];
+extern const BusObjectImplementation manager_object;
index b1153f6236d6d867527eb9a874e0191b0fdafe13..7d951bee3b1189be626fae638b4721228137deac 100644 (file)
@@ -12,6 +12,7 @@
 #include "btrfs-util.h"
 #include "bus-common-errors.h"
 #include "bus-error.h"
+#include "bus-log-control-api.h"
 #include "bus-polkit.h"
 #include "clean-ipc.h"
 #include "conf-files.h"
@@ -23,6 +24,7 @@
 #include "fs-util.h"
 #include "gpt.h"
 #include "home-util.h"
+#include "homed-conf.h"
 #include "homed-home-bus.h"
 #include "homed-home.h"
 #include "homed-manager-bus.h"
@@ -183,10 +185,18 @@ int manager_new(Manager **ret) {
 
         assert(ret);
 
-        m = new0(Manager, 1);
+        m = new(Manager, 1);
         if (!m)
                 return -ENOMEM;
 
+        *m = (Manager) {
+                .default_storage = _USER_STORAGE_INVALID,
+        };
+
+        r = manager_parse_config_file(m);
+        if (r < 0)
+                return r;
+
         r = sd_event_default(&m->event);
         if (r < 0)
                 return r;
@@ -250,6 +260,8 @@ Manager* manager_free(Manager *m) {
 
         varlink_server_unref(m->varlink_server);
 
+        free(m->default_file_system_type);
+
         return mfree(m);
 }
 
@@ -317,21 +329,36 @@ static int manager_add_home_by_record(
         _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
         unsigned line, column;
         int r, is_signed;
+        struct stat st;
         Home *h;
 
         assert(m);
         assert(name);
         assert(fname);
 
+        if (fstatat(dir_fd, fname, &st, 0) < 0)
+                return log_error_errno(errno, "Failed to stat identity record %s: %m", fname);
+
+        if (!S_ISREG(st.st_mode)) {
+                log_debug("Identity record file %s is not a regular file, ignoring.", fname);
+                return 0;
+        }
+
+        if (st.st_size == 0)
+                goto unlink_this_file;
+
         r = json_parse_file_at(NULL, dir_fd, fname, JSON_PARSE_SENSITIVE, &v, &line, &column);
         if (r < 0)
                 return log_error_errno(r, "Failed to parse identity record at %s:%u%u: %m", fname, line, column);
 
+        if (json_variant_is_blank_object(v))
+                goto unlink_this_file;
+
         hr = user_record_new();
         if (!hr)
                 return log_oom();
 
-        r = user_record_load(hr, v, USER_RECORD_LOAD_REFUSE_SECRET);
+        r = user_record_load(hr, v, USER_RECORD_LOAD_REFUSE_SECRET|USER_RECORD_LOG);
         if (r < 0)
                 return r;
 
@@ -367,7 +394,7 @@ static int manager_add_home_by_record(
 
                 /* If we acquired a record now for a previously unallocated entry, then reset the state. This
                  * makes sure home_get_state() will check for the availability of the image file dynamically
-                 * in order to detect to distuingish HOME_INACTIVE and HOME_ABSENT. */
+                 * in order to detect to distinguish HOME_INACTIVE and HOME_ABSENT. */
                 if (h->state == HOME_UNFIXATED)
                         h->state = _HOME_STATE_INVALID;
         } else {
@@ -382,6 +409,19 @@ static int manager_add_home_by_record(
         h->signed_locally = is_signed == USER_RECORD_SIGNED_EXCLUSIVE;
 
         return 1;
+
+unlink_this_file:
+        /* If this is an empty file, then let's just remove it. An empty file is not useful in any case, and
+         * apparently xfs likes to leave empty files around when not unmounted cleanly (see
+         * https://github.com/systemd/systemd/issues/15178 for example). Note that we don't delete non-empty
+         * files even if they are invalid, because that's just too risky, we might delete data the user still
+         * needs. But empty files are never useful, hence let's just remove them. */
+
+        if (unlinkat(dir_fd, fname, 0) < 0)
+                return log_error_errno(errno, "Failed to remove empty user record file %s: %m", fname);
+
+        log_notice("Discovered empty user record file /var/lib/systemd/home/%s, removed automatically.", fname);
+        return 0;
 }
 
 static int manager_enumerate_records(Manager *m) {
@@ -473,7 +513,7 @@ static int search_quota(uid_t uid, const char *exclude_quota_path) {
                         if (ERRNO_IS_NOT_SUPPORTED(r))
                                 log_debug_errno(r, "No UID quota support on %s, ignoring.", where);
                         else
-                                log_warning_errno(r, "Failed to query quota on %s, ignoring.", where);
+                                log_warning_errno(r, "Failed to query quota on %s, ignoring: %m", where);
 
                         continue;
                 }
@@ -878,21 +918,9 @@ static int manager_connect_bus(Manager *m) {
         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/home1", "org.freedesktop.home1.Manager", manager_vtable, m);
+        r = bus_add_implementation(m->bus, &manager_object, m);
         if (r < 0)
-                return log_error_errno(r, "Failed to add manager object vtable: %m");
-
-        r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/home1/home", "org.freedesktop.home1.Home", home_vtable, bus_home_object_find, m);
-        if (r < 0)
-                return log_error_errno(r, "Failed to add image object vtable: %m");
-
-        r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/home1/home", bus_home_node_enumerator, m);
-        if (r < 0)
-                return log_error_errno(r, "Failed to add image enumerator: %m");
-
-        r = sd_bus_add_object_manager(m->bus, NULL, "/org/freedesktop/home1/home");
-        if (r < 0)
-                return log_error_errno(r, "Failed to add object manager: %m");
+                return r;
 
         r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.home1", 0, NULL, NULL);
         if (r < 0)
@@ -957,10 +985,7 @@ static ssize_t read_datagram(int fd, struct ucred *ret_sender, void **ret) {
                 return -ENOMEM;
 
         if (ret_sender) {
-                union {
-                        struct cmsghdr cmsghdr;
-                        uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
-                } control;
+                CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control;
                 bool found_ucred = false;
                 struct cmsghdr *cmsg;
                 struct msghdr mh;
@@ -1042,7 +1067,7 @@ static int on_notify_socket(sd_event_source *s, int fd, uint32_t revents, void *
 
         h = hashmap_get(m->homes_by_worker_pid, PID_TO_PTR(sender.pid));
         if (!h) {
-                log_warning("Recieved notify datagram of unknown process, ignoring.");
+                log_warning("Received notify datagram of unknown process, ignoring.");
                 return 0;
         }
 
@@ -1312,7 +1337,7 @@ static int manager_generate_key_pair(Manager *m) {
         if (PEM_write_PUBKEY(fpublic, m->private_key) <= 0)
                 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write public key.");
 
-        r = fflush_and_check(fpublic);
+        r = fflush_sync_and_check(fpublic);
         if (r < 0)
                 return log_error_errno(r, "Failed to write private key: %m");
 
@@ -1326,7 +1351,7 @@ static int manager_generate_key_pair(Manager *m) {
         if (PEM_write_PrivateKey(fprivate, m->private_key, NULL, NULL, 0, NULL, 0) <= 0)
                 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write private key pair.");
 
-        r = fflush_and_check(fprivate);
+        r = fflush_sync_and_check(fprivate);
         if (r < 0)
                 return log_error_errno(r, "Failed to write private key: %m");
 
@@ -1340,10 +1365,14 @@ static int manager_generate_key_pair(Manager *m) {
 
         if (rename(temp_private, "/var/lib/systemd/home/local.private") < 0) {
                 (void) unlink_noerrno("/var/lib/systemd/home/local.public"); /* try to remove the file we already created */
-                return log_error_errno(errno, "Failed to move privtate key file into place: %m");
+                return log_error_errno(errno, "Failed to move private key file into place: %m");
         }
         temp_private = mfree(temp_private);
 
+        r = fsync_path_at(AT_FDCWD, "/var/lib/systemd/home/");
+        if (r < 0)
+                log_warning_errno(r, "Failed to sync /var/lib/systemd/home/, ignoring: %m");
+
         return 1;
 }
 
@@ -1656,7 +1685,7 @@ int manager_enqueue_gc(Manager *m, Home *focus) {
 
                 return 0;
         } else
-                m->gc_focus = focus; /* start focussed */
+                m->gc_focus = focus; /* start focused */
 
         r = sd_event_add_defer(m->event, &m->deferred_gc_event_source, on_deferred_gc, m);
         if (r < 0)
index 00298a3d2dcbce661ffda2be2edab3a270082cfd..83a714462792a103cc0d0f010df40fe40ad52555 100644 (file)
@@ -28,10 +28,12 @@ struct Manager {
         Hashmap *homes_by_sysfs;
 
         bool scan_slash_home;
+        UserStorage default_storage;
+        char *default_file_system_type;
 
         sd_event_source *inotify_event_source;
 
-        /* An even source we receieve sd_notify() messages from our worker from */
+        /* An event source we receive sd_notify() messages from our worker from */
         sd_event_source *notify_socket_event_source;
 
         sd_device_monitor *device_monitor;
index ca435582691e33d2bb22df4279e9ccdc02f88134..ed8404d7e3da2585921877df3bea812185a99b16 100644 (file)
@@ -3,23 +3,31 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 
+#include "bus-log-control-api.h"
 #include "daemon-util.h"
 #include "homed-manager.h"
+#include "homed-manager-bus.h"
 #include "log.h"
 #include "main-func.h"
+#include "service-util.h"
 #include "signal-util.h"
 
 static int run(int argc, char *argv[]) {
-        _cleanup_(notify_on_cleanup) const char *notify_stop = NULL;
         _cleanup_(manager_freep) Manager *m = NULL;
+        _cleanup_(notify_on_cleanup) const char *notify_stop = NULL;
         int r;
 
         log_setup_service();
 
-        umask(0022);
+        r = service_parse_argv("systemd-homed.service",
+                               "A service to create, remove, change or inspect home areas.",
+                               BUS_IMPLEMENTATIONS(&manager_object,
+                                                   &log_control_object),
+                               argc, argv);
+        if (r <= 0)
+                return r;
 
-        if (argc != 1)
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments.");
+        umask(0022);
 
         if (setenv("SYSTEMD_BYPASS_USERDB", "io.systemd.Home", 1) < 0)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to set $SYSTEMD_BYPASS_USERDB: %m");
diff --git a/src/home/homed.conf b/src/home/homed.conf
new file mode 100644 (file)
index 0000000..6de75fc
--- /dev/null
@@ -0,0 +1,16 @@
+#  This file is part of systemd.
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU Lesser General Public License as published by
+#  the Free Software Foundation; either version 2.1 of the License, or
+#  (at your option) any later version.
+#
+# Entries in this file show the compile time defaults.
+# You can change settings by editing this file.
+# Defaults can be restored by simply deleting this file.
+#
+# See homed.conf(5) for details
+
+[Home]
+#DefaultStorage=
+#DefaultFileSystemType=ext4
index 27f298144a0f4af096ddd1c14f820ee04878f8c6..cfceaed74274fdd2dd98ccc41d5d5103a5b03371 100644 (file)
@@ -28,7 +28,7 @@ int home_prepare_cifs(
                 char **pw;
                 int r;
 
-                r = home_unshare_and_mount(NULL, NULL, false);
+                r = home_unshare_and_mount(NULL, NULL, false, user_record_mount_flags(h));
                 if (r < 0)
                         return r;
 
@@ -98,7 +98,7 @@ int home_prepare_cifs(
 
 int home_activate_cifs(
                 UserRecord *h,
-                char ***pkcs11_decrypted_passwords,
+                PasswordCache *cache,
                 UserRecord **ret_home) {
 
         _cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT;
@@ -120,7 +120,7 @@ int home_activate_cifs(
         if (r < 0)
                 return r;
 
-        r = home_refresh(h, &setup, NULL, pkcs11_decrypted_passwords, NULL, &new_home);
+        r = home_refresh(h, &setup, NULL, cache, NULL, &new_home);
         if (r < 0)
                 return r;
 
@@ -142,7 +142,8 @@ int home_create_cifs(UserRecord *h, UserRecord **ret_home) {
         _cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT;
         _cleanup_(user_record_unrefp) UserRecord *new_home = NULL;
         _cleanup_(closedirp) DIR *d = NULL;
-        int r, copy;
+        _cleanup_close_ int copy = -1;
+        int r;
 
         assert(h);
         assert(user_record_storage(h) == USER_CIFS);
@@ -166,11 +167,9 @@ int home_create_cifs(UserRecord *h, UserRecord **ret_home) {
         if (copy < 0)
                 return -errno;
 
-        d = fdopendir(copy);
-        if (!d) {
-                safe_close(copy);
+        d = take_fdopendir(&copy);
+        if (!d)
                 return -errno;
-        }
 
         errno = 0;
         if (readdir_no_dot(d))
index 346be8826ea0e2f0d118605e8327626ef7adef4d..ee799e2a4b8ed9ed2dc7bb3c62c5f5011b3e14df 100644 (file)
@@ -6,6 +6,6 @@
 
 int home_prepare_cifs(UserRecord *h, bool already_activated, HomeSetup *setup);
 
-int home_activate_cifs(UserRecord *h, char ***pkcs11_decrypted_passwords, UserRecord **ret_home);
+int home_activate_cifs(UserRecord *h, PasswordCache *cache, UserRecord **ret_home);
 
 int home_create_cifs(UserRecord *h, UserRecord **ret_home);
index 8a4cb1732ac771749b53dd474724d6836e8503fe..7d00da214aa1a563ac0b8282a2775897b0a87582 100644 (file)
@@ -26,7 +26,7 @@ int home_prepare_directory(UserRecord *h, bool already_activated, HomeSetup *set
 
 int home_activate_directory(
                 UserRecord *h,
-                char ***pkcs11_decrypted_passwords,
+                PasswordCache *cache,
                 UserRecord **ret_home) {
 
         _cleanup_(user_record_unrefp) UserRecord *new_home = NULL, *header_home = NULL;
@@ -44,11 +44,11 @@ int home_activate_directory(
         assert_se(hdo = user_record_home_directory(h));
         hd = strdupa(hdo);
 
-        r = home_prepare(h, false, pkcs11_decrypted_passwords, &setup, &header_home);
+        r = home_prepare(h, false, cache, &setup, &header_home);
         if (r < 0)
                 return r;
 
-        r = home_refresh(h, &setup, header_home, pkcs11_decrypted_passwords, NULL, &new_home);
+        r = home_refresh(h, &setup, header_home, cache, NULL, &new_home);
         if (r < 0)
                 return r;
 
@@ -193,7 +193,7 @@ int home_create_directory_or_subvolume(UserRecord *h, UserRecord **ret_home) {
 int home_resize_directory(
                 UserRecord *h,
                 bool already_activated,
-                char ***pkcs11_decrypted_passwords,
+                PasswordCache *cache,
                 HomeSetup *setup,
                 UserRecord **ret_home) {
 
@@ -205,11 +205,11 @@ int home_resize_directory(
         assert(ret_home);
         assert(IN_SET(user_record_storage(h), USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT));
 
-        r = home_prepare(h, already_activated, pkcs11_decrypted_passwords, setup, NULL);
+        r = home_prepare(h, already_activated, cache, setup, NULL);
         if (r < 0)
                 return r;
 
-        r = home_load_embedded_identity(h, setup->root_fd, NULL, USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL, pkcs11_decrypted_passwords, &embedded_home, &new_home);
+        r = home_load_embedded_identity(h, setup->root_fd, NULL, USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL, cache, &embedded_home, &new_home);
         if (r < 0)
                 return r;
 
index 047c3a70a03260f10da837a20a4829db6e7fb676..717837f348f6078ff54901d5aeff03191321db49 100644 (file)
@@ -5,6 +5,6 @@
 #include "user-record.h"
 
 int home_prepare_directory(UserRecord *h, bool already_activated, HomeSetup *setup);
-int home_activate_directory(UserRecord *h, char ***pkcs11_decrypted_passwords, UserRecord **ret_home);
+int home_activate_directory(UserRecord *h, PasswordCache *cache, UserRecord **ret_home);
 int home_create_directory_or_subvolume(UserRecord *h, UserRecord **ret_home);
-int home_resize_directory(UserRecord *h, bool already_activated, char ***pkcs11_decrypted_passwords, HomeSetup *setup, UserRecord **ret_home);
+int home_resize_directory(UserRecord *h, bool already_activated, PasswordCache *cache, HomeSetup *setup, UserRecord **ret_home);
diff --git a/src/home/homework-fido2.c b/src/home/homework-fido2.c
new file mode 100644 (file)
index 0000000..36fe059
--- /dev/null
@@ -0,0 +1,197 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <fido.h>
+
+#include "hexdecoct.h"
+#include "homework-fido2.h"
+#include "strv.h"
+
+static int fido2_use_specific_token(
+                const char *path,
+                UserRecord *h,
+                UserRecord *secret,
+                const Fido2HmacSalt *salt,
+                char **ret) {
+
+        _cleanup_(fido_cbor_info_free) fido_cbor_info_t *di = NULL;
+        _cleanup_(fido_assert_free) fido_assert_t *a = NULL;
+        _cleanup_(fido_dev_free) fido_dev_t *d = NULL;
+        bool found_extension = false;
+        size_t n, hmac_size;
+        const void *hmac;
+        char **e;
+        int r;
+
+        d = fido_dev_new();
+        if (!d)
+                return log_oom();
+
+        r = fido_dev_open(d, path);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to open FIDO2 device %s: %s", path, fido_strerr(r));
+
+        if (!fido_dev_is_fido2(d))
+                return log_error_errno(SYNTHETIC_ERRNO(ENODEV),
+                                       "Specified device %s is not a FIDO2 device.", path);
+
+        di = fido_cbor_info_new();
+        if (!di)
+                return log_oom();
+
+        r = fido_dev_get_cbor_info(d, di);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to get CBOR device info for %s: %s", path, fido_strerr(r));
+
+        e = fido_cbor_info_extensions_ptr(di);
+        n = fido_cbor_info_extensions_len(di);
+
+        for (size_t i = 0; i < n; i++)
+                if (streq(e[i], "hmac-secret")) {
+                        found_extension = true;
+                        break;
+                }
+
+        if (!found_extension)
+                return log_error_errno(SYNTHETIC_ERRNO(ENODEV),
+                                       "Specified device %s is a FIDO2 device, but does not support the required HMAC-SECRET extension.", path);
+
+        a = fido_assert_new();
+        if (!a)
+                return log_oom();
+
+        r = fido_assert_set_extensions(a, FIDO_EXT_HMAC_SECRET);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to enable HMAC-SECRET extension on FIDO2 assertion: %s", fido_strerr(r));
+
+        r = fido_assert_set_hmac_salt(a, salt->salt, salt->salt_size);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to set salt on FIDO2 assertion: %s", fido_strerr(r));
+
+        r = fido_assert_set_rp(a, "io.systemd.home");
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to set FIDO2 assertion ID: %s", fido_strerr(r));
+
+        r = fido_assert_set_clientdata_hash(a, (const unsigned char[32]) {}, 32);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to set FIDO2 assertion client data hash: %s", fido_strerr(r));
+
+        r = fido_assert_allow_cred(a, salt->credential.id, salt->credential.size);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to add FIDO2 assertion credential ID: %s", fido_strerr(r));
+
+        r = fido_assert_set_up(a, h->fido2_user_presence_permitted <= 0 ? FIDO_OPT_FALSE : FIDO_OPT_TRUE);
+        if (r != FIDO_OK)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to set FIDO2 assertion user presence: %s", fido_strerr(r));
+
+        log_info("Asking FIDO2 token for authentication.");
+
+        r = fido_dev_get_assert(d, a, NULL); /* try without pin first */
+        if (r == FIDO_ERR_PIN_REQUIRED) {
+                char **i;
+
+                /* OK, we needed a pin, try with all pins in turn */
+                STRV_FOREACH(i, secret->token_pin) {
+                        r = fido_dev_get_assert(d, a, *i);
+                        if (r != FIDO_ERR_PIN_INVALID)
+                                break;
+                }
+        }
+
+        switch (r) {
+        case FIDO_OK:
+                break;
+        case FIDO_ERR_NO_CREDENTIALS:
+                return log_error_errno(SYNTHETIC_ERRNO(EBADSLT),
+                                       "Wrong security token; needed credentials not present on token.");
+        case FIDO_ERR_PIN_REQUIRED:
+                return log_error_errno(SYNTHETIC_ERRNO(ENOANO),
+                                       "Security token requires PIN.");
+        case FIDO_ERR_PIN_AUTH_BLOCKED:
+                return log_error_errno(SYNTHETIC_ERRNO(EOWNERDEAD),
+                                       "PIN of security token is blocked, please remove/reinsert token.");
+        case FIDO_ERR_PIN_INVALID:
+                return log_error_errno(SYNTHETIC_ERRNO(ENOLCK),
+                                       "PIN of security token incorrect.");
+        case FIDO_ERR_UP_REQUIRED:
+                return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE),
+                                       "User presence required.");
+        case FIDO_ERR_ACTION_TIMEOUT:
+                return log_error_errno(SYNTHETIC_ERRNO(ENOSTR),
+                                       "Token action timeout. (User didn't interact with token quickly enough.)");
+        default:
+                return log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                       "Failed to ask token for assertion: %s", fido_strerr(r));
+        }
+
+        hmac = fido_assert_hmac_secret_ptr(a, 0);
+        if (!hmac)
+                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve HMAC secret.");
+
+        hmac_size = fido_assert_hmac_secret_len(a, 0);
+
+        r = base64mem(hmac, hmac_size, ret);
+        if (r < 0)
+                return log_error_errno(r, "Failed to base64 encode HMAC secret: %m");
+
+        return 0;
+}
+
+int fido2_use_token(UserRecord *h, UserRecord *secret, const Fido2HmacSalt *salt, char **ret) {
+        size_t allocated = 64, found = 0;
+        fido_dev_info_t *di = NULL;
+        int r;
+
+        di = fido_dev_info_new(allocated);
+        if (!di)
+                return log_oom();
+
+        r = fido_dev_info_manifest(di, allocated, &found);
+        if (r == FIDO_ERR_INTERNAL) {
+                /* The library returns FIDO_ERR_INTERNAL when no devices are found. I wish it wouldn't. */
+                r = log_debug_errno(SYNTHETIC_ERRNO(EAGAIN), "Got FIDO_ERR_INTERNAL, assuming no devices.");
+                goto finish;
+        }
+        if (r != FIDO_OK) {
+                r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to enumerate FIDO2 devices: %s", fido_strerr(r));
+                goto finish;
+        }
+
+        for (size_t i = 0; i < found; i++) {
+                const fido_dev_info_t *entry;
+                const char *path;
+
+                entry = fido_dev_info_ptr(di, i);
+                if (!entry) {
+                        r = log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                            "Failed to get device information for FIDO device %zu.", i);
+                        goto finish;
+                }
+
+                path = fido_dev_info_path(entry);
+                if (!path) {
+                        r = log_error_errno(SYNTHETIC_ERRNO(EIO),
+                                            "Failed to query FIDO device path.");
+                        goto finish;
+                }
+
+                r = fido2_use_specific_token(path, h, secret, salt, ret);
+                if (!IN_SET(r,
+                            -EBADSLT, /* device doesn't understand our credential hash */
+                            -ENODEV   /* device is not a FIDO2 device with HMAC-SECRET */))
+                        goto finish;
+        }
+
+        r = -EAGAIN;
+
+finish:
+        fido_dev_info_free(&di, allocated);
+        return r;
+}
diff --git a/src/home/homework-fido2.h b/src/home/homework-fido2.h
new file mode 100644 (file)
index 0000000..d3b142a
--- /dev/null
@@ -0,0 +1,6 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "user-record.h"
+
+int fido2_use_token(UserRecord *h, UserRecord *secret, const Fido2HmacSalt *salt, char **ret);
index 696e265397e2b941d775ae8f4ecfa4a96d56817e..da9bb64b712cb7b5e5a35e4094cfecdb83a69b07 100644 (file)
@@ -208,7 +208,7 @@ static int fscrypt_slot_try_many(
 }
 
 static int fscrypt_setup(
-                char **pkcs11_decrypted_passwords,
+                const PasswordCache *cache,
                 char **password,
                 HomeSetup *setup,
                 void **ret_volume_key,
@@ -230,6 +230,7 @@ static int fscrypt_setup(
                 _cleanup_free_ char *value = NULL;
                 size_t salt_size, encrypted_size;
                 const char *nr, *e;
+                char **list;
                 int n;
 
                 /* Check if this xattr has the format 'trusted.fscrypt_slot<nr>' where '<nr>' is a 32bit unsigned integer */
@@ -256,19 +257,17 @@ static int fscrypt_setup(
                 if (r < 0)
                         return log_error_errno(r, "Failed to decode encrypted key of %s: %m", xa);
 
-                r = fscrypt_slot_try_many(
-                                pkcs11_decrypted_passwords,
-                                salt, salt_size,
-                                encrypted, encrypted_size,
-                                setup->fscrypt_key_descriptor,
-                                ret_volume_key, ret_volume_key_size);
-                if (r == -ENOANO)
+                r = -ENOANO;
+                FOREACH_POINTER(list, cache->pkcs11_passwords, cache->fido2_passwords, password) {
                         r = fscrypt_slot_try_many(
-                                        password,
+                                        list,
                                         salt, salt_size,
                                         encrypted, encrypted_size,
                                         setup->fscrypt_key_descriptor,
                                         ret_volume_key, ret_volume_key_size);
+                        if (r != -ENOANO)
+                                break;
+                }
                 if (r < 0) {
                         if (r != -ENOANO)
                                 return r;
@@ -282,7 +281,7 @@ static int fscrypt_setup(
 int home_prepare_fscrypt(
                 UserRecord *h,
                 bool already_activated,
-                char ***pkcs11_decrypted_passwords,
+                PasswordCache *cache,
                 HomeSetup *setup) {
 
         _cleanup_(erase_and_freep) void *volume_key = NULL;
@@ -314,7 +313,7 @@ int home_prepare_fscrypt(
         memcpy(setup->fscrypt_key_descriptor, policy.master_key_descriptor, FS_KEY_DESCRIPTOR_SIZE);
 
         r = fscrypt_setup(
-                        pkcs11_decrypted_passwords ? *pkcs11_decrypted_passwords : NULL,
+                        cache,
                         h->password,
                         setup,
                         &volume_key,
@@ -584,7 +583,7 @@ int home_create_fscrypt(
 int home_passwd_fscrypt(
                 UserRecord *h,
                 HomeSetup *setup,
-                char **pkcs11_decrypted_passwords, /* the passwords acquired via PKCS#11 security tokens */
+                PasswordCache *cache,               /* the passwords acquired via PKCS#11/FIDO2 security tokens */
                 char **effective_passwords          /* new passwords */) {
 
         _cleanup_(erase_and_freep) void *volume_key = NULL;
@@ -600,7 +599,7 @@ int home_passwd_fscrypt(
         assert(setup);
 
         r = fscrypt_setup(
-                        pkcs11_decrypted_passwords,
+                        cache,
                         h->password,
                         setup,
                         &volume_key,
index aa3bcd3a69faa358222012d0563e35a5a7e08660..e5cf7baaaa456585ea50b0268fa1e8d3021a3734 100644 (file)
@@ -4,7 +4,7 @@
 #include "homework.h"
 #include "user-record.h"
 
-int home_prepare_fscrypt(UserRecord *h, bool already_activated, char ***pkcs11_decrypted_passwords, HomeSetup *setup);
+int home_prepare_fscrypt(UserRecord *h, bool already_activated, PasswordCache *cache, HomeSetup *setup);
 int home_create_fscrypt(UserRecord *h, char **effective_passwords, UserRecord **ret_home);
 
-int home_passwd_fscrypt(UserRecord *h, HomeSetup *setup, char **pkcs11_decrypted_passwords, char **effective_passwords);
+int home_passwd_fscrypt(UserRecord *h, HomeSetup *setup, PasswordCache *cache, char **effective_passwords);
index d731d0d64f0a43acf6517600335f43993c8c86ad..ba47688b0f187f940913bcedb59622eae5ba7757 100644 (file)
@@ -8,6 +8,7 @@
 
 #include "blkid-util.h"
 #include "blockdev-util.h"
+#include "btrfs-util.h"
 #include "chattr-util.h"
 #include "dm-util.h"
 #include "errno-util.h"
@@ -15,6 +16,7 @@
 #include "fileio.h"
 #include "fs-util.h"
 #include "fsck-util.h"
+#include "home-util.h"
 #include "homework-luks.h"
 #include "homework-mount.h"
 #include "id128-util.h"
  * strictly round disk sizes down to the next 1K boundary.*/
 #define DISK_SIZE_ROUND_DOWN(x) ((x) & ~UINT64_C(1023))
 
-static bool supported_fstype(const char *fstype) {
-        /* Limit the set of supported file systems a bit, as protection against little tested kernel file
-         * systems. Also, we only support the resize ioctls for these file systems. */
-        return STR_IN_SET(fstype, "ext4", "btrfs", "xfs");
-}
-
 static int probe_file_system_by_fd(
                 int fd,
                 char **ret_fstype,
@@ -220,7 +216,7 @@ static int luks_setup(
                 const char *cipher_mode,
                 uint64_t volume_key_size,
                 char **passwords,
-                char **pkcs11_decrypted_passwords,
+                const PasswordCache *cache,
                 bool discard,
                 struct crypt_device **ret,
                 sd_id128_t *ret_found_uuid,
@@ -231,6 +227,7 @@ static int luks_setup(
         _cleanup_(erase_and_freep) void *vk = NULL;
         sd_id128_t p;
         size_t vks;
+        char **list;
         int r;
 
         assert(node);
@@ -282,12 +279,14 @@ static int luks_setup(
         if (!vk)
                 return log_oom();
 
-        r = luks_try_passwords(cd, pkcs11_decrypted_passwords, vk, &vks);
-        if (r == -ENOKEY) {
-                r = luks_try_passwords(cd, passwords, vk, &vks);
-                if (r == -ENOKEY)
-                        return log_error_errno(r, "No valid password for LUKS superblock.");
+        r = -ENOKEY;
+        FOREACH_POINTER(list, cache->pkcs11_passwords, cache->fido2_passwords, passwords) {
+                r = luks_try_passwords(cd, list, vk, &vks);
+                if (r != -ENOKEY)
+                        break;
         }
+        if (r == -ENOKEY)
+                return log_error_errno(r, "No valid password for LUKS superblock.");
         if (r < 0)
                 return log_error_errno(r, "Failed to unlocks LUKS superblock: %m");
 
@@ -316,7 +315,7 @@ static int luks_setup(
 static int luks_open(
                 const char *dm_name,
                 char **passwords,
-                char **pkcs11_decrypted_passwords,
+                PasswordCache *cache,
                 struct crypt_device **ret,
                 sd_id128_t *ret_found_uuid,
                 void **ret_volume_key,
@@ -325,6 +324,7 @@ static int luks_open(
         _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
         _cleanup_(erase_and_freep) void *vk = NULL;
         sd_id128_t p;
+        char **list;
         size_t vks;
         int r;
 
@@ -365,12 +365,14 @@ static int luks_open(
         if (!vk)
                 return log_oom();
 
-        r = luks_try_passwords(cd, pkcs11_decrypted_passwords, vk, &vks);
-        if (r == -ENOKEY) {
-                r = luks_try_passwords(cd, passwords, vk, &vks);
-                if (r == -ENOKEY)
-                        return log_error_errno(r, "No valid password for LUKS superblock.");
+        r = -ENOKEY;
+        FOREACH_POINTER(list, cache->pkcs11_passwords, cache->fido2_passwords, passwords) {
+                r = luks_try_passwords(cd, list, vk, &vks);
+                if (r != -ENOKEY)
+                        break;
         }
+        if (r == -ENOKEY)
+                return log_error_errno(r, "No valid password for LUKS superblock.");
         if (r < 0)
                 return log_error_errno(r, "Failed to unlocks LUKS superblock: %m");
 
@@ -626,7 +628,7 @@ static int luks_validate_home_record(
                 struct crypt_device *cd,
                 UserRecord *h,
                 const void *volume_key,
-                char ***pkcs11_decrypted_passwords,
+                PasswordCache *cache,
                 UserRecord **ret_luks_home_record) {
 
         int r, token;
@@ -731,9 +733,10 @@ static int luks_validate_home_record(
                 if (!user_record_compatible(h, lhr))
                         return log_error_errno(SYNTHETIC_ERRNO(EREMCHG), "LUKS home record not compatible with host record, refusing.");
 
-                r = user_record_authenticate(lhr, h, pkcs11_decrypted_passwords);
+                r = user_record_authenticate(lhr, h, cache, /* strict_verify= */ true);
                 if (r < 0)
                         return r;
+                assert(r > 0); /* Insist that a password was verified */
 
                 *ret_luks_home_record = TAKE_PTR(lhr);
                 return 0;
@@ -892,19 +895,19 @@ int home_store_header_identity_luks(
         return 1;
 }
 
-static int run_fitrim(int root_fd) {
+int run_fitrim(int root_fd) {
         char buf[FORMAT_BYTES_MAX];
         struct fstrim_range range = {
                 .len = UINT64_MAX,
         };
 
         /* If discarding is on, discard everything right after mounting, so that the discard setting takes
-         * effect on activation. */
+         * effect on activation. (Also, optionally, trim on logout) */
 
         assert(root_fd >= 0);
 
         if (ioctl(root_fd, FITRIM, &range) < 0) {
-                if (IN_SET(errno, ENOTTY, EOPNOTSUPP, EBADF)) {
+                if (ERRNO_IS_NOT_SUPPORTED(errno) || errno == EBADF) {
                         log_debug_errno(errno, "File system does not support FITRIM, not trimming.");
                         return 0;
                 }
@@ -917,15 +920,32 @@ static int run_fitrim(int root_fd) {
         return 1;
 }
 
-static int run_fallocate(int backing_fd, const struct stat *st) {
+int run_fitrim_by_path(const char *root_path) {
+        _cleanup_close_ int root_fd = -1;
+
+        root_fd = open(root_path, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
+        if (root_fd < 0)
+                return log_error_errno(errno, "Failed to open file system '%s' for trimming: %m", root_path);
+
+        return run_fitrim(root_fd);
+}
+
+int run_fallocate(int backing_fd, const struct stat *st) {
         char buf[FORMAT_BYTES_MAX];
+        struct stat stbuf;
 
         assert(backing_fd >= 0);
-        assert(st);
 
         /* If discarding is off, let's allocate the whole image before mounting, so that the setting takes
          * effect on activation */
 
+        if (!st) {
+                if (fstat(backing_fd, &stbuf) < 0)
+                        return log_error_errno(errno, "Failed to fstat(): %m");
+
+                st = &stbuf;
+        }
+
         if (!S_ISREG(st->st_mode))
                 return 0;
 
@@ -954,11 +974,21 @@ static int run_fallocate(int backing_fd, const struct stat *st) {
         return 1;
 }
 
+int run_fallocate_by_path(const char *backing_path) {
+        _cleanup_close_ int backing_fd = -1;
+
+        backing_fd = open(backing_path, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
+        if (backing_fd < 0)
+                return log_error_errno(errno, "Failed to open '%s' for fallocate(): %m", backing_path);
+
+        return run_fallocate(backing_fd, NULL);
+}
+
 int home_prepare_luks(
                 UserRecord *h,
                 bool already_activated,
                 const char *force_image_path,
-                char ***pkcs11_decrypted_passwords,
+                PasswordCache *cache,
                 HomeSetup *setup,
                 UserRecord **ret_luks_home) {
 
@@ -986,7 +1016,7 @@ int home_prepare_luks(
 
                 r = luks_open(setup->dm_name,
                               h->password,
-                              pkcs11_decrypted_passwords ? *pkcs11_decrypted_passwords : NULL,
+                              cache,
                               &cd,
                               &found_luks_uuid,
                               &volume_key,
@@ -994,7 +1024,7 @@ int home_prepare_luks(
                 if (r < 0)
                         return r;
 
-                r = luks_validate_home_record(cd, h, volume_key, pkcs11_decrypted_passwords, &luks_home);
+                r = luks_validate_home_record(cd, h, volume_key, cache, &luks_home);
                 if (r < 0)
                         return r;
 
@@ -1109,8 +1139,8 @@ int home_prepare_luks(
                                h->luks_cipher_mode,
                                h->luks_volume_key_size,
                                h->password,
-                               pkcs11_decrypted_passwords ? *pkcs11_decrypted_passwords : NULL,
-                               user_record_luks_discard(h),
+                               cache,
+                               user_record_luks_discard(h) || user_record_luks_offline_discard(h),
                                &cd,
                                &found_luks_uuid,
                                &volume_key,
@@ -1120,7 +1150,7 @@ int home_prepare_luks(
 
                 dm_activated = true;
 
-                r = luks_validate_home_record(cd, h, volume_key, pkcs11_decrypted_passwords, &luks_home);
+                r = luks_validate_home_record(cd, h, volume_key, cache, &luks_home);
                 if (r < 0)
                         goto fail;
 
@@ -1132,7 +1162,7 @@ int home_prepare_luks(
                 if (r < 0)
                         goto fail;
 
-                r = home_unshare_and_mount(setup->dm_node, fstype, user_record_luks_discard(h));
+                r = home_unshare_and_mount(setup->dm_node, fstype, user_record_luks_discard(h), user_record_mount_flags(h));
                 if (r < 0)
                         goto fail;
 
@@ -1146,6 +1176,9 @@ int home_prepare_luks(
 
                 if (user_record_luks_discard(h))
                         (void) run_fitrim(root_fd);
+
+                setup->image_fd = TAKE_FD(fd);
+                setup->do_offline_fallocate = !(setup->do_offline_fitrim = user_record_luks_offline_discard(h));
         }
 
         setup->loop = TAKE_PTR(loop);
@@ -1191,7 +1224,7 @@ static void print_size_summary(uint64_t host_size, uint64_t encrypted_size, stru
 
 int home_activate_luks(
                 UserRecord *h,
-                char ***pkcs11_decrypted_passwords,
+                PasswordCache *cache,
                 UserRecord **ret_home) {
 
         _cleanup_(user_record_unrefp) UserRecord *new_home = NULL, *luks_home_record = NULL;
@@ -1223,7 +1256,7 @@ int home_activate_luks(
                         h,
                         false,
                         NULL,
-                        pkcs11_decrypted_passwords,
+                        cache,
                         &setup,
                         &luks_home_record);
         if (r < 0)
@@ -1241,7 +1274,7 @@ int home_activate_luks(
                         h,
                         &setup,
                         luks_home_record,
-                        pkcs11_decrypted_passwords,
+                        cache,
                         &sfs,
                         &new_home);
         if (r < 0)
@@ -1258,14 +1291,16 @@ int home_activate_luks(
                 return r;
 
         setup.undo_mount = false;
+        setup.do_offline_fitrim = false;
 
         loop_device_relinquish(setup.loop);
 
-        r = dm_deferred_remove(setup.dm_name);
+        r = crypt_deactivate_by_name(NULL, setup.dm_name, CRYPT_DEACTIVATE_DEFERRED);
         if (r < 0)
                 log_warning_errno(r, "Failed to relinquish DM device, ignoring: %m");
 
         setup.undo_dm = false;
+        setup.do_offline_fallocate = false;
 
         log_info("Everything completed.");
 
@@ -1278,6 +1313,7 @@ int home_activate_luks(
 int home_deactivate_luks(UserRecord *h) {
         _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
         _cleanup_free_ char *dm_name = NULL, *dm_node = NULL;
+        bool we_detached;
         int r;
 
         /* Note that the DM device and loopback device are set to auto-detach, hence strictly speaking we
@@ -1292,23 +1328,45 @@ int home_deactivate_luks(UserRecord *h) {
 
         r = crypt_init_by_name(&cd, dm_name);
         if (IN_SET(r, -ENODEV, -EINVAL, -ENOENT)) {
-                log_debug_errno(r, "LUKS device %s is already detached.", dm_name);
-                return false;
+                log_debug_errno(r, "LUKS device %s has already been detached.", dm_name);
+                we_detached = false;
         } else if (r < 0)
                 return log_error_errno(r, "Failed to initialize cryptsetup context for %s: %m", dm_name);
+        else {
+                log_info("Discovered used LUKS device %s.", dm_node);
+
+                crypt_set_log_callback(cd, cryptsetup_log_glue, NULL);
+
+                r = crypt_deactivate(cd, dm_name);
+                if (IN_SET(r, -ENODEV, -EINVAL, -ENOENT)) {
+                        log_debug_errno(r, "LUKS device %s is already detached.", dm_node);
+                        we_detached = false;
+                } else if (r < 0)
+                        return log_info_errno(r, "LUKS device %s couldn't be deactivated: %m", dm_node);
+                else {
+                        log_info("LUKS device detaching completed.");
+                        we_detached = true;
+                }
+        }
 
-        log_info("Discovered used LUKS device %s.", dm_node);
+        if (user_record_luks_offline_discard(h))
+                log_debug("Not allocating on logout.");
+        else
+                (void) run_fallocate_by_path(user_record_image_path(h));
 
-        crypt_set_log_callback(cd, cryptsetup_log_glue, NULL);
+        return we_detached;
+}
 
-        r = crypt_deactivate(cd, dm_name);
-        if (IN_SET(r, -ENODEV, -EINVAL, -ENOENT))
-                log_debug_errno(r, "LUKS device %s is already detached.", dm_node);
-        else if (r < 0)
-                return log_info_errno(r, "LUKS device %s couldn't be deactivated: %m", dm_node);
+int home_trim_luks(UserRecord *h) {
+        assert(h);
 
-        log_info("LUKS device detaching completed.");
-        return true;
+        if (!user_record_luks_offline_discard(h)) {
+                log_debug("Not trimming on logout.");
+                return 0;
+        }
+
+        (void) run_fitrim_by_path(user_record_home_directory(h));
+        return 0;
 }
 
 static int run_mkfs(
@@ -1412,7 +1470,7 @@ static int luks_format(
                 const char *dm_name,
                 sd_id128_t uuid,
                 const char *label,
-                char **pkcs11_decrypted_passwords,
+                const PasswordCache *cache,
                 char **effective_passwords,
                 bool discard,
                 UserRecord *hr,
@@ -1481,7 +1539,8 @@ static int luks_format(
 
         STRV_FOREACH(pp, effective_passwords) {
 
-                if (strv_contains(pkcs11_decrypted_passwords, *pp)) {
+                if (strv_contains(cache->pkcs11_passwords, *pp) ||
+                    strv_contains(cache->fido2_passwords, *pp)) {
                         log_debug("Using minimal PBKDF for slot %i", slot);
                         r = crypt_set_pbkdf_type(cd, &minimal_pbkdf);
                 } else {
@@ -1666,7 +1725,7 @@ static int wait_for_devlink(const char *path) {
         usec_t until;
         int r;
 
-        /* let's wait for a device link to show up in /dev, with a time-out. This is good to do since we
+        /* let's wait for a device link to show up in /dev, with a timeout. This is good to do since we
          * return a /dev/disk/by-uuid/… link to our callers and they likely want to access it right-away,
          * hence let's wait until udev has caught up with our changes, and wait for the symlink to be
          * created. */
@@ -1765,9 +1824,48 @@ static int calculate_disk_size(UserRecord *h, const char *parent_dir, uint64_t *
         return 0;
 }
 
+static int home_truncate(
+                UserRecord *h,
+                int fd,
+                const char *path,
+                uint64_t size) {
+
+        bool trunc;
+        int r;
+
+        assert(h);
+        assert(fd >= 0);
+        assert(path);
+
+        trunc = user_record_luks_discard(h);
+        if (!trunc) {
+                r = fallocate(fd, 0, 0, size);
+                if (r < 0 && ERRNO_IS_NOT_SUPPORTED(errno)) {
+                        /* Some file systems do not support fallocate(), let's gracefully degrade
+                         * (ZFS, reiserfs, …) and fall back to truncation */
+                        log_notice_errno(errno, "Backing file system does not support fallocate(), falling back to ftruncate(), i.e. implicitly using non-discard mode.");
+                        trunc = true;
+                }
+        }
+
+        if (trunc)
+                r = ftruncate(fd, size);
+
+        if (r < 0) {
+                if (ERRNO_IS_DISK_SPACE(errno)) {
+                        log_error_errno(errno, "Not enough disk space to allocate home.");
+                        return -ENOSPC; /* make recognizable */
+                }
+
+                return log_error_errno(errno, "Failed to truncate home image %s: %m", path);
+        }
+
+        return 0;
+}
+
 int home_create_luks(
                 UserRecord *h,
-                char **pkcs11_decrypted_passwords,
+                PasswordCache *cache,
                 char **effective_passwords,
                 UserRecord **ret_home) {
 
@@ -1878,7 +1976,9 @@ int home_create_luks(
                 if (asprintf(&disk_uuid_path, "/dev/disk/by-uuid/" SD_ID128_UUID_FORMAT_STR, SD_ID128_FORMAT_VAL(luks_uuid)) < 0)
                         return log_oom();
 
-                if (user_record_luks_discard(h)) {
+                if (user_record_luks_discard(h) || user_record_luks_offline_discard(h)) {
+                        /* If we want online or offline discard, discard once before we start using things. */
+
                         if (ioctl(image_fd, BLKDISCARD, (uint64_t[]) { 0, block_device_size }) < 0)
                                 log_full_errno(errno == EOPNOTSUPP ? LOG_DEBUG : LOG_WARNING, errno,
                                                "Failed to issue full-device BLKDISCARD on device, ignoring: %m");
@@ -1917,20 +2017,9 @@ int home_create_luks(
                 if (r < 0)
                         log_warning_errno(r, "Failed to set file attributes on %s, ignoring: %m", temporary_image_path);
 
-                if (user_record_luks_discard(h))
-                        r = ftruncate(image_fd, host_size);
-                else
-                        r = fallocate(image_fd, 0, 0, host_size);
-                if (r < 0) {
-                        if (ERRNO_IS_DISK_SPACE(errno)) {
-                                log_debug_errno(errno, "Not enough disk space to allocate home.");
-                                r = -ENOSPC; /* make recognizable */
-                                goto fail;
-                        }
-
-                        r = log_error_errno(errno, "Failed to truncate home image %s: %m", temporary_image_path);
+                r = home_truncate(h, image_fd, temporary_image_path, host_size);
+                if (r < 0)
                         goto fail;
-                }
 
                 log_info("Allocating image file completed.");
         }
@@ -1973,9 +2062,9 @@ int home_create_luks(
                         dm_name,
                         luks_uuid,
                         user_record_user_name_and_realm(h),
-                        pkcs11_decrypted_passwords,
+                        cache,
                         effective_passwords,
-                        user_record_luks_discard(h),
+                        user_record_luks_discard(h) || user_record_luks_offline_discard(h),
                         h,
                         &cd);
         if (r < 0)
@@ -1997,7 +2086,7 @@ int home_create_luks(
 
         log_info("Formatting file system completed.");
 
-        r = home_unshare_and_mount(dm_node, fstype, user_record_luks_discard(h));
+        r = home_unshare_and_mount(dm_node, fstype, user_record_luks_discard(h), user_record_mount_flags(h));
         if (r < 0)
                 goto fail;
 
@@ -2009,8 +2098,10 @@ int home_create_luks(
                 goto fail;
         }
 
-        if (mkdir(subdir, 0700) < 0) {
-                r = log_error_errno(errno, "Failed to create user directory in mounted image file: %m");
+        /* Prefer using a btrfs subvolume if we can, fall back to directory otherwise */
+        r = btrfs_subvol_make_fallback(subdir, 0700);
+        if (r < 0) {
+                log_error_errno(r, "Failed to create user directory in mounted image file: %m");
                 goto fail;
         }
 
@@ -2053,6 +2144,12 @@ int home_create_luks(
                 goto fail;
         }
 
+        if (user_record_luks_offline_discard(h)) {
+                r = run_fitrim(root_fd);
+                if (r < 0)
+                        goto fail;
+        }
+
         root_fd = safe_close(root_fd);
 
         r = umount_verbose("/run/systemd/user-home-mount");
@@ -2071,6 +2168,12 @@ int home_create_luks(
 
         loop = loop_device_unref(loop);
 
+        if (!user_record_luks_offline_discard(h)) {
+                r = run_fallocate(image_fd, NULL /* refresh stat() data */);
+                if (r < 0)
+                        goto fail;
+        }
+
         if (disk_uuid_path)
                 (void) ioctl(image_fd, BLKRRPART, 0);
 
@@ -2188,7 +2291,7 @@ static int can_resize_fs(int fd, uint64_t old_size, uint64_t new_size) {
         return CAN_RESIZE_ONLINE;
 }
 
-static int ext4_offline_resize_fs(HomeSetup *setup, uint64_t new_size, bool discard) {
+static int ext4_offline_resize_fs(HomeSetup *setup, uint64_t new_size, bool discard, unsigned long flags) {
         _cleanup_free_ char *size_str = NULL;
         bool re_open = false, re_mount = false;
         pid_t resize_pid, fsck_pid;
@@ -2258,7 +2361,7 @@ static int ext4_offline_resize_fs(HomeSetup *setup, uint64_t new_size, bool disc
 
         /* Re-establish mounts and reopen the directory */
         if (re_mount) {
-                r = home_mount_node(setup->dm_node, "ext4", discard);
+                r = home_mount_node(setup->dm_node, "ext4", discard, flags);
                 if (r < 0)
                         return r;
 
@@ -2465,7 +2568,7 @@ static int apply_resize_partition(int fd, sd_id128_t disk_uuids, struct fdisk_ta
 int home_resize_luks(
                 UserRecord *h,
                 bool already_activated,
-                char ***pkcs11_decrypted_passwords,
+                PasswordCache *cache,
                 HomeSetup *setup,
                 UserRecord **ret_home) {
 
@@ -2551,11 +2654,11 @@ int home_resize_luks(
                 }
         }
 
-        r = home_prepare_luks(h, already_activated, whole_disk, pkcs11_decrypted_passwords, setup, &header_home);
+        r = home_prepare_luks(h, already_activated, whole_disk, cache, setup, &header_home);
         if (r < 0)
                 return r;
 
-        r = home_load_embedded_identity(h, setup->root_fd, header_home, USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL, pkcs11_decrypted_passwords, &embedded_home, &new_home);
+        r = home_load_embedded_identity(h, setup->root_fd, header_home, USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL, cache, &embedded_home, &new_home);
         if (r < 0)
                 return r;
 
@@ -2625,19 +2728,9 @@ int home_resize_luks(
 
                 if (S_ISREG(st.st_mode)) {
                         /* Grow file size */
-
-                        if (user_record_luks_discard(h))
-                                r = ftruncate(image_fd, new_image_size);
-                        else
-                                r = fallocate(image_fd, 0, 0, new_image_size);
-                        if (r < 0) {
-                                if (ERRNO_IS_DISK_SPACE(errno)) {
-                                        log_debug_errno(errno, "Not enough disk space to grow home.");
-                                        return -ENOSPC; /* make recognizable */
-                                }
-
-                                return log_error_errno(errno, "Failed to grow image file %s: %m", ip);
-                        }
+                        r = home_truncate(h, image_fd, ip, new_image_size);
+                        if (r < 0)
+                                return r;
 
                         log_info("Growing of image file completed.");
                 }
@@ -2688,7 +2781,7 @@ int home_resize_luks(
         if (resize_type == CAN_RESIZE_ONLINE)
                 r = resize_fs(setup->root_fd, new_fs_size, NULL);
         else
-                r = ext4_offline_resize_fs(setup, new_fs_size, user_record_luks_discard(h));
+                r = ext4_offline_resize_fs(setup, new_fs_size, user_record_luks_discard(h), user_record_mount_flags(h));
         if (r < 0)
                 return log_error_errno(r, "Failed to resize file system: %m");
 
@@ -2769,13 +2862,14 @@ int home_resize_luks(
 int home_passwd_luks(
                 UserRecord *h,
                 HomeSetup *setup,
-                char **pkcs11_decrypted_passwords, /* the passwords acquired via PKCS#11 security tokens */
-                char **effective_passwords         /* new passwords */) {
+                PasswordCache *cache,      /* the passwords acquired via PKCS#11/FIDO2 security tokens */
+                char **effective_passwords /* new passwords */) {
 
         size_t volume_key_size, i, max_key_slots, n_effective;
         _cleanup_(erase_and_freep) void *volume_key = NULL;
         struct crypt_pbkdf_type good_pbkdf, minimal_pbkdf;
         const char *type;
+        char **list;
         int r;
 
         assert(h);
@@ -2800,12 +2894,14 @@ int home_passwd_luks(
         if (!volume_key)
                 return log_oom();
 
-        r = luks_try_passwords(setup->crypt_device, pkcs11_decrypted_passwords, volume_key, &volume_key_size);
-        if (r == -ENOKEY) {
-                r = luks_try_passwords(setup->crypt_device, h->password, volume_key, &volume_key_size);
-                if (r == -ENOKEY)
-                        return log_error_errno(SYNTHETIC_ERRNO(ENOKEY), "Failed to unlock LUKS superblock with supplied passwords.");
+        r = -ENOKEY;
+        FOREACH_POINTER(list, cache->pkcs11_passwords, cache->fido2_passwords, h->password) {
+                r = luks_try_passwords(setup->crypt_device, list, volume_key, &volume_key_size);
+                if (r != -ENOKEY)
+                        break;
         }
+        if (r == -ENOKEY)
+                return log_error_errno(SYNTHETIC_ERRNO(ENOKEY), "Failed to unlock LUKS superblock with supplied passwords.");
         if (r < 0)
                 return log_error_errno(r, "Failed to unlocks LUKS superblock: %m");
 
@@ -2825,7 +2921,8 @@ int home_passwd_luks(
                         continue;
                 }
 
-                if (strv_find(pkcs11_decrypted_passwords, effective_passwords[i])) {
+                if (strv_contains(cache->pkcs11_passwords, effective_passwords[i]) ||
+                    strv_contains(cache->fido2_passwords, effective_passwords[i])) {
                         log_debug("Using minimal PBKDF for slot %zu", i);
                         r = crypt_set_pbkdf_type(setup->crypt_device, &minimal_pbkdf);
                 } else {
@@ -2922,9 +3019,10 @@ static int luks_try_resume(
         return -ENOKEY;
 }
 
-int home_unlock_luks(UserRecord *h, char ***pkcs11_decrypted_passwords) {
+int home_unlock_luks(UserRecord *h, PasswordCache *cache) {
         _cleanup_free_ char *dm_name = NULL, *dm_node = NULL;
         _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
+        char **list;
         int r;
 
         assert(h);
@@ -2940,12 +3038,14 @@ int home_unlock_luks(UserRecord *h, char ***pkcs11_decrypted_passwords) {
         log_info("Discovered used LUKS device %s.", dm_node);
         crypt_set_log_callback(cd, cryptsetup_log_glue, NULL);
 
-        r = luks_try_resume(cd, dm_name, pkcs11_decrypted_passwords ? *pkcs11_decrypted_passwords : NULL);
-        if (r == -ENOKEY) {
-                r = luks_try_resume(cd, dm_name, h->password);
-                if (r == -ENOKEY)
-                        return log_error_errno(r, "No valid password for LUKS superblock.");
+        r = -ENOKEY;
+        FOREACH_POINTER(list, cache->pkcs11_passwords, cache->fido2_passwords, h->password) {
+                r = luks_try_resume(cd, dm_name, list);
+                if (r != -ENOKEY)
+                        break;
         }
+        if (r == -ENOKEY)
+                return log_error_errno(r, "No valid password for LUKS superblock.");
         if (r < 0)
                 return log_error_errno(r, "Failed to resume LUKS superblock: %m");
 
index 581255a223aba392168fdfe221821143f632a4fe..b51f1ad7a01d07f747ed67b9f6d1ad86c8c359f7 100644 (file)
@@ -5,23 +5,24 @@
 #include "homework.h"
 #include "user-record.h"
 
-int home_prepare_luks(UserRecord *h, bool already_activated, const char *force_image_path, char ***pkcs11_decrypted_passwords, HomeSetup *setup, UserRecord **ret_luks_home);
+int home_prepare_luks(UserRecord *h, bool already_activated, const char *force_image_path, PasswordCache *cache, HomeSetup *setup, UserRecord **ret_luks_home);
 
-int home_activate_luks(UserRecord *h, char ***pkcs11_decrypted_passwords, UserRecord **ret_home);
+int home_activate_luks(UserRecord *h, PasswordCache *cache, UserRecord **ret_home);
 int home_deactivate_luks(UserRecord *h);
+int home_trim_luks(UserRecord *h);
 
 int home_store_header_identity_luks(UserRecord *h, HomeSetup *setup, UserRecord *old_home);
 
-int home_create_luks(UserRecord *h, char **pkcs11_decrypted_passwords, char **effective_passwords, UserRecord **ret_home);
+int home_create_luks(UserRecord *h, PasswordCache *cache, char **effective_passwords, UserRecord **ret_home);
 
 int home_validate_update_luks(UserRecord *h, HomeSetup *setup);
 
-int home_resize_luks(UserRecord *h, bool already_activated, char ***pkcs11_decrypted_passwords, HomeSetup *setup, UserRecord **ret_home);
+int home_resize_luks(UserRecord *h, bool already_activated, PasswordCache *cache, HomeSetup *setup, UserRecord **ret_home);
 
-int home_passwd_luks(UserRecord *h, HomeSetup *setup, char **pkcs11_decrypted_passwords, char **effective_passwords);
+int home_passwd_luks(UserRecord *h, HomeSetup *setup, PasswordCache *cache, char **effective_passwords);
 
 int home_lock_luks(UserRecord *h);
-int home_unlock_luks(UserRecord *h, char ***pkcs11_decrypted_passwords);
+int home_unlock_luks(UserRecord *h, PasswordCache *cache);
 
 static inline uint64_t luks_volume_key_size_convert(struct crypt_device *cd) {
         int k;
@@ -36,3 +37,8 @@ static inline uint64_t luks_volume_key_size_convert(struct crypt_device *cd) {
 
         return (uint64_t) k;
 }
+
+int run_fitrim(int root_fd);
+int run_fitrim_by_path(const char *root_path);
+int run_fallocate(int backing_fd, const struct stat *st);
+int run_fallocate_by_path(const char *backing_path);
index 9e1116840de2f6a789adf92c28960f38f3b9f150..51c0a3864945190b0a8a1741b43a735f295fc880 100644 (file)
@@ -20,7 +20,7 @@ static const char *mount_options_for_fstype(const char *fstype) {
         return NULL;
 }
 
-int home_mount_node(const char *node, const char *fstype, bool discard) {
+int home_mount_node(const char *node, const char *fstype, bool discard, unsigned long flags) {
         _cleanup_free_ char *joined = NULL;
         const char *options, *discard_option;
         int r;
@@ -38,7 +38,7 @@ int home_mount_node(const char *node, const char *fstype, bool discard) {
         } else
                 options = discard_option;
 
-        r = mount_verbose(LOG_ERR, node, "/run/systemd/user-home-mount", fstype, MS_NODEV|MS_NOSUID|MS_RELATIME, strempty(options));
+        r = mount_verbose(LOG_ERR, node, "/run/systemd/user-home-mount", fstype, flags|MS_RELATIME, strempty(options));
         if (r < 0)
                 return r;
 
@@ -46,7 +46,7 @@ int home_mount_node(const char *node, const char *fstype, bool discard) {
         return 0;
 }
 
-int home_unshare_and_mount(const char *node, const char *fstype, bool discard) {
+int home_unshare_and_mount(const char *node, const char *fstype, bool discard, unsigned long flags) {
         int r;
 
         if (unshare(CLONE_NEWNS) < 0)
@@ -59,7 +59,7 @@ int home_unshare_and_mount(const char *node, const char *fstype, bool discard) {
         (void) mkdir_p("/run/systemd/user-home-mount", 0700);
 
         if (node)
-                return home_mount_node(node, fstype, discard);
+                return home_mount_node(node, fstype, discard, flags);
 
         return 0;
 }
index d926756f7bc9886f074a82e0121fe61ee3ce194c..cf7c8cfcab4bd848ed98d6c71d4115cd479c76e1 100644 (file)
@@ -3,6 +3,6 @@
 
 #include <stdbool.h>
 
-int home_mount_node(const char *node, const char *fstype, bool discard);
-int home_unshare_and_mount(const char *node, const char *fstype, bool discard);
+int home_mount_node(const char *node, const char *fstype, bool discard, unsigned long flags);
+int home_unshare_and_mount(const char *node, const char *fstype, bool discard, unsigned long flags);
 int home_move_mount(const char *user_name_and_realm, const char *target);
index 915bc0e57ec8880af77e029bfe9d866fffa12b41..3a03fb7200d1b6cc2bcc266d049181f546469837 100644 (file)
@@ -62,10 +62,10 @@ int pkcs11_callback(
                 goto decrypt;
         }
 
-        if (strv_isempty(data->secret->pkcs11_pin))
-                return log_error_errno(SYNTHETIC_ERRNO(ENOANO), "Security Token requires PIN.");
+        if (strv_isempty(data->secret->token_pin))
+                return log_error_errno(SYNTHETIC_ERRNO(ENOANO), "Security token requires PIN.");
 
-        STRV_FOREACH(i, data->secret->pkcs11_pin) {
+        STRV_FOREACH(i, data->secret->token_pin) {
                 rv = m->C_Login(session, CKU_USER, (CK_UTF8CHAR*) *i, strlen(*i));
                 if (rv == CKR_OK) {
                         log_info("Successfully logged into security token '%s' with PIN.", token_label);
index cd38d438bceb971ca0d45f5ace97669de8407520..83bd875d2d6f39529b942465005896298ca516a0 100644 (file)
@@ -7,9 +7,11 @@
 #include "copy.h"
 #include "fd-util.h"
 #include "fileio.h"
+#include "fs-util.h"
 #include "home-util.h"
 #include "homework-cifs.h"
 #include "homework-directory.h"
+#include "homework-fido2.h"
 #include "homework-fscrypt.h"
 #include "homework-luks.h"
 #include "homework-mount.h"
@@ -20,7 +22,6 @@
 #include "missing_magic.h"
 #include "mount-util.h"
 #include "path-util.h"
-#include "pkcs11-util.h"
 #include "rm-rf.h"
 #include "stat-util.h"
 #include "strv.h"
 /* Make sure a bad password always results in a 3s delay, no matter what */
 #define BAD_PASSWORD_DELAY_USEC (3 * USEC_PER_SEC)
 
+void password_cache_free(PasswordCache *cache) {
+        if (!cache)
+                return;
+
+        cache->pkcs11_passwords = strv_free_erase(cache->pkcs11_passwords);
+        cache->fido2_passwords = strv_free_erase(cache->fido2_passwords);
+}
+
 int user_record_authenticate(
                 UserRecord *h,
                 UserRecord *secret,
-                char ***pkcs11_decrypted_passwords) {
+                PasswordCache *cache,
+                bool strict_verify) {
 
-        bool need_password = false, need_token = false, need_pin = false, need_protected_authentication_path_permitted = false,
-                pin_locked = false, pin_incorrect = false, pin_incorrect_few_tries_left = false, pin_incorrect_one_try_left = false;
-        size_t n;
+        bool need_password = false, need_token = false, need_pin = false, need_protected_authentication_path_permitted = false, need_user_presence_permitted = false,
+                pin_locked = false, pin_incorrect = false, pin_incorrect_few_tries_left = false, pin_incorrect_one_try_left = false, token_action_timeout = false;
         int r;
 
         assert(h);
@@ -46,14 +55,14 @@ int user_record_authenticate(
 
         /* Tries to authenticate a user record with the supplied secrets. i.e. checks whether at least one
          * supplied plaintext passwords matches a hashed password field of the user record. Or if a
-         * configured PKCS#11 token is around and can unlock the record.
+         * configured PKCS#11 or FIDO2 token is around and can unlock the record.
          *
-         * Note that the pkcs11_decrypted_passwords parameter is both an input and and output parameter: it
-         * is a list of configured, decrypted PKCS#11 passwords. We typically have to call this function
-         * multiple times over the course of an operation (think: on login we authenticate the host user
-         * record, the record embedded in the LUKS record and the one embedded in $HOME). Hence we keep a
-         * list of passwords we already decrypted, so that we don't have to do the (slow an potentially
-         * interactive) PKCS#11 dance for the relevant token again and again. */
+         * Note that the 'cache' parameter is both an input and output parameter: it contains lists of
+         * configured, decrypted PKCS#11/FIDO2 passwords. We typically have to call this function multiple
+         * times over the course of an operation (think: on login we authenticate the host user record, the
+         * record embedded in the LUKS record and the one embedded in $HOME). Hence we keep a list of
+         * passwords we already decrypted, so that we don't have to do the (slow and potentially interactive)
+         * PKCS#11/FIDO2 dance for the relevant token again and again. */
 
         /* First, let's see if the supplied plain-text passwords work? */
         r = user_record_test_secret(h, secret);
@@ -66,29 +75,48 @@ int user_record_authenticate(
                 return log_error_errno(r, "Failed to validate password of record: %m");
         else {
                 log_info("Provided password unlocks user record.");
-                return 0;
+                return 1;
         }
 
-        /* Second, let's see if any of the PKCS#11 security tokens are plugged in and help us */
-        for (n = 0; n < h->n_pkcs11_encrypted_key; n++) {
-#if HAVE_P11KIT
-                _cleanup_(pkcs11_callback_data_release) struct pkcs11_callback_data data = {
-                        .user_record = h,
-                        .secret = secret,
-                        .encrypted_key = h->pkcs11_encrypted_key + n,
-                };
+        /* Second, test cached PKCS#11 passwords */
+        for (size_t n = 0; n < h->n_pkcs11_encrypted_key; n++) {
                 char **pp;
 
-                /* See if any of the previously calculated passwords work */
-                STRV_FOREACH(pp, *pkcs11_decrypted_passwords) {
-                        r = test_password_one(data.encrypted_key->hashed_password, *pp);
+                STRV_FOREACH(pp, cache->pkcs11_passwords) {
+                        r = test_password_one(h->pkcs11_encrypted_key[n].hashed_password, *pp);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to check supplied PKCS#11 password: %m");
                         if (r > 0) {
                                 log_info("Previously acquired PKCS#11 password unlocks user record.");
+                                return 1;
+                        }
+                }
+        }
+
+        /* Third, test cached FIDO2 passwords */
+        for (size_t n = 0; n < h->n_fido2_hmac_salt; n++) {
+                char **pp;
+
+                /* See if any of the previously calculated passwords work */
+                STRV_FOREACH(pp, cache->fido2_passwords) {
+                        r = test_password_one(h->fido2_hmac_salt[n].hashed_password, *pp);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to check supplied FIDO2 password: %m");
+                        if (r > 0) {
+                                log_info("Previously acquired FIDO2 password unlocks user record.");
                                 return 0;
                         }
                 }
+        }
+
+        /* Fourth, let's see if any of the PKCS#11 security tokens are plugged in and help us */
+        for (size_t n = 0; n < h->n_pkcs11_encrypted_key; n++) {
+#if HAVE_P11KIT
+                _cleanup_(pkcs11_callback_data_release) struct pkcs11_callback_data data = {
+                        .user_record = h,
+                        .secret = secret,
+                        .encrypted_key = h->pkcs11_encrypted_key + n,
+                };
 
                 r = pkcs11_find_token(data.encrypted_key->uri, pkcs11_callback, &data);
                 switch (r) {
@@ -125,7 +153,7 @@ int user_record_authenticate(
 
                         log_info("Decrypted password from PKCS#11 security token %s unlocks user record.", data.encrypted_key->uri);
 
-                        r = strv_extend(pkcs11_decrypted_passwords, data.decrypted_password);
+                        r = strv_extend(&cache->pkcs11_passwords, data.decrypted_password);
                         if (r < 0)
                                 return log_oom();
 
@@ -137,6 +165,55 @@ int user_record_authenticate(
 #endif
         }
 
+        /* Fifth, let's see if any of the FIDO2 security tokens are plugged in and help us */
+        for (size_t n = 0; n < h->n_fido2_hmac_salt; n++) {
+#if HAVE_LIBFIDO2
+                _cleanup_(erase_and_freep) char *decrypted_password = NULL;
+
+                r = fido2_use_token(h, secret, h->fido2_hmac_salt + n, &decrypted_password);
+                switch (r) {
+                case -EAGAIN:
+                        need_token = true;
+                        break;
+                case -ENOANO:
+                        need_pin = true;
+                        break;
+                case -EOWNERDEAD:
+                        pin_locked = true;
+                        break;
+                case -ENOLCK:
+                        pin_incorrect = true;
+                        break;
+                case -EMEDIUMTYPE:
+                        need_user_presence_permitted = true;
+                        break;
+                case -ENOSTR:
+                        token_action_timeout = true;
+                        break;
+                default:
+                        if (r < 0)
+                                return r;
+
+                        r = test_password_one(h->fido2_hmac_salt[n].hashed_password, decrypted_password);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to test FIDO2 password: %m");
+                        if (r == 0)
+                                return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Configured FIDO2 security token does not decrypt encrypted key correctly.");
+
+                        log_info("Decrypted password from FIDO2 security token unlocks user record.");
+
+                        r = strv_extend(&cache->fido2_passwords, decrypted_password);
+                        if (r < 0)
+                                return log_oom();
+
+                        return 1;
+                }
+#else
+                need_token = true;
+                break;
+#endif
+        }
+
         /* Ordered by "relevance", i.e. the most "important" or "interesting" error condition is returned. */
         if (pin_incorrect_one_try_left)
                 return -EUCLEAN;
@@ -146,8 +223,12 @@ int user_record_authenticate(
                 return -ENOLCK;
         if (pin_locked)
                 return -EOWNERDEAD;
+        if (token_action_timeout)
+                return -ENOSTR;
         if (need_protected_authentication_path_permitted)
                 return -ERFKILL;
+        if (need_user_presence_permitted)
+                return -EMEDIUMTYPE;
         if (need_pin)
                 return -ENOANO;
         if (need_token)
@@ -155,8 +236,20 @@ int user_record_authenticate(
         if (need_password)
                 return -ENOKEY;
 
-        /* Hmm, this means neither PCKS#11 nor classic hashed passwords were supplied, we cannot authenticate this reasonably */
-        return log_debug_errno(SYNTHETIC_ERRNO(EKEYREVOKED), "No hashed passwords and no PKCS#11 tokens defined, cannot authenticate user record.");
+        /* Hmm, this means neither PCKS#11/FIDO2 nor classic hashed passwords were supplied, we cannot
+         * authenticate this reasonably */
+        if (strict_verify)
+                return log_debug_errno(SYNTHETIC_ERRNO(EKEYREVOKED),
+                                       "No hashed passwords and no PKCS#11/FIDO2 tokens defined, cannot authenticate user record, refusing.");
+
+        /* If strict verification is off this means we are possibly in the case where we encountered an
+         * unfixated record, i.e. a synthetic one that accordingly lacks any authentication data. In this
+         * case, allow the authentication to pass for now, so that the second (or third) authentication level
+         * (the ones of the user record in the LUKS header or inside the home directory) will then catch
+         * invalid passwords. The second/third authentication always runs in strict verification mode. */
+        log_debug("No hashed passwords and no PKCS#11 tokens defined in record, cannot authenticate user record. "
+                  "Deferring to embedded user record.");
+        return 0;
 }
 
 int home_setup_undo(HomeSetup *setup) {
@@ -164,7 +257,15 @@ int home_setup_undo(HomeSetup *setup) {
 
         assert(setup);
 
-        setup->root_fd = safe_close(setup->root_fd);
+        if (setup->root_fd >= 0) {
+                if (setup->do_offline_fitrim) {
+                        q = run_fitrim(setup->root_fd);
+                        if (q < 0)
+                                r = q;
+                }
+
+                setup->root_fd = safe_close(setup->root_fd);
+        }
 
         if (setup->undo_mount) {
                 q = umount_verbose("/run/systemd/user-home-mount");
@@ -178,8 +279,20 @@ int home_setup_undo(HomeSetup *setup) {
                         r = q;
         }
 
+        if (setup->image_fd >= 0) {
+                if (setup->do_offline_fallocate) {
+                        q = run_fallocate(setup->image_fd, NULL);
+                        if (q < 0)
+                                r = q;
+                }
+
+                setup->image_fd = safe_close(setup->image_fd);
+        }
+
         setup->undo_mount = false;
         setup->undo_dm = false;
+        setup->do_offline_fitrim = false;
+        setup->do_offline_fallocate = false;
 
         setup->dm_name = mfree(setup->dm_name);
         setup->dm_node = mfree(setup->dm_node);
@@ -198,7 +311,7 @@ int home_setup_undo(HomeSetup *setup) {
 int home_prepare(
                 UserRecord *h,
                 bool already_activated,
-                char ***pkcs11_decrypted_passwords,
+                PasswordCache *cache,
                 HomeSetup *setup,
                 UserRecord **ret_header_home) {
 
@@ -217,7 +330,7 @@ int home_prepare(
         switch (user_record_storage(h)) {
 
         case USER_LUKS:
-                return home_prepare_luks(h, already_activated, NULL, pkcs11_decrypted_passwords, setup, ret_header_home);
+                return home_prepare_luks(h, already_activated, NULL, cache, setup, ret_header_home);
 
         case USER_SUBVOLUME:
         case USER_DIRECTORY:
@@ -225,7 +338,7 @@ int home_prepare(
                 break;
 
         case USER_FSCRYPT:
-                r = home_prepare_fscrypt(h, already_activated, pkcs11_decrypted_passwords, setup);
+                r = home_prepare_fscrypt(h, already_activated, cache, setup);
                 break;
 
         case USER_CIFS:
@@ -280,12 +393,10 @@ static int read_identity_file(int root_fd, JsonVariant **ret) {
         if (r < 0)
                 return log_error_errno(r, "Embedded identity file is not a regular file, refusing: %m");
 
-        identity_file = fdopen(identity_fd, "r");
+        identity_file = take_fdopen(&identity_fd, "r");
         if (!identity_file)
                 return log_oom();
 
-        identity_fd = -1;
-
         r = json_parse_file(identity_file, ".identity", JSON_PARSE_SENSITIVE, ret, &line, &column);
         if (r < 0)
                 return log_error_errno(r, "[.identity:%u:%u] Failed to parse JSON data: %m", line, column);
@@ -319,14 +430,12 @@ static int write_identity_file(int root_fd, JsonVariant *v, uid_t uid) {
         if (identity_fd < 0)
                 return log_error_errno(errno, "Failed to create .identity file in home directory: %m");
 
-        identity_file = fdopen(identity_fd, "w");
+        identity_file = take_fdopen(&identity_fd, "w");
         if (!identity_file) {
                 r = log_oom();
                 goto fail;
         }
 
-        identity_fd = -1;
-
         json_variant_dump(normalized, JSON_FORMAT_PRETTY, identity_file, NULL);
 
         r = fflush_and_check(identity_file);
@@ -359,7 +468,7 @@ int home_load_embedded_identity(
                 int root_fd,
                 UserRecord *header_home,
                 UserReconcileMode mode,
-                char ***pkcs11_decrypted_passwords,
+                PasswordCache *cache,
                 UserRecord **ret_embedded_home,
                 UserRecord **ret_new_home) {
 
@@ -386,9 +495,10 @@ int home_load_embedded_identity(
                 return log_error_errno(SYNTHETIC_ERRNO(EREMCHG), "Embedded home record not compatible with host record, refusing.");
 
         /* Insist that credentials the user supplies also unlocks any embedded records. */
-        r = user_record_authenticate(embedded_home, h, pkcs11_decrypted_passwords);
+        r = user_record_authenticate(embedded_home, h, cache, /* strict_verify= */ true);
         if (r < 0)
                 return r;
+        assert(r > 0); /* Insist that a password was verified */
 
         /* At this point we have three records to deal with:
          *
@@ -547,7 +657,7 @@ int home_refresh(
                 UserRecord *h,
                 HomeSetup *setup,
                 UserRecord *header_home,
-                char ***pkcs11_decrypted_passwords,
+                PasswordCache *cache,
                 struct statfs *ret_statfs,
                 UserRecord **ret_new_home) {
 
@@ -561,7 +671,7 @@ int home_refresh(
         /* When activating a home directory, does the identity work: loads the identity from the $HOME
          * directory, reconciles it with our idea, chown()s everything. */
 
-        r = home_load_embedded_identity(h, setup->root_fd, header_home, USER_RECONCILE_ANY, pkcs11_decrypted_passwords, &embedded_home, &new_home);
+        r = home_load_embedded_identity(h, setup->root_fd, header_home, USER_RECONCILE_ANY, cache, &embedded_home, &new_home);
         if (r < 0)
                 return r;
 
@@ -586,7 +696,7 @@ int home_refresh(
 }
 
 static int home_activate(UserRecord *h, UserRecord **ret_home) {
-        _cleanup_(strv_free_erasep) char **pkcs11_decrypted_passwords = NULL;
+        _cleanup_(password_cache_free) PasswordCache cache = {};
         _cleanup_(user_record_unrefp) UserRecord *new_home = NULL;
         int r;
 
@@ -599,7 +709,7 @@ static int home_activate(UserRecord *h, UserRecord **ret_home) {
         if (!IN_SET(user_record_storage(h), USER_LUKS, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT, USER_CIFS))
                 return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "Activating home directories of type '%s' currently not supported.", user_storage_to_string(user_record_storage(h)));
 
-        r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords);
+        r = user_record_authenticate(h, h, &cache, /* strict_verify= */ false);
         if (r < 0)
                 return r;
 
@@ -618,7 +728,7 @@ static int home_activate(UserRecord *h, UserRecord **ret_home) {
         switch (user_record_storage(h)) {
 
         case USER_LUKS:
-                r = home_activate_luks(h, &pkcs11_decrypted_passwords, &new_home);
+                r = home_activate_luks(h, &cache, &new_home);
                 if (r < 0)
                         return r;
 
@@ -627,14 +737,14 @@ static int home_activate(UserRecord *h, UserRecord **ret_home) {
         case USER_SUBVOLUME:
         case USER_DIRECTORY:
         case USER_FSCRYPT:
-                r = home_activate_directory(h, &pkcs11_decrypted_passwords, &new_home);
+                r = home_activate_directory(h, &cache, &new_home);
                 if (r < 0)
                         return r;
 
                 break;
 
         case USER_CIFS:
-                r = home_activate_cifs(h, &pkcs11_decrypted_passwords, &new_home);
+                r = home_activate_cifs(h, &cache, &new_home);
                 if (r < 0)
                         return r;
 
@@ -671,6 +781,12 @@ static int home_deactivate(UserRecord *h, bool force) {
         if (r < 0)
                 return r;
         if (r == USER_TEST_MOUNTED) {
+                if (user_record_storage(h) == USER_LUKS) {
+                        r = home_trim_luks(h);
+                        if (r < 0)
+                                return r;
+                }
+
                 if (umount2(user_record_home_directory(h), UMOUNT_NOFOLLOW | (force ? MNT_FORCE|MNT_DETACH : 0)) < 0)
                         return log_error_errno(errno, "Failed to unmount %s: %m", user_record_home_directory(h));
 
@@ -748,15 +864,16 @@ int home_populate(UserRecord *h, int dir_fd) {
 
 static int user_record_compile_effective_passwords(
                 UserRecord *h,
-                char ***ret_effective_passwords,
-                char ***ret_pkcs11_decrypted_passwords) {
+                PasswordCache *cache,
+                char ***ret_effective_passwords) {
 
-        _cleanup_(strv_free_erasep) char **effective = NULL, **pkcs11_passwords = NULL;
+        _cleanup_(strv_free_erasep) char **effective = NULL;
         size_t n;
         char **i;
         int r;
 
         assert(h);
+        assert(cache);
 
         /* We insist on at least one classic hashed password to be defined in addition to any PKCS#11 one, as
          * a safe fallback, but also to simplify the password changing algorithm: there we require providing
@@ -823,11 +940,37 @@ static int user_record_compile_effective_passwords(
                                 return log_oom();
                 }
 
-                if (ret_pkcs11_decrypted_passwords) {
-                        r = strv_extend(&pkcs11_passwords, data.decrypted_password);
+                r = strv_extend(&cache->pkcs11_passwords, data.decrypted_password);
+                if (r < 0)
+                        return log_oom();
+#else
+                return -EBADSLT;
+#endif
+        }
+
+        for (n = 0; n < h->n_fido2_hmac_salt; n++) {
+#if HAVE_LIBFIDO2
+                _cleanup_(erase_and_freep) char *decrypted_password = NULL;
+
+                r = fido2_use_token(h, h, h->fido2_hmac_salt + n, &decrypted_password);
+                if (r < 0)
+                        return r;
+
+                r = test_password_one(h->fido2_hmac_salt[n].hashed_password, decrypted_password);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to test FIDO2 password: %m");
+                if (r == 0)
+                        return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Decrypted password from token is not correct, refusing.");
+
+                if (ret_effective_passwords) {
+                        r = strv_extend(&effective, decrypted_password);
                         if (r < 0)
                                 return log_oom();
                 }
+
+                r = strv_extend(&cache->fido2_passwords, decrypted_password);
+                if (r < 0)
+                        return log_oom();
 #else
                 return -EBADSLT;
 #endif
@@ -835,15 +978,73 @@ static int user_record_compile_effective_passwords(
 
         if (ret_effective_passwords)
                 *ret_effective_passwords = TAKE_PTR(effective);
-        if (ret_pkcs11_decrypted_passwords)
-                *ret_pkcs11_decrypted_passwords = TAKE_PTR(pkcs11_passwords);
+
+        return 0;
+}
+
+static int determine_default_storage(UserStorage *ret) {
+        UserStorage storage = _USER_STORAGE_INVALID;
+        const char *e;
+        int r;
+
+        assert(ret);
+
+        /* homed tells us via an environment variable which default storage to use */
+        e = getenv("SYSTEMD_HOME_DEFAULT_STORAGE");
+        if (e) {
+                storage = user_storage_from_string(e);
+                if (storage < 0)
+                        log_warning("$SYSTEMD_HOME_DEFAULT_STORAGE set to invalid storage type, ignoring: %s", e);
+                else {
+                        log_info("Using configured default storage '%s'.", user_storage_to_string(storage));
+                        *ret = storage;
+                        return 0;
+                }
+        }
+
+        /* When neither user nor admin specified the storage type to use, fix it to be LUKS — unless we run
+         * in a container where loopback devices and LUKS/DM are not available. Also, if /home is encrypted
+         * anyway, let's avoid duplicate encryption. Note that we typically default to the assumption of
+         * "classic" storage for most operations. However, if we create a new home, then let's user LUKS if
+         * nothing is specified. */
+
+        r = detect_container();
+        if (r < 0)
+                return log_error_errno(r, "Failed to determine whether we are in a container: %m");
+        if (r == 0) {
+                r = path_is_encrypted("/home");
+                if (r < 0)
+                        log_warning_errno(r, "Failed to determine if /home is encrypted, ignoring: %m");
+                if (r <= 0) {
+                        log_info("Using automatic default storage of '%s'.", user_storage_to_string(USER_LUKS));
+                        *ret = USER_LUKS;
+                        return 0;
+                }
+
+                log_info("/home is encrypted, not using '%s' storage, in order to avoid double encryption.", user_storage_to_string(USER_LUKS));
+        } else
+                log_info("Running in container, not using '%s' storage.", user_storage_to_string(USER_LUKS));
+
+        r = path_is_fs_type("/home", BTRFS_SUPER_MAGIC);
+        if (r < 0)
+                log_warning_errno(r, "Failed to determine file system of /home, ignoring: %m");
+        if (r > 0) {
+                log_info("/home is on btrfs, using '%s' as storage.", user_storage_to_string(USER_SUBVOLUME));
+                *ret = USER_SUBVOLUME;
+        } else {
+                log_info("/home is on simple file system, using '%s' as storage.", user_storage_to_string(USER_DIRECTORY));
+                *ret = USER_DIRECTORY;
+        }
 
         return 0;
 }
 
 static int home_create(UserRecord *h, UserRecord **ret_home) {
-        _cleanup_(strv_free_erasep) char **effective_passwords = NULL, **pkcs11_decrypted_passwords = NULL;
+        _cleanup_(strv_free_erasep) char **effective_passwords = NULL;
         _cleanup_(user_record_unrefp) UserRecord *new_home = NULL;
+        _cleanup_(password_cache_free) PasswordCache cache = {};
+        UserStorage new_storage = _USER_STORAGE_INVALID;
+        const char *new_fs = NULL;
         int r;
 
         assert(h);
@@ -853,7 +1054,7 @@ static int home_create(UserRecord *h, UserRecord **ret_home) {
         if (!uid_is_valid(h->uid))
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "User record lacks UID, refusing.");
 
-        r = user_record_compile_effective_passwords(h, &effective_passwords, &pkcs11_decrypted_passwords);
+        r = user_record_compile_effective_passwords(h, &cache, &effective_passwords);
         if (r < 0)
                 return r;
 
@@ -863,27 +1064,18 @@ static int home_create(UserRecord *h, UserRecord **ret_home) {
         if (r != USER_TEST_ABSENT)
                 return log_error_errno(SYNTHETIC_ERRNO(EEXIST), "Home directory %s already exists, refusing.", user_record_home_directory(h));
 
-        /* When the user didn't specify the storage type to use, fix it to be LUKS -- unless we run in a
-         * container where loopback devices and LUKS/DM are not available. Note that we typically default to
-         * the assumption of "classic" storage for most operations. However, if we create a new home, then
-         * let's user LUKS if nothing is specified. */
         if (h->storage < 0) {
-                UserStorage new_storage;
-
-                r = detect_container();
+                r = determine_default_storage(&new_storage);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to determine whether we are in a container: %m");
-                if (r > 0) {
-                        new_storage = USER_DIRECTORY;
-
-                        r = path_is_fs_type("/home", BTRFS_SUPER_MAGIC);
-                        if (r < 0)
-                                log_debug_errno(r, "Failed to determine file system of /home, ignoring: %m");
+                        return r;
+        }
 
-                        new_storage = r > 0 ? USER_SUBVOLUME : USER_DIRECTORY;
-                } else
-                        new_storage = USER_LUKS;
+        if ((h->storage == USER_LUKS ||
+             (h->storage < 0 && new_storage == USER_LUKS)) &&
+            !h->file_system_type)
+                new_fs = getenv("SYSTEMD_HOME_DEFAULT_FILE_SYSTEM_TYPE");
 
+        if (new_storage >= 0 || new_fs) {
                 r = user_record_add_binding(
                                 h,
                                 new_storage,
@@ -894,18 +1086,12 @@ static int home_create(UserRecord *h, UserRecord **ret_home) {
                                 NULL,
                                 NULL,
                                 UINT64_MAX,
-                                NULL,
+                                new_fs,
                                 NULL,
                                 UID_INVALID,
                                 GID_INVALID);
                 if (r < 0)
                         return log_error_errno(r, "Failed to change storage type to LUKS: %m");
-
-                if (!h->image_path_auto) {
-                        h->image_path_auto = strjoin("/home/", user_record_user_name_and_realm(h), new_storage == USER_LUKS ? ".home" : ".homedir");
-                        if (!h->image_path_auto)
-                                return log_oom();
-                }
         }
 
         r = user_record_test_image_path_and_warn(h);
@@ -917,7 +1103,7 @@ static int home_create(UserRecord *h, UserRecord **ret_home) {
         switch (user_record_storage(h)) {
 
         case USER_LUKS:
-                r = home_create_luks(h, pkcs11_decrypted_passwords, effective_passwords, &new_home);
+                r = home_create_luks(h, &cache, effective_passwords, &new_home);
                 break;
 
         case USER_DIRECTORY:
@@ -1043,10 +1229,9 @@ static int home_remove(UserRecord *h) {
 
         if (deleted)
                 log_info("Everything completed.");
-        else {
-                log_notice("Nothing to remove.");
-                return -EALREADY;
-        }
+        else
+                return log_notice_errno(SYNTHETIC_ERRNO(EALREADY),
+                                        "Nothing to remove.");
 
         return 0;
 }
@@ -1104,17 +1289,18 @@ static int home_validate_update(UserRecord *h, HomeSetup *setup) {
 
 static int home_update(UserRecord *h, UserRecord **ret) {
         _cleanup_(user_record_unrefp) UserRecord *new_home = NULL, *header_home = NULL, *embedded_home = NULL;
-        _cleanup_(strv_free_erasep) char **pkcs11_decrypted_passwords = NULL;
         _cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT;
+        _cleanup_(password_cache_free) PasswordCache cache = {};
         bool already_activated = false;
         int r;
 
         assert(h);
         assert(ret);
 
-        r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords);
+        r = user_record_authenticate(h, h, &cache, /* strict_verify= */ true);
         if (r < 0)
                 return r;
+        assert(r > 0); /* Insist that a password was verified */
 
         r = home_validate_update(h, &setup);
         if (r < 0)
@@ -1122,11 +1308,11 @@ static int home_update(UserRecord *h, UserRecord **ret) {
 
         already_activated = r > 0;
 
-        r = home_prepare(h, already_activated, &pkcs11_decrypted_passwords, &setup, &header_home);
+        r = home_prepare(h, already_activated, &cache, &setup, &header_home);
         if (r < 0)
                 return r;
 
-        r = home_load_embedded_identity(h, setup.root_fd, header_home, USER_RECONCILE_REQUIRE_NEWER, &pkcs11_decrypted_passwords, &embedded_home, &new_home);
+        r = home_load_embedded_identity(h, setup.root_fd, header_home, USER_RECONCILE_REQUIRE_NEWER, &cache, &embedded_home, &new_home);
         if (r < 0)
                 return r;
 
@@ -1158,7 +1344,7 @@ static int home_update(UserRecord *h, UserRecord **ret) {
 
 static int home_resize(UserRecord *h, UserRecord **ret) {
         _cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT;
-        _cleanup_(strv_free_erasep) char **pkcs11_decrypted_passwords = NULL;
+        _cleanup_(password_cache_free) PasswordCache cache = {};
         bool already_activated = false;
         int r;
 
@@ -1168,9 +1354,10 @@ static int home_resize(UserRecord *h, UserRecord **ret) {
         if (h->disk_size == UINT64_MAX)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No target size specified, refusing.");
 
-        r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords);
+        r = user_record_authenticate(h, h, &cache, /* strict_verify= */ true);
         if (r < 0)
                 return r;
+        assert(r > 0); /* Insist that a password was verified */
 
         r = home_validate_update(h, &setup);
         if (r < 0)
@@ -1181,12 +1368,12 @@ static int home_resize(UserRecord *h, UserRecord **ret) {
         switch (user_record_storage(h)) {
 
         case USER_LUKS:
-                return home_resize_luks(h, already_activated, &pkcs11_decrypted_passwords, &setup, ret);
+                return home_resize_luks(h, already_activated, &cache, &setup, ret);
 
         case USER_DIRECTORY:
         case USER_SUBVOLUME:
         case USER_FSCRYPT:
-                return home_resize_directory(h, already_activated, &pkcs11_decrypted_passwords, &setup, ret);
+                return home_resize_directory(h, already_activated, &cache, &setup, ret);
 
         default:
                 return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "Resizing home directories of type '%s' currently not supported.", user_storage_to_string(user_record_storage(h)));
@@ -1195,8 +1382,9 @@ static int home_resize(UserRecord *h, UserRecord **ret) {
 
 static int home_passwd(UserRecord *h, UserRecord **ret_home) {
         _cleanup_(user_record_unrefp) UserRecord *header_home = NULL, *embedded_home = NULL, *new_home = NULL;
-        _cleanup_(strv_free_erasep) char **effective_passwords = NULL, **pkcs11_decrypted_passwords = NULL;
+        _cleanup_(strv_free_erasep) char **effective_passwords = NULL;
         _cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT;
+        _cleanup_(password_cache_free) PasswordCache cache = {};
         bool already_activated = false;
         int r;
 
@@ -1206,7 +1394,7 @@ static int home_passwd(UserRecord *h, UserRecord **ret_home) {
         if (!IN_SET(user_record_storage(h), USER_LUKS, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT))
                 return log_error_errno(SYNTHETIC_ERRNO(ENOTTY), "Changing password of home directories of type '%s' currently not supported.", user_storage_to_string(user_record_storage(h)));
 
-        r = user_record_compile_effective_passwords(h, &effective_passwords, &pkcs11_decrypted_passwords);
+        r = user_record_compile_effective_passwords(h, &cache, &effective_passwords);
         if (r < 0)
                 return r;
 
@@ -1216,24 +1404,24 @@ static int home_passwd(UserRecord *h, UserRecord **ret_home) {
 
         already_activated = r > 0;
 
-        r = home_prepare(h, already_activated, &pkcs11_decrypted_passwords, &setup, &header_home);
+        r = home_prepare(h, already_activated, &cache, &setup, &header_home);
         if (r < 0)
                 return r;
 
-        r = home_load_embedded_identity(h, setup.root_fd, header_home, USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL, &pkcs11_decrypted_passwords, &embedded_home, &new_home);
+        r = home_load_embedded_identity(h, setup.root_fd, header_home, USER_RECONCILE_REQUIRE_NEWER_OR_EQUAL, &cache, &embedded_home, &new_home);
         if (r < 0)
                 return r;
 
         switch (user_record_storage(h)) {
 
         case USER_LUKS:
-                r = home_passwd_luks(h, &setup, pkcs11_decrypted_passwords, effective_passwords);
+                r = home_passwd_luks(h, &setup, &cache, effective_passwords);
                 if (r < 0)
                         return r;
                 break;
 
         case USER_FSCRYPT:
-                r = home_passwd_fscrypt(h, &setup, pkcs11_decrypted_passwords, effective_passwords);
+                r = home_passwd_fscrypt(h, &setup, &cache, effective_passwords);
                 if (r < 0)
                         return r;
                 break;
@@ -1271,14 +1459,14 @@ static int home_passwd(UserRecord *h, UserRecord **ret_home) {
 static int home_inspect(UserRecord *h, UserRecord **ret_home) {
         _cleanup_(user_record_unrefp) UserRecord *header_home = NULL, *new_home = NULL;
         _cleanup_(home_setup_undo) HomeSetup setup = HOME_SETUP_INIT;
-        _cleanup_(strv_free_erasep) char **pkcs11_decrypted_passwords = NULL;
+        _cleanup_(password_cache_free) PasswordCache cache = {};
         bool already_activated = false;
         int r;
 
         assert(h);
         assert(ret_home);
 
-        r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords);
+        r = user_record_authenticate(h, h, &cache, /* strict_verify= */ false);
         if (r < 0)
                 return r;
 
@@ -1288,11 +1476,11 @@ static int home_inspect(UserRecord *h, UserRecord **ret_home) {
 
         already_activated = r > 0;
 
-        r = home_prepare(h, already_activated, &pkcs11_decrypted_passwords, &setup, &header_home);
+        r = home_prepare(h, already_activated, &cache, &setup, &header_home);
         if (r < 0)
                 return r;
 
-        r = home_load_embedded_identity(h, setup.root_fd, header_home, USER_RECONCILE_ANY, &pkcs11_decrypted_passwords, NULL, &new_home);
+        r = home_load_embedded_identity(h, setup.root_fd, header_home, USER_RECONCILE_ANY, &cache, NULL, &new_home);
         if (r < 0)
                 return r;
 
@@ -1335,7 +1523,7 @@ static int home_lock(UserRecord *h) {
 }
 
 static int home_unlock(UserRecord *h) {
-        _cleanup_(strv_free_erasep) char **pkcs11_decrypted_passwords = NULL;
+        _cleanup_(password_cache_free) PasswordCache cache = {};
         int r;
 
         assert(h);
@@ -1348,11 +1536,11 @@ static int home_unlock(UserRecord *h) {
         /* Note that we don't check if $HOME is actually mounted, since we want to avoid disk accesses on
          * that mount until we have resumed the device. */
 
-        r = user_record_authenticate(h, h, &pkcs11_decrypted_passwords);
+        r = user_record_authenticate(h, h, &cache, /* strict_verify= */ false);
         if (r < 0)
                 return r;
 
-        r = home_unlock_luks(h, &pkcs11_decrypted_passwords);
+        r = home_unlock_luks(h, &cache);
         if (r < 0)
                 return r;
 
@@ -1406,8 +1594,8 @@ static int run(int argc, char *argv[]) {
 
         /* Well known return values of these operations, that systemd-homed knows and converts to proper D-Bus errors:
          *
-         * EMSGSIZE        → file systems of this type cannnot be shrinked
-         * ETXTBSY         → file systems of this type can only be shrinked offline
+         * EMSGSIZE        → file systems of this type cannot be shrunk
+         * ETXTBSY         → file systems of this type can only be shrunk offline
          * ERANGE          → file system size too small
          * ENOLINK         → system does not support selected storage backend
          * EPROTONOSUPPORT → system does not support selected file system
@@ -1415,15 +1603,18 @@ static int run(int argc, char *argv[]) {
          * ESOCKTNOSUPPORT → operation not support on this file system
          * ENOKEY          → password incorrect (or not sufficient, or not supplied)
          * EBADSLT         → similar, but PKCS#11 device is defined and might be able to provide password, if it was plugged in which it is not
-         * ENOANO          → suitable PKCS#11 device found, but PIN is missing to unlock it
+         * ENOANO          → suitable PKCS#11/FIDO2 device found, but PIN is missing to unlock it
          * ERFKILL         → suitable PKCS#11 device found, but OK to ask for on-device interactive authentication not given
-         * EOWNERDEAD      → suitable PKCS#11 device found, but its PIN is locked
-         * ENOLCK          → suitable PKCS#11 device found, but PIN incorrect
+         * EMEDIUMTYPE     → suitable FIDO2 device found, but OK to ask for user presence not given
+         * ENOSTR          → suitable FIDO2 device found, but user didn't react to action request on token quickly enough
+         * EOWNERDEAD      → suitable PKCS#11/FIDO2 device found, but its PIN is locked
+         * ENOLCK          → suitable PKCS#11/FIDO2 device found, but PIN incorrect
          * ETOOMANYREFS    → suitable PKCS#11 device found, but PIN incorrect, and only few tries left
          * EUCLEAN         → suitable PKCS#11 device found, but PIN incorrect, and only one try left
          * EBUSY           → file system is currently active
          * ENOEXEC         → file system is currently not active
          * ENOSPC          → not enough disk space for operation
+         * EKEYREVOKED     → user record has not suitable hashed password or pkcs#11 entry, we cannot authenticate
          */
 
         if (streq(argv[1], "activate"))
index 81698b760149dc78dc7ee8b0342cbb82a41b013d..ce8f2a461fe827e81f2669886de97f206171b4da 100644 (file)
@@ -17,6 +17,7 @@ typedef struct HomeSetup {
         LoopDevice *loop;
         struct crypt_device *crypt_device;
         int root_fd;
+        int image_fd;
         sd_id128_t found_partition_uuid;
         sd_id128_t found_luks_uuid;
         sd_id128_t found_fs_uuid;
@@ -28,30 +29,41 @@ typedef struct HomeSetup {
 
         bool undo_dm;
         bool undo_mount;
+        bool do_offline_fitrim;
+        bool do_offline_fallocate;
 
         uint64_t partition_offset;
         uint64_t partition_size;
 } HomeSetup;
 
+typedef struct PasswordCache {
+        /* Decoding passwords from security tokens is expensive and typically requires user interaction, hence cache any we already figured out. */
+        char **pkcs11_passwords;
+        char **fido2_passwords;
+} PasswordCache;
+
+void password_cache_free(PasswordCache *cache);
+
 #define HOME_SETUP_INIT                                 \
         {                                               \
                 .root_fd = -1,                          \
+                .image_fd = -1,                         \
                 .partition_offset = UINT64_MAX,         \
                 .partition_size = UINT64_MAX,           \
         }
 
 int home_setup_undo(HomeSetup *setup);
 
-int home_prepare(UserRecord *h, bool already_activated, char ***pkcs11_decrypted_passwords, HomeSetup *setup, UserRecord **ret_header_home);
+int home_prepare(UserRecord *h, bool already_activated, PasswordCache *cache, HomeSetup *setup, UserRecord **ret_header_home);
 
-int home_refresh(UserRecord *h, HomeSetup *setup, UserRecord *header_home, char ***pkcs11_decrypted_passwords, struct statfs *ret_statfs, UserRecord **ret_new_home);
+int home_refresh(UserRecord *h, HomeSetup *setup, UserRecord *header_home, PasswordCache *cache, struct statfs *ret_statfs, UserRecord **ret_new_home);
 
 int home_populate(UserRecord *h, int dir_fd);
 
-int home_load_embedded_identity(UserRecord *h, int root_fd, UserRecord *header_home, UserReconcileMode mode, char ***pkcs11_decrypted_passwords, UserRecord **ret_embedded_home, UserRecord **ret_new_home);
+int home_load_embedded_identity(UserRecord *h, int root_fd, UserRecord *header_home, UserReconcileMode mode, PasswordCache *cache, UserRecord **ret_embedded_home, UserRecord **ret_new_home);
 int home_store_embedded_identity(UserRecord *h, int root_fd, uid_t uid, UserRecord *old_home);
 int home_extend_embedded_identity(UserRecord *h, UserRecord *used, HomeSetup *setup);
 
-int user_record_authenticate(UserRecord *h, UserRecord *secret, char ***pkcs11_decrypted_passwords);
+int user_record_authenticate(UserRecord *h, UserRecord *secret, PasswordCache *cache, bool strict_verify);
 
 int home_sync_and_statfs(int root_fd, struct statfs *ret);
index eb6da0b6969b5865bc9a30e42abf684900b40a68..797f3a3c6d0a3a9b772c493e85b65fa25a3acc02 100644 (file)
@@ -14,6 +14,7 @@ systemd_homework_sources = files('''
         homework-mount.c
         homework-mount.h
         homework-pkcs11.h
+        homework-fido2.h
         homework-quota.c
         homework-quota.h
         homework.c
@@ -25,12 +26,17 @@ systemd_homework_sources = files('''
 if conf.get('HAVE_P11KIT') == 1
         systemd_homework_sources += files('homework-pkcs11.c')
 endif
+if conf.get('HAVE_LIBFIDO2') == 1
+        systemd_homework_sources += files('homework-fido2.c')
+endif
 
 systemd_homed_sources = files('''
         home-util.c
         home-util.h
         homed-bus.c
         homed-bus.h
+        homed-conf.c
+        homed-conf.h
         homed-home-bus.c
         homed-home-bus.h
         homed-home.c
@@ -52,9 +58,21 @@ systemd_homed_sources = files('''
         user-record-util.h
 '''.split())
 
+homed_gperf_c = custom_target(
+        'homed_gperf.c',
+        input : 'homed-gperf.gperf',
+        output : 'homed-gperf.c',
+        command : [gperf, '@INPUT@', '--output-file', '@OUTPUT@'])
+
+systemd_homed_sources += [homed_gperf_c]
+
 homectl_sources = files('''
         home-util.c
         home-util.h
+        homectl-fido2.c
+        homectl-fido2.h
+        homectl-pkcs11.c
+        homectl-pkcs11.h
         homectl.c
         pwquality-util.c
         pwquality-util.h
@@ -78,4 +96,7 @@ if conf.get('ENABLE_HOMED') == 1
                      install_dir : dbussystemservicedir)
         install_data('org.freedesktop.home1.policy',
                      install_dir : polkitpolicydir)
+
+        install_data('homed.conf',
+                     install_dir : pkgsysconfdir)
 endif
index 5a3c817a5e351e62f80c7defce91b3b4e7f8ff1f..2c2c7a0819c34be2f91f483cc4b82795a384f0fb 100644 (file)
@@ -6,6 +6,7 @@
 #include "sd-bus.h"
 
 #include "bus-common-errors.h"
+#include "bus-locator.h"
 #include "errno-util.h"
 #include "fd-util.h"
 #include "home-util.h"
 #include "user-record.h"
 #include "user-util.h"
 
-/* Used for the "systemd-user-record-is-homed" PAM data field, to indicate whether we know whether this user
- * record is managed by homed or by something else. */
-#define USER_RECORD_IS_HOMED INT_TO_PTR(1)
-#define USER_RECORD_IS_OTHER INT_TO_PTR(2)
-
 static int parse_argv(
                 pam_handle_t *handle,
                 int argc, const char **argv,
@@ -64,29 +60,61 @@ static int parse_argv(
         return 0;
 }
 
+static int parse_env(
+                pam_handle_t *handle,
+                bool *please_suspend) {
+
+        const char *v;
+        int r;
+
+        /* Let's read the suspend setting from an env var in addition to the PAM command line. That makes it
+         * easy to declare the features of a display manager in code rather than configuration, and this is
+         * really a feature of code */
+
+        v = pam_getenv(handle, "SYSTEMD_HOME_SUSPEND");
+        if (!v) {
+                /* Also check the process env block, so that people can control this via an env var from the
+                 * outside of our process. */
+                v = secure_getenv("SYSTEMD_HOME_SUSPEND");
+                if (!v)
+                        return 0;
+        }
+
+        r = parse_boolean(v);
+        if (r < 0)
+                pam_syslog(handle, LOG_WARNING, "Failed to parse $SYSTEMD_HOME_SUSPEND argument, ignoring: %s", v);
+        else if (please_suspend)
+                *please_suspend = r;
+
+        return 0;
+}
+
 static int acquire_user_record(
                 pam_handle_t *handle,
+                const char *username,
                 UserRecord **ret_record) {
 
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
         _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
         _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
         _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
-        const char *username = NULL, *json = NULL;
-        const void *b = NULL;
+        _cleanup_free_ char *homed_field = NULL;
+        const char *json = NULL;
         int r;
 
         assert(handle);
 
-        r = pam_get_user(handle, &username, NULL);
-        if (r != PAM_SUCCESS) {
-                pam_syslog(handle, LOG_ERR, "Failed to get user name: %s", pam_strerror(handle, r));
-                return r;
-        }
+        if (!username) {
+                r = pam_get_user(handle, &username, NULL);
+                if (r != PAM_SUCCESS) {
+                        pam_syslog(handle, LOG_ERR, "Failed to get user name: %s", pam_strerror(handle, r));
+                        return r;
+                }
 
-        if (isempty(username)) {
-                pam_syslog(handle, LOG_ERR, "User name not set.");
-                return PAM_SERVICE_ERR;
+                if (isempty(username)) {
+                        pam_syslog(handle, LOG_ERR, "User name not set.");
+                        return PAM_SERVICE_ERR;
+                }
         }
 
         /* Let's bypass all IPC complexity for the two user names we know for sure we don't manage, and for
@@ -94,46 +122,36 @@ static int acquire_user_record(
         if (STR_IN_SET(username, "root", NOBODY_USER_NAME) || !valid_user_group_name(username, 0))
                 return PAM_USER_UNKNOWN;
 
-        /* Let's check if a previous run determined that this user is not managed by homed. If so, let's exit early */
-        r = pam_get_data(handle, "systemd-user-record-is-homed", &b);
+        /* We cache the user record in the PAM context. We use a field name that includes the username, since
+         * clients might change the user name associated with a PAM context underneath us. Notably, 'sudo'
+         * creates a single PAM context and first authenticates it with the user set to the originating user,
+         * then updates the user for the destination user and issues the session stack with the same PAM
+         * context. We thus must be prepared that the user record changes between calls and we keep any
+         * caching separate. */
+        homed_field = strjoin("systemd-home-user-record-", username);
+        if (!homed_field)
+                return pam_log_oom(handle);
+
+        /* Let's use the cache, so that we can share it between the session and the authentication hooks */
+        r = pam_get_data(handle, homed_field, (const void**) &json);
         if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) {
-                /* Failure */
-                pam_syslog(handle, LOG_ERR, "Failed to get PAM user-record-is-homed flag: %s", pam_strerror(handle, r));
+                pam_syslog(handle, LOG_ERR, "Failed to get PAM user record data: %s", pam_strerror(handle, r));
                 return r;
-        } else if (b == NULL)
-                /* Nothing cached yet, need to acquire fresh */
-                json = NULL;
-        else if (b != USER_RECORD_IS_HOMED)
-                /* Definitely not a homed record */
-                return PAM_USER_UNKNOWN;
-        else {
-                /* It's a homed record, let's use the cache, so that we can share it between the session and
-                 * the authentication hooks */
-                r = pam_get_data(handle, "systemd-user-record", (const void**) &json);
-                if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) {
-                        pam_syslog(handle, LOG_ERR, "Failed to get PAM user record data: %s", pam_strerror(handle, r));
-                        return r;
-                }
         }
-
-        if (!json) {
+        if (r == PAM_SUCCESS && json) {
+                /* We determined earlier that this is not a homed user? Then exit early. (We use -1 as
+                 * negative cache indicator) */
+                if (json == (void*) -1)
+                        return PAM_USER_UNKNOWN;
+        } else {
                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
-                _cleanup_free_ char *json_copy = NULL;
+                _cleanup_free_ char *generic_field = NULL, *json_copy = NULL;
 
                 r = pam_acquire_bus_connection(handle, &bus);
                 if (r != PAM_SUCCESS)
                         return r;
 
-                r = sd_bus_call_method(
-                                bus,
-                                "org.freedesktop.home1",
-                                "/org/freedesktop/home1",
-                                "org.freedesktop.home1.Manager",
-                                "GetUserRecordByName",
-                                &error,
-                                &reply,
-                                "s",
-                                username);
+                r = bus_call_method(bus, bus_home_mgr, "GetUserRecordByName", &error, &reply, "s", username);
                 if (r < 0) {
                         if (sd_bus_error_has_name(&error, SD_BUS_ERROR_SERVICE_UNKNOWN) ||
                             sd_bus_error_has_name(&error, SD_BUS_ERROR_NAME_HAS_NO_OWNER)) {
@@ -154,23 +172,38 @@ static int acquire_user_record(
                 if (r < 0)
                         return pam_bus_log_parse_error(handle, r);
 
+                /* First copy: for the homed-specific data field, i.e. where we know the user record is from
+                 * homed */
                 json_copy = strdup(json);
                 if (!json_copy)
                         return pam_log_oom(handle);
 
-                r = pam_set_data(handle, "systemd-user-record", json_copy, pam_cleanup_free);
+                r = pam_set_data(handle, homed_field, json_copy, pam_cleanup_free);
                 if (r != PAM_SUCCESS) {
-                        pam_syslog(handle, LOG_ERR, "Failed to set PAM user record data: %s", pam_strerror(handle, r));
+                        pam_syslog(handle, LOG_ERR, "Failed to set PAM user record data '%s': %s",
+                                   homed_field, pam_strerror(handle, r));
                         return r;
                 }
 
-                TAKE_PTR(json_copy);
+                /* Take a second copy: for the generic data field, the one which we share with
+                 * pam_systemd. While we insist on only reusing homed records, pam_systemd is fine with homed
+                 * and non-homed user records. */
+                json_copy = strdup(json);
+                if (!json_copy)
+                        return pam_log_oom(handle);
+
+                generic_field = strjoin("systemd-user-record-", username);
+                if (!generic_field)
+                        return pam_log_oom(handle);
 
-                r = pam_set_data(handle, "systemd-user-record-is-homed", USER_RECORD_IS_HOMED, NULL);
+                r = pam_set_data(handle, generic_field, json_copy, pam_cleanup_free);
                 if (r != PAM_SUCCESS) {
-                        pam_syslog(handle, LOG_ERR, "Failed to set PAM user record is homed flag: %s", pam_strerror(handle, r));
+                        pam_syslog(handle, LOG_ERR, "Failed to set PAM user record data '%s': %s",
+                                   homed_field, pam_strerror(handle, r));
                         return r;
                 }
+
+                TAKE_PTR(json_copy);
         }
 
         r = json_parse(json, JSON_PARSE_SENSITIVE, &v, NULL, NULL);
@@ -189,6 +222,7 @@ static int acquire_user_record(
                 return PAM_SERVICE_ERR;
         }
 
+        /* Safety check if cached record actually matches what we are looking for */
         if (!streq_ptr(username, ur->user_name)) {
                 pam_syslog(handle, LOG_ERR, "Acquired user record does not match user name.");
                 return PAM_SERVICE_ERR;
@@ -201,23 +235,36 @@ static int acquire_user_record(
 
 user_unknown:
         /* Cache this, so that we don't check again */
-        r = pam_set_data(handle, "systemd-user-record-is-homed", USER_RECORD_IS_OTHER, NULL);
+        r = pam_set_data(handle, homed_field, (void*) -1, NULL);
         if (r != PAM_SUCCESS)
-                pam_syslog(handle, LOG_ERR, "Failed to set PAM user-record-is-homed flag, ignoring: %s", pam_strerror(handle, r));
+                pam_syslog(handle, LOG_ERR, "Failed to set PAM user record data '%s' to invalid, ignoring: %s",
+                           homed_field, pam_strerror(handle, r));
 
         return PAM_USER_UNKNOWN;
 }
 
-static int release_user_record(pam_handle_t *handle) {
+static int release_user_record(pam_handle_t *handle, const char *username) {
+        _cleanup_free_ char *homed_field = NULL, *generic_field = NULL;
         int r, k;
 
-        r = pam_set_data(handle, "systemd-user-record", NULL, NULL);
+        assert(handle);
+        assert(username);
+
+        homed_field = strjoin("systemd-home-user-record-", username);
+        if (!homed_field)
+                return pam_log_oom(handle);
+
+        r = pam_set_data(handle, homed_field, NULL, NULL);
         if (r != PAM_SUCCESS)
-                pam_syslog(handle, LOG_ERR, "Failed to release PAM user record data: %s", pam_strerror(handle, r));
+                pam_syslog(handle, LOG_ERR, "Failed to release PAM user record data '%s': %s", homed_field, pam_strerror(handle, r));
 
-        k = pam_set_data(handle, "systemd-user-record-is-homed", NULL, NULL);
+        generic_field = strjoin("systemd-user-record-", username);
+        if (!generic_field)
+                return pam_log_oom(handle);
+
+        k = pam_set_data(handle, generic_field, NULL, NULL);
         if (k != PAM_SUCCESS)
-                pam_syslog(handle, LOG_ERR, "Failed to release PAM user-record-is-homed flag: %s", pam_strerror(handle, k));
+                pam_syslog(handle, LOG_ERR, "Failed to release PAM user record data '%s': %s", generic_field, pam_strerror(handle, k));
 
         return IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA) ? k : r;
 }
@@ -312,7 +359,7 @@ static int handle_generic_user_record_error(
                         return PAM_AUTHTOK_ERR;
                 }
 
-                r = user_record_set_pkcs11_pin(secret, STRV_MAKE(newp), false);
+                r = user_record_set_token_pin(secret, STRV_MAKE(newp), false);
                 if (r < 0) {
                         pam_syslog(handle, LOG_ERR, "Failed to store PIN: %s", strerror_safe(r));
                         return PAM_SERVICE_ERR;
@@ -328,6 +375,21 @@ static int handle_generic_user_record_error(
                         return PAM_SERVICE_ERR;
                 }
 
+        } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED)) {
+
+                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Please verify presence on security token of user %s.", user_name);
+
+                r = user_record_set_fido2_user_presence_permitted(secret, true);
+                if (r < 0) {
+                        pam_syslog(handle, LOG_ERR, "Failed to set FIDO2 user presence permitted flag: %s", strerror_safe(r));
+                        return PAM_SERVICE_ERR;
+                }
+
+        } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_PIN_LOCKED)) {
+
+                (void) pam_prompt(handle, PAM_ERROR_MSG, NULL, "Security token PIN is locked, please unlock it first. (Hint: Removal and re-insertion might suffice.)");
+                return PAM_SERVICE_ERR;
+
         } else if (sd_bus_error_has_name(error, BUS_ERROR_TOKEN_BAD_PIN)) {
                 _cleanup_(erase_and_freep) char *newp = NULL;
 
@@ -341,7 +403,7 @@ static int handle_generic_user_record_error(
                         return PAM_AUTHTOK_ERR;
                 }
 
-                r = user_record_set_pkcs11_pin(secret, STRV_MAKE(newp), false);
+                r = user_record_set_token_pin(secret, STRV_MAKE(newp), false);
                 if (r < 0) {
                         pam_syslog(handle, LOG_ERR, "Failed to store PIN: %s", strerror_safe(r));
                         return PAM_SERVICE_ERR;
@@ -360,7 +422,7 @@ static int handle_generic_user_record_error(
                         return PAM_AUTHTOK_ERR;
                 }
 
-                r = user_record_set_pkcs11_pin(secret, STRV_MAKE(newp), false);
+                r = user_record_set_token_pin(secret, STRV_MAKE(newp), false);
                 if (r < 0) {
                         pam_syslog(handle, LOG_ERR, "Failed to store PIN: %s", strerror_safe(r));
                         return PAM_SERVICE_ERR;
@@ -379,7 +441,7 @@ static int handle_generic_user_record_error(
                         return PAM_AUTHTOK_ERR;
                 }
 
-                r = user_record_set_pkcs11_pin(secret, STRV_MAKE(newp), false);
+                r = user_record_set_token_pin(secret, STRV_MAKE(newp), false);
                 if (r < 0) {
                         pam_syslog(handle, LOG_ERR, "Failed to store PIN: %s", strerror_safe(r));
                         return PAM_SERVICE_ERR;
@@ -403,7 +465,9 @@ static int acquire_home(
         bool do_auth = please_authenticate, home_not_active = false, home_locked = false;
         _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL;
         _cleanup_close_ int acquired_fd = -1;
+        _cleanup_free_ char *fd_field = NULL;
         const void *home_fd_ptr = NULL;
+        const char *username = NULL;
         unsigned n_attempts = 0;
         int r;
 
@@ -417,8 +481,27 @@ static int acquire_home(
          * authenticates, while the other PAM hooks unset it so that they can a ref of their own without
          * authentication if possible, but with authentication if necessary. */
 
+        r = pam_get_user(handle, &username, NULL);
+        if (r != PAM_SUCCESS) {
+                pam_syslog(handle, LOG_ERR, "Failed to get user name: %s", pam_strerror(handle, r));
+                return r;
+        }
+
+        if (isempty(username)) {
+                pam_syslog(handle, LOG_ERR, "User name not set.");
+                return PAM_SERVICE_ERR;
+        }
+
         /* If we already have acquired the fd, let's shortcut this */
-        r = pam_get_data(handle, "systemd-home-fd", &home_fd_ptr);
+        fd_field = strjoin("systemd-home-fd-", username);
+        if (!fd_field)
+                return pam_log_oom(handle);
+
+        r = pam_get_data(handle, fd_field, &home_fd_ptr);
+        if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) {
+                pam_syslog(handle, LOG_ERR, "Failed to retrieve PAM home reference fd: %s", pam_strerror(handle, r));
+                return r;
+        }
         if (r == PAM_SUCCESS && PTR_TO_FD(home_fd_ptr) >= 0)
                 return PAM_SUCCESS;
 
@@ -426,12 +509,12 @@ static int acquire_home(
         if (r != PAM_SUCCESS)
                 return r;
 
-        r = acquire_user_record(handle, &ur);
+        r = acquire_user_record(handle, username, &ur);
         if (r != PAM_SUCCESS)
                 return r;
 
         /* Implement our own retry loop here instead of relying on the PAM client's one. That's because it
-         * might happen that the the record we stored on the host does not match the encryption password of
+         * might happen that the record we stored on the host does not match the encryption password of
          * the LUKS image in case the image was used in a different system where the password was
          * changed. In that case it will happen that the LUKS password and the host password are
          * different, and we handle that by collecting and passing multiple passwords in that case. Hence we
@@ -467,13 +550,7 @@ static int acquire_home(
                         }
                 }
 
-                r = sd_bus_message_new_method_call(
-                                bus,
-                                &m,
-                                "org.freedesktop.home1",
-                                "/org/freedesktop/home1",
-                                "org.freedesktop.home1.Manager",
-                                do_auth ? "AcquireHome" : "RefHome");
+                r = bus_message_new_method_call(bus, &m, bus_home_mgr, do_auth ? "AcquireHome" : "RefHome");
                 if (r < 0)
                         return pam_bus_log_create_error(handle, r);
 
@@ -548,7 +625,7 @@ static int acquire_home(
                 do_auth = true;
         }
 
-        r = pam_set_data(handle, "systemd-home-fd", FD_TO_PTR(acquired_fd), cleanup_home_fd);
+        r = pam_set_data(handle, fd_field, FD_TO_PTR(acquired_fd), cleanup_home_fd);
         if (r < 0) {
                 pam_syslog(handle, LOG_ERR, "Failed to set PAM bus data: %s", pam_strerror(handle, r));
                 return r;
@@ -559,7 +636,7 @@ static int acquire_home(
                 /* We likely just activated the home directory, let's flush out the user record, since a
                  * newer embedded user record might have been acquired from the activation. */
 
-                r = release_user_record(handle);
+                r = release_user_record(handle, ur->user_name);
                 if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA))
                         return r;
         }
@@ -569,15 +646,27 @@ static int acquire_home(
         return PAM_SUCCESS;
 }
 
-static int release_home_fd(pam_handle_t *handle) {
+static int release_home_fd(pam_handle_t *handle, const char *username) {
+        _cleanup_free_ char *fd_field = NULL;
         const void *home_fd_ptr = NULL;
         int r;
 
-        r = pam_get_data(handle, "systemd-home-fd", &home_fd_ptr);
-        if (r == PAM_NO_MODULE_DATA || PTR_TO_FD(home_fd_ptr) < 0)
+        assert(handle);
+        assert(username);
+
+        fd_field = strjoin("systemd-home-fd-", username);
+        if (!fd_field)
+                return pam_log_oom(handle);
+
+        r = pam_get_data(handle, fd_field, &home_fd_ptr);
+        if (r == PAM_NO_MODULE_DATA || (r == PAM_SUCCESS && PTR_TO_FD(home_fd_ptr) < 0))
                 return PAM_NO_MODULE_DATA;
+        if (r != PAM_SUCCESS) {
+                pam_syslog(handle, LOG_ERR, "Failed to retrieve PAM home reference fd: %s", pam_strerror(handle, r));
+                return r;
+        }
 
-        r = pam_set_data(handle, "systemd-home-fd", NULL, NULL);
+        r = pam_set_data(handle, fd_field, NULL, NULL);
         if (r != PAM_SUCCESS)
                 pam_syslog(handle, LOG_ERR, "Failed to release PAM home reference fd: %s", pam_strerror(handle, r));
 
@@ -591,6 +680,9 @@ _public_ PAM_EXTERN int pam_sm_authenticate(
 
         bool debug = false, suspend_please = false;
 
+        if (parse_env(handle, &suspend_please) < 0)
+                return PAM_AUTH_ERR;
+
         if (parse_argv(handle,
                        argc, argv,
                        &suspend_please,
@@ -615,6 +707,9 @@ _public_ PAM_EXTERN int pam_sm_open_session(
         bool debug = false, suspend_please = false;
         int r;
 
+        if (parse_env(handle, &suspend_please) < 0)
+                return PAM_SESSION_ERR;
+
         if (parse_argv(handle,
                        argc, argv,
                        &suspend_please,
@@ -636,6 +731,12 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                 return r;
         }
 
+        r = pam_putenv(handle, suspend_please ? "SYSTEMD_HOME_SUSPEND=1" : "SYSTEMD_HOME_SUSPEND=0");
+        if (r != PAM_SUCCESS) {
+                pam_syslog(handle, LOG_ERR, "Failed to set PAM environment variable $SYSTEMD_HOME_SUSPEND: %s", pam_strerror(handle, r));
+                return r;
+        }
+
         /* Let's release the D-Bus connection, after all the session might live quite a long time, and we are
          * not going to process the bus connection in that time, so let's better close before the daemon
          * kicks us off because we are not processing anything. */
@@ -664,31 +765,30 @@ _public_ PAM_EXTERN int pam_sm_close_session(
         if (debug)
                 pam_syslog(handle, LOG_DEBUG, "pam-systemd-homed session end");
 
+        r = pam_get_user(handle, &username, NULL);
+        if (r != PAM_SUCCESS) {
+                pam_syslog(handle, LOG_ERR, "Failed to get user name: %s", pam_strerror(handle, r));
+                return r;
+        }
+
+        if (isempty(username)) {
+                pam_syslog(handle, LOG_ERR, "User name not set.");
+                return PAM_SERVICE_ERR;
+        }
+
         /* Let's explicitly drop the reference to the homed session, so that the subsequent ReleaseHome()
          * call will be able to do its thing. */
-        r = release_home_fd(handle);
+        r = release_home_fd(handle, username);
         if (r == PAM_NO_MODULE_DATA) /* Nothing to do, we never acquired an fd */
                 return PAM_SUCCESS;
         if (r != PAM_SUCCESS)
                 return r;
 
-        r = pam_get_user(handle, &username, NULL);
-        if (r != PAM_SUCCESS) {
-                pam_syslog(handle, LOG_ERR, "Failed to get user name: %s", pam_strerror(handle, r));
-                return r;
-        }
-
         r = pam_acquire_bus_connection(handle, &bus);
         if (r != PAM_SUCCESS)
                 return r;
 
-        r = sd_bus_message_new_method_call(
-                        bus,
-                        &m,
-                        "org.freedesktop.home1",
-                        "/org/freedesktop/home1",
-                        "org.freedesktop.home1.Manager",
-                        "ReleaseHome");
+        r = bus_message_new_method_call(bus, &m, bus_home_mgr, "ReleaseHome");
         if (r < 0)
                 return pam_bus_log_create_error(handle, r);
 
@@ -720,6 +820,9 @@ _public_ PAM_EXTERN int pam_sm_acct_mgmt(
         usec_t t;
         int r;
 
+        if (parse_env(handle, &please_suspend) < 0)
+                return PAM_AUTH_ERR;
+
         if (parse_argv(handle,
                        argc, argv,
                        &please_suspend,
@@ -735,7 +838,7 @@ _public_ PAM_EXTERN int pam_sm_acct_mgmt(
         if (r != PAM_SUCCESS)
                 return r;
 
-        r = acquire_user_record(handle, &ur);
+        r = acquire_user_record(handle, NULL, &ur);
         if (r != PAM_SUCCESS)
                 return r;
 
@@ -843,7 +946,7 @@ _public_ PAM_EXTERN int pam_sm_chauthtok(
         if (r != PAM_SUCCESS)
                 return r;
 
-        r = acquire_user_record(handle, &ur);
+        r = acquire_user_record(handle, NULL, &ur);
         if (r != PAM_SUCCESS)
                 return r;
 
@@ -888,7 +991,6 @@ _public_ PAM_EXTERN int pam_sm_chauthtok(
         if (FLAGS_SET(flags, PAM_PRELIM_CHECK))
                 return PAM_SUCCESS;
 
-
         old_secret = user_record_new();
         if (!old_secret)
                 return pam_log_oom(handle);
@@ -915,13 +1017,7 @@ _public_ PAM_EXTERN int pam_sm_chauthtok(
                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
                 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
 
-                r = sd_bus_message_new_method_call(
-                                bus,
-                                &m,
-                                "org.freedesktop.home1",
-                                "/org/freedesktop/home1",
-                                "org.freedesktop.home1.Manager",
-                                "ChangePasswordHome");
+                r = bus_message_new_method_call(bus, &m, bus_home_mgr, "ChangePasswordHome");
                 if (r < 0)
                         return pam_bus_log_create_error(handle, r);
 
index 34f9d76cb262046353640e5416fcdc88f9293033..5d0ac8653387de240f98b727e84efe86eff73f2c 100644 (file)
@@ -172,7 +172,7 @@ int user_record_reconcile(
          *     -REMCHG: identity records are not about the same user
          *     -ESTALE: embedded identity record is equally new or newer than supplied record
          *
-         * Return the new record to use, which is either the the embedded record updated with the host
+         * Return the new record to use, which is either the embedded record updated with the host
          * binding or the host record. In both cases the secret data is stripped. */
 
         assert(host);
@@ -276,7 +276,7 @@ int user_record_add_binding(
 
         _cleanup_(json_variant_unrefp) JsonVariant *new_binding_entry = NULL, *binding = NULL;
         char smid[SD_ID128_STRING_MAX], partition_uuids[37], luks_uuids[37], fs_uuids[37];
-        _cleanup_free_ char *ip = NULL, *hd = NULL;
+        _cleanup_free_ char *ip = NULL, *hd = NULL, *ip_auto = NULL, *lc = NULL, *lcm = NULL, *fst = NULL;
         sd_id128_t mid;
         int r;
 
@@ -294,6 +294,10 @@ int user_record_add_binding(
                 ip = strdup(image_path);
                 if (!ip)
                         return -ENOMEM;
+        } else if (!h->image_path && storage >= 0) {
+                r = user_record_build_image_path(storage, user_record_user_name_and_realm(h), &ip_auto);
+                if (r < 0)
+                        return r;
         }
 
         if (home_directory) {
@@ -302,6 +306,24 @@ int user_record_add_binding(
                         return -ENOMEM;
         }
 
+        if (file_system_type) {
+                fst = strdup(file_system_type);
+                if (!fst)
+                        return -ENOMEM;
+        }
+
+        if (luks_cipher) {
+                lc = strdup(luks_cipher);
+                if (!lc)
+                        return -ENOMEM;
+        }
+
+        if (luks_cipher_mode) {
+                lcm = strdup(luks_cipher_mode);
+                if (!lcm)
+                        return -ENOMEM;
+        }
+
         r = json_build(&new_binding_entry,
                        JSON_BUILD_OBJECT(
                                        JSON_BUILD_PAIR_CONDITION(!!image_path, "imagePath", JSON_BUILD_STRING(image_path)),
@@ -348,6 +370,8 @@ int user_record_add_binding(
 
         if (ip)
                 free_and_replace(h->image_path, ip);
+        if (ip_auto)
+                free_and_replace(h->image_path_auto, ip_auto);
 
         if (!sd_id128_is_null(partition_uuid))
                 h->partition_uuid = partition_uuid;
@@ -358,11 +382,22 @@ int user_record_add_binding(
         if (!sd_id128_is_null(fs_uuid))
                 h->file_system_uuid = fs_uuid;
 
+        if (lc)
+                free_and_replace(h->luks_cipher, lc);
+        if (lcm)
+                free_and_replace(h->luks_cipher_mode, lcm);
+        if (luks_volume_key_size != UINT64_MAX)
+                h->luks_volume_key_size = luks_volume_key_size;
+
+        if (fst)
+                free_and_replace(h->file_system_type, fst);
         if (hd)
                 free_and_replace(h->home_directory, hd);
 
         if (uid_is_valid(uid))
                 h->uid = uid;
+        if (gid_is_valid(gid))
+                h->gid = gid;
 
         h->mask |= USER_RECORD_BINDING;
         return 1;
@@ -460,7 +495,7 @@ int user_record_test_image_path(UserRecord *h) {
                 if (S_ISBLK(st.st_mode)) {
                         /* For block devices we can't really be sure if the device referenced actually is the
                          * fs we look for or some other file system (think: what does /dev/sdb1 refer
-                         * to?). Hence, let's return USER_TEST_MAYBE as an ambigious return value for these
+                         * to?). Hence, let's return USER_TEST_MAYBE as an ambiguous return value for these
                          * case, except if the device path used is one of the paths that is based on a
                          * filesystem or partition UUID or label, because in those cases we can be sure we
                          * are referring to the right device. */
@@ -840,6 +875,8 @@ int user_record_set_password(UserRecord *h, char **password, bool prepend) {
         if (r < 0)
                 return r;
 
+        json_variant_sensitive(w);
+
         r = json_variant_set_field(&h->json, "secret", w);
         if (r < 0)
                 return r;
@@ -850,7 +887,7 @@ int user_record_set_password(UserRecord *h, char **password, bool prepend) {
         return 0;
 }
 
-int user_record_set_pkcs11_pin(UserRecord *h, char **pin, bool prepend) {
+int user_record_set_token_pin(UserRecord *h, char **pin, bool prepend) {
         _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
         _cleanup_(strv_free_erasep) char **e = NULL;
         int r;
@@ -862,17 +899,17 @@ int user_record_set_pkcs11_pin(UserRecord *h, char **pin, bool prepend) {
                 if (!e)
                         return -ENOMEM;
 
-                r = strv_extend_strv(&e, h->pkcs11_pin, true);
+                r = strv_extend_strv(&e, h->token_pin, true);
                 if (r < 0)
                         return r;
 
                 strv_uniq(e);
 
-                if (strv_equal(h->pkcs11_pin, e))
+                if (strv_equal(h->token_pin, e))
                         return 0;
 
         } else {
-                if (strv_equal(h->pkcs11_pin, pin))
+                if (strv_equal(h->token_pin, pin))
                         return 0;
 
                 e = strv_copy(pin);
@@ -885,7 +922,7 @@ int user_record_set_pkcs11_pin(UserRecord *h, char **pin, bool prepend) {
         w = json_variant_ref(json_variant_by_key(h->json, "secret"));
 
         if (strv_isempty(e))
-                r = json_variant_filter(&w, STRV_MAKE("pkcs11Pin"));
+                r = json_variant_filter(&w, STRV_MAKE("tokenPin"));
         else {
                 _cleanup_(json_variant_unrefp) JsonVariant *l = NULL;
 
@@ -895,16 +932,18 @@ int user_record_set_pkcs11_pin(UserRecord *h, char **pin, bool prepend) {
 
                 json_variant_sensitive(l);
 
-                r = json_variant_set_field(&w, "pkcs11Pin", l);
+                r = json_variant_set_field(&w, "tokenPin", l);
         }
         if (r < 0)
                 return r;
 
+        json_variant_sensitive(w);
+
         r = json_variant_set_field(&h->json, "secret", w);
         if (r < 0)
                 return r;
 
-        strv_free_and_replace(h->pkcs11_pin, e);
+        strv_free_and_replace(h->token_pin, e);
 
         SET_FLAG(h->mask, USER_RECORD_SECRET, !json_variant_is_blank_object(w));
         return 0;
@@ -927,8 +966,11 @@ int user_record_set_pkcs11_protected_authentication_path_permitted(UserRecord *h
 
         if (json_variant_is_blank_object(w))
                 r = json_variant_filter(&h->json, STRV_MAKE("secret"));
-        else
+        else {
+                json_variant_sensitive(w);
+
                 r = json_variant_set_field(&h->json, "secret", w);
+        }
         if (r < 0)
                 return r;
 
@@ -938,6 +980,34 @@ int user_record_set_pkcs11_protected_authentication_path_permitted(UserRecord *h
         return 0;
 }
 
+int user_record_set_fido2_user_presence_permitted(UserRecord *h, int b) {
+        _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
+        int r;
+
+        assert(h);
+
+        w = json_variant_ref(json_variant_by_key(h->json, "secret"));
+
+        if (b < 0)
+                r = json_variant_filter(&w, STRV_MAKE("fido2UserPresencePermitted"));
+        else
+                r = json_variant_set_field_boolean(&w, "fido2UserPresencePermitted", b);
+        if (r < 0)
+                return r;
+
+        if (json_variant_is_blank_object(w))
+                r = json_variant_filter(&h->json, STRV_MAKE("secret"));
+        else
+                r = json_variant_set_field(&h->json, "secret", w);
+        if (r < 0)
+                return r;
+
+        h->fido2_user_presence_permitted = b;
+
+        SET_FLAG(h->mask, USER_RECORD_SECRET, !json_variant_is_blank_object(w));
+        return 0;
+}
+
 static bool per_machine_entry_empty(JsonVariant *v) {
         const char *k;
         _unused_ JsonVariant *e;
@@ -1020,12 +1090,22 @@ int user_record_merge_secret(UserRecord *h, UserRecord *secret) {
         if (r < 0)
                 return r;
 
-        r = user_record_set_pkcs11_pin(h, secret->pkcs11_pin, true);
+        r = user_record_set_token_pin(h, secret->token_pin, true);
         if (r < 0)
                 return r;
 
         if (secret->pkcs11_protected_authentication_path_permitted >= 0) {
-                r = user_record_set_pkcs11_protected_authentication_path_permitted(h, secret->pkcs11_protected_authentication_path_permitted);
+                r = user_record_set_pkcs11_protected_authentication_path_permitted(
+                                h,
+                                secret->pkcs11_protected_authentication_path_permitted);
+                if (r < 0)
+                        return r;
+        }
+
+        if (secret->fido2_user_presence_permitted >= 0) {
+                r = user_record_set_fido2_user_presence_permitted(
+                                h,
+                                secret->fido2_user_presence_permitted);
                 if (r < 0)
                         return r;
         }
index 6afc8df19a7097c6ee036d4ead14f707788bc5f0..24582988257723b2220d59fe2a32a83bcad678a6 100644 (file)
@@ -47,8 +47,9 @@ int user_record_set_disk_size(UserRecord *h, uint64_t disk_size);
 int user_record_set_password(UserRecord *h, char **password, bool prepend);
 int user_record_make_hashed_password(UserRecord *h, char **password, bool extend);
 int user_record_set_hashed_password(UserRecord *h, char **hashed_password);
-int user_record_set_pkcs11_pin(UserRecord *h, char **pin, bool prepend);
+int user_record_set_token_pin(UserRecord *h, char **pin, bool prepend);
 int user_record_set_pkcs11_protected_authentication_path_permitted(UserRecord *h, int b);
+int user_record_set_fido2_user_presence_permitted(UserRecord *h, int b);
 int user_record_set_password_change_now(UserRecord *h, int b);
 int user_record_merge_secret(UserRecord *h, UserRecord *secret);
 int user_record_good_authentication(UserRecord *h);
index 5596846ed26cd415036fb0657916d2d67d5f9586..2d6b2da125e3128124a305222f23ee3913270fd6 100644 (file)
@@ -12,7 +12,7 @@
 #include "alloc-util.h"
 #include "architecture.h"
 #include "bus-error.h"
-#include "bus-util.h"
+#include "bus-map-properties.h"
 #include "hostname-util.h"
 #include "main-func.h"
 #include "pretty-print.h"
@@ -436,9 +436,7 @@ static int run(int argc, char *argv[]) {
         int r;
 
         setlocale(LC_ALL, "");
-        log_show_color(true);
-        log_parse_environment();
-        log_open();
+        log_setup_cli();
 
         r = parse_argv(argc, argv);
         if (r <= 0)
@@ -446,7 +444,7 @@ static int run(int argc, char *argv[]) {
 
         r = bus_connect_transport(arg_transport, arg_host, false, &bus);
         if (r < 0)
-                return log_error_errno(r, "Failed to create bus connection: %m");
+                return bus_log_connect_error(r);
 
         return hostnamectl_main(bus, argc, argv);
 }
index 21f647149514ab1a77f3c6ccf6ec15b8169057e0..7f6607a5270370d14bb52ad0d06606374a333e59 100644 (file)
@@ -8,6 +8,7 @@
 
 #include "alloc-util.h"
 #include "bus-common-errors.h"
+#include "bus-log-control-api.h"
 #include "bus-polkit.h"
 #include "def.h"
 #include "env-file-label.h"
@@ -25,7 +26,9 @@
 #include "parse-util.h"
 #include "path-util.h"
 #include "selinux-util.h"
+#include "service-util.h"
 #include "signal-util.h"
+#include "stat-util.h"
 #include "strv.h"
 #include "user-util.h"
 #include "util.h"
 #define VALID_DEPLOYMENT_CHARS (DIGITS LETTERS "-.:")
 
 enum {
-        PROP_HOSTNAME,
+        /* Read from /etc/hostname */
         PROP_STATIC_HOSTNAME,
+
+        /* Read from /etc/machine-info */
         PROP_PRETTY_HOSTNAME,
         PROP_ICON_NAME,
         PROP_CHASSIS,
         PROP_DEPLOYMENT,
         PROP_LOCATION,
-        PROP_KERNEL_NAME,
-        PROP_KERNEL_RELEASE,
-        PROP_KERNEL_VERSION,
+
+        /* Read from /etc/os-release (or /usr/lib/os-release) */
         PROP_OS_PRETTY_NAME,
         PROP_OS_CPE_NAME,
-        PROP_HOME_URL,
-        _PROP_MAX
+        PROP_OS_HOME_URL,
+        _PROP_MAX,
+        _PROP_INVALID = -1,
 };
 
 typedef struct Context {
         char *data[_PROP_MAX];
+
+        struct stat etc_hostname_stat;
+        struct stat etc_os_release_stat;
+        struct stat etc_machine_info_stat;
+
         Hashmap *polkit_registry;
-        sd_id128_t uuid;
-        bool has_uuid;
 } Context;
 
-static void context_reset(Context *c) {
+static void context_reset(Context *c, uint64_t mask) {
         int p;
 
         assert(c);
 
-        for (p = 0; p < _PROP_MAX; p++)
+        for (p = 0; p < _PROP_MAX; p++) {
+                if (!FLAGS_SET(mask, UINT64_C(1) << p))
+                        continue;
+
                 c->data[p] = mfree(c->data[p]);
+        }
 }
 
-static void context_clear(Context *c) {
+static void context_destroy(Context *c) {
         assert(c);
 
-        context_reset(c);
+        context_reset(c, UINT64_MAX);
         bus_verify_polkit_async_registry_free(c->polkit_registry);
 }
 
-static int context_read_data(Context *c) {
+static void context_read_etc_hostname(Context *c) {
+        struct stat current_stat = {};
         int r;
-        struct utsname u;
 
         assert(c);
 
-        context_reset(c);
+        if (stat("/etc/hostname", &current_stat) >= 0 &&
+            stat_inode_unmodified(&c->etc_hostname_stat, &current_stat))
+                return;
 
-        assert_se(uname(&u) >= 0);
-        c->data[PROP_KERNEL_NAME] = strdup(u.sysname);
-        c->data[PROP_KERNEL_RELEASE] = strdup(u.release);
-        c->data[PROP_KERNEL_VERSION] = strdup(u.version);
-        if (!c->data[PROP_KERNEL_NAME] || !c->data[PROP_KERNEL_RELEASE] ||
-            !c->data[PROP_KERNEL_VERSION])
-                return -ENOMEM;
-
-        c->data[PROP_HOSTNAME] = gethostname_malloc();
-        if (!c->data[PROP_HOSTNAME])
-                return -ENOMEM;
+        context_reset(c, UINT64_C(1) << PROP_STATIC_HOSTNAME);
 
         r = read_etc_hostname(NULL, &c->data[PROP_STATIC_HOSTNAME]);
         if (r < 0 && r != -ENOENT)
-                return r;
+                log_warning_errno(r, "Failed to read /etc/hostname, ignoring: %m");
+
+        c->etc_hostname_stat = current_stat;
+}
+
+static void context_read_machine_info(Context *c) {
+        struct stat current_stat = {};
+        int r;
+
+        assert(c);
+
+        if (stat("/etc/machine-info", &current_stat) >= 0 &&
+            stat_inode_unmodified(&c->etc_machine_info_stat, &current_stat))
+                return;
+
+        context_reset(c,
+                      (UINT64_C(1) << PROP_PRETTY_HOSTNAME) |
+                      (UINT64_C(1) << PROP_ICON_NAME) |
+                      (UINT64_C(1) << PROP_CHASSIS) |
+                      (UINT64_C(1) << PROP_DEPLOYMENT) |
+                      (UINT64_C(1) << PROP_LOCATION));
 
         r = parse_env_file(NULL, "/etc/machine-info",
                            "PRETTY_HOSTNAME", &c->data[PROP_PRETTY_HOSTNAME],
@@ -104,28 +128,36 @@ static int context_read_data(Context *c) {
                            "DEPLOYMENT", &c->data[PROP_DEPLOYMENT],
                            "LOCATION", &c->data[PROP_LOCATION]);
         if (r < 0 && r != -ENOENT)
-                return r;
+                log_warning_errno(r, "Failed to read /etc/machine-info, ignoring: %m");
+
+        c->etc_machine_info_stat = current_stat;
+}
+
+static void context_read_os_release(Context *c) {
+        struct stat current_stat = {};
+        int r;
+
+        assert(c);
+
+        if ((stat("/etc/os-release", &current_stat) >= 0 ||
+             stat("/usr/lib/os-release", &current_stat) >= 0) &&
+            stat_inode_unmodified(&c->etc_os_release_stat, &current_stat))
+                return;
+
+        context_reset(c,
+                      (UINT64_C(1) << PROP_OS_PRETTY_NAME) |
+                      (UINT64_C(1) << PROP_OS_CPE_NAME) |
+                      (UINT64_C(1) << PROP_OS_HOME_URL));
 
         r = parse_os_release(NULL,
                              "PRETTY_NAME", &c->data[PROP_OS_PRETTY_NAME],
                              "CPE_NAME", &c->data[PROP_OS_CPE_NAME],
-                             "HOME_URL", &c->data[PROP_HOME_URL],
+                             "HOME_URL", &c->data[PROP_OS_HOME_URL],
                              NULL);
         if (r < 0 && r != -ENOENT)
-                return r;
-
-        r = id128_read("/sys/class/dmi/id/product_uuid", ID128_UUID, &c->uuid);
-        if (r == -ENOENT)
-                r = id128_read("/sys/firmware/devicetree/base/vm,uuid", ID128_UUID, &c->uuid);
-        if (r < 0)
-                log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r,
-                               "Failed to read product UUID, ignoring: %m");
-        else if (sd_id128_is_null(c->uuid) || sd_id128_is_allf(c->uuid))
-                log_debug("DMI product UUID " SD_ID128_FORMAT_STR " is all 0x00 or all 0xFF, ignoring.", SD_ID128_FORMAT_VAL(c->uuid));
-        else
-                c->has_uuid = true;
+                log_warning_errno(r, "Failed to read os-release file, ignoring: %m");
 
-        return 0;
+        c->etc_os_release_stat = current_stat;
 }
 
 static bool valid_chassis(const char *chassis) {
@@ -267,12 +299,22 @@ static bool hostname_is_useful(const char *hn) {
         return !isempty(hn) && !is_localhost(hn);
 }
 
-static int context_update_kernel_hostname(Context *c) {
-        const char *static_hn;
-        const char *hn;
+static int context_update_kernel_hostname(
+                Context *c,
+                const char *transient_hn) {
+
+        const char *static_hn, *hn;
+        struct utsname u;
 
         assert(c);
 
+        if (!transient_hn) {
+                /* If no transient hostname is passed in, then let's check what is currently set. */
+                assert_se(uname(&u) >= 0);
+                transient_hn =
+                        isempty(u.nodename) || streq(u.nodename, "(none)") ? NULL : u.nodename;
+        }
+
         static_hn = c->data[PROP_STATIC_HOSTNAME];
 
         /* /etc/hostname with something other than "localhost"
@@ -280,9 +322,9 @@ static int context_update_kernel_hostname(Context *c) {
         if (hostname_is_useful(static_hn))
                 hn = static_hn;
 
-        /* ... the transient host name, (ie: DHCP) comes next ... */
-        else if (!isempty(c->data[PROP_HOSTNAME]))
-                hn = c->data[PROP_HOSTNAME];
+        /* ... the transient hostname, (ie: DHCP) comes next ... */
+        else if (!isempty(transient_hn))
+                hn = transient_hn;
 
         /* ... fallback to static "localhost.*" ignored above ... */
         else if (!isempty(static_hn))
@@ -301,7 +343,6 @@ static int context_update_kernel_hostname(Context *c) {
 }
 
 static int context_write_data_static_hostname(Context *c) {
-
         assert(c);
 
         if (isempty(c->data[PROP_STATIC_HOSTNAME])) {
@@ -365,6 +406,94 @@ static int context_write_data_machine_info(Context *c) {
         return write_env_file_label("/etc/machine-info", l);
 }
 
+static int property_get_hostname(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        _cleanup_free_ char *current = NULL;
+        int r;
+
+        r = gethostname_strict(&current);
+        if (r == -ENXIO)
+                return sd_bus_message_append(reply, "s", FALLBACK_HOSTNAME);
+        if (r < 0)
+                return r;
+
+        return sd_bus_message_append(reply, "s", current);
+}
+
+static int property_get_static_hostname(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        Context *c = userdata;
+        assert(c);
+
+        context_read_etc_hostname(c);
+
+        return sd_bus_message_append(reply, "s", c->data[PROP_STATIC_HOSTNAME]);
+}
+
+static int property_get_machine_info_field(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        sd_bus_slot *slot;
+        Context *c;
+
+        /* Acquire the context object without this property's userdata offset added. Explanation: we want
+         * access to two pointers here: a) the main context object we cache all properties in, and b) the
+         * pointer to the property field inside the context object that we are supposed to update and
+         * use. The latter (b) we get in the 'userdata' function parameter, and sd-bus calculates that for us
+         * from the 'userdata' pointer we supplied when the vtable was registered, with the offset we
+         * specified in the vtable added on top. To get the former (a) we need the 'userdata' pointer from
+         * the vtable registration directly, without the offset added. Hence we ask sd-bus what the slot
+         * object is (which encapsulates the vtable registration), and then query the 'userdata' field
+         * directly off it. */
+        assert_se(slot = sd_bus_get_current_slot(bus));
+        assert_se(c = sd_bus_slot_get_userdata(slot));
+
+        context_read_machine_info(c);
+
+        return sd_bus_message_append(reply, "s", *(char**) userdata);
+}
+
+static int property_get_os_release_field(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        sd_bus_slot *slot;
+        Context *c;
+
+        /* As above, acquire the current context without this property's userdata offset added. */
+        assert_se(slot = sd_bus_get_current_slot(bus));
+        assert_se(c = sd_bus_slot_get_userdata(slot));
+
+        context_read_os_release(c);
+
+        return sd_bus_message_append(reply, "s", *(char**) userdata);
+}
+
 static int property_get_icon_name(
                 sd_bus *bus,
                 const char *path,
@@ -378,6 +507,8 @@ static int property_get_icon_name(
         Context *c = userdata;
         const char *name;
 
+        context_read_machine_info(c);
+
         if (isempty(c->data[PROP_ICON_NAME]))
                 name = n = context_fallback_icon_name(c);
         else
@@ -401,6 +532,8 @@ static int property_get_chassis(
         Context *c = userdata;
         const char *name;
 
+        context_read_machine_info(c);
+
         if (isempty(c->data[PROP_CHASSIS]))
                 name = fallback_chassis();
         else
@@ -409,11 +542,27 @@ static int property_get_chassis(
         return sd_bus_message_append(reply, "s", name);
 }
 
+static int property_get_uname_field(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        struct utsname u;
+
+        assert_se(uname(&u) >= 0);
+
+        return sd_bus_message_append(reply, "s", (char*) &u + PTR_TO_SIZE(userdata));
+}
+
 static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error *error) {
         Context *c = userdata;
         const char *name;
-        int interactive;
-        int r;
+        int interactive, r;
+        struct utsname u;
 
         assert(m);
         assert(c);
@@ -422,6 +571,8 @@ static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error *
         if (r < 0)
                 return r;
 
+        context_read_etc_hostname(c);
+
         if (isempty(name))
                 name = c->data[PROP_STATIC_HOSTNAME];
 
@@ -431,7 +582,8 @@ static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error *
         if (!hostname_is_valid(name, false))
                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", name);
 
-        if (streq_ptr(name, c->data[PROP_HOSTNAME]))
+        assert_se(uname(&u) >= 0);
+        if (streq_ptr(name, u.nodename))
                 return sd_bus_reply_method_return(m, NULL);
 
         r = bus_verify_polkit_async(
@@ -448,17 +600,13 @@ static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error *
         if (r == 0)
                 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
 
-        r = free_and_strdup(&c->data[PROP_HOSTNAME], name);
-        if (r < 0)
-                return r;
-
-        r = context_update_kernel_hostname(c);
+        r = context_update_kernel_hostname(c, name);
         if (r < 0) {
-                log_error_errno(r, "Failed to set host name: %m");
+                log_error_errno(r, "Failed to set hostname: %m");
                 return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %m");
         }
 
-        log_info("Changed host name to '%s'", strna(c->data[PROP_HOSTNAME]));
+        log_info("Changed hostname to '%s'", name);
 
         (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/hostname1", "org.freedesktop.hostname1", "Hostname", NULL);
 
@@ -480,6 +628,8 @@ static int method_set_static_hostname(sd_bus_message *m, void *userdata, sd_bus_
 
         name = empty_to_null(name);
 
+        context_read_etc_hostname(c);
+
         if (streq_ptr(name, c->data[PROP_STATIC_HOSTNAME]))
                 return sd_bus_reply_method_return(m, NULL);
 
@@ -504,19 +654,19 @@ static int method_set_static_hostname(sd_bus_message *m, void *userdata, sd_bus_
         if (r < 0)
                 return r;
 
-        r = context_update_kernel_hostname(c);
+        r = context_update_kernel_hostname(c, NULL);
         if (r < 0) {
-                log_error_errno(r, "Failed to set host name: %m");
+                log_error_errno(r, "Failed to set hostname: %m");
                 return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %m");
         }
 
         r = context_write_data_static_hostname(c);
         if (r < 0) {
-                log_error_errno(r, "Failed to write static host name: %m");
+                log_error_errno(r, "Failed to write static hostname: %m");
                 return sd_bus_error_set_errnof(error, r, "Failed to set static hostname: %m");
         }
 
-        log_info("Changed static host name to '%s'", strna(c->data[PROP_STATIC_HOSTNAME]));
+        log_info("Changed static hostname to '%s'", strna(c->data[PROP_STATIC_HOSTNAME]));
 
         (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/hostname1", "org.freedesktop.hostname1", "StaticHostname", NULL);
 
@@ -537,6 +687,8 @@ static int set_machine_info(Context *c, sd_bus_message *m, int prop, sd_bus_mess
 
         name = empty_to_null(name);
 
+        context_read_machine_info(c);
+
         if (streq_ptr(name, c->data[prop]))
                 return sd_bus_reply_method_return(m, NULL);
 
@@ -547,7 +699,7 @@ static int set_machine_info(Context *c, sd_bus_message *m, int prop, sd_bus_mess
                 if (prop == PROP_ICON_NAME && !filename_is_valid(name))
                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid icon name '%s'", name);
                 if (prop == PROP_PRETTY_HOSTNAME && string_has_cc(name, NULL))
-                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid pretty host name '%s'", name);
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid pretty hostname '%s'", name);
                 if (prop == PROP_CHASSIS && !valid_chassis(name))
                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid chassis '%s'", name);
                 if (prop == PROP_DEPLOYMENT && !valid_deployment(name))
@@ -585,7 +737,7 @@ static int set_machine_info(Context *c, sd_bus_message *m, int prop, sd_bus_mess
         }
 
         log_info("Changed %s to '%s'",
-                 prop == PROP_PRETTY_HOSTNAME ? "pretty host name" :
+                 prop == PROP_PRETTY_HOSTNAME ? "pretty hostname" :
                  prop == PROP_DEPLOYMENT ? "deployment" :
                  prop == PROP_LOCATION ? "location" :
                  prop == PROP_CHASSIS ? "chassis" : "icon name", strna(c->data[prop]));
@@ -625,13 +777,27 @@ static int method_set_location(sd_bus_message *m, void *userdata, sd_bus_error *
 static int method_get_product_uuid(sd_bus_message *m, void *userdata, sd_bus_error *error) {
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
         Context *c = userdata;
+        bool has_uuid = false;
         int interactive, r;
+        sd_id128_t uuid;
 
         assert(m);
         assert(c);
 
-        if (!c->has_uuid)
-                return sd_bus_error_set(error, BUS_ERROR_NO_PRODUCT_UUID, "Failed to read product UUID from /sys/class/dmi/id/product_uuid");
+        r = id128_read("/sys/class/dmi/id/product_uuid", ID128_UUID, &uuid);
+        if (r == -ENOENT)
+                r = id128_read("/sys/firmware/devicetree/base/vm,uuid", ID128_UUID, &uuid);
+        if (r < 0)
+                log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r,
+                               "Failed to read product UUID, ignoring: %m");
+        else if (sd_id128_is_null(uuid) || sd_id128_is_allf(uuid))
+                log_debug("DMI product UUID " SD_ID128_FORMAT_STR " is all 0x00 or all 0xFF, ignoring.", SD_ID128_FORMAT_VAL(uuid));
+        else
+                has_uuid = true;
+
+        if (!has_uuid)
+                return sd_bus_error_set(error, BUS_ERROR_NO_PRODUCT_UUID,
+                                        "Failed to read product UUID from firmware.");
 
         r = sd_bus_message_read(m, "b", &interactive);
         if (r < 0)
@@ -655,7 +821,7 @@ static int method_get_product_uuid(sd_bus_message *m, void *userdata, sd_bus_err
         if (r < 0)
                 return r;
 
-        r = sd_bus_message_append_array(reply, 'y', &c->uuid, sizeof(c->uuid));
+        r = sd_bus_message_append_array(reply, 'y', &uuid, sizeof(uuid));
         if (r < 0)
                 return r;
 
@@ -664,30 +830,86 @@ static int method_get_product_uuid(sd_bus_message *m, void *userdata, sd_bus_err
 
 static const sd_bus_vtable hostname_vtable[] = {
         SD_BUS_VTABLE_START(0),
-        SD_BUS_PROPERTY("Hostname", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_HOSTNAME, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
-        SD_BUS_PROPERTY("StaticHostname", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_STATIC_HOSTNAME, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
-        SD_BUS_PROPERTY("PrettyHostname", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_PRETTY_HOSTNAME, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("Hostname", "s", property_get_hostname, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("StaticHostname", "s", property_get_static_hostname, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("PrettyHostname", "s", property_get_machine_info_field, offsetof(Context, data) + sizeof(char*) * PROP_PRETTY_HOSTNAME, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("IconName", "s", property_get_icon_name, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("Chassis", "s", property_get_chassis, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
-        SD_BUS_PROPERTY("Deployment", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_DEPLOYMENT, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
-        SD_BUS_PROPERTY("Location", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_LOCATION, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
-        SD_BUS_PROPERTY("KernelName", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_KERNEL_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("KernelRelease", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_KERNEL_RELEASE, SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("KernelVersion", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_KERNEL_VERSION, SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("OperatingSystemPrettyName", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_OS_PRETTY_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("OperatingSystemCPEName", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_OS_CPE_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("HomeURL", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_HOME_URL, SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_METHOD("SetHostname", "sb", NULL, method_set_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetStaticHostname", "sb", NULL, method_set_static_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetPrettyHostname", "sb", NULL, method_set_pretty_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetIconName", "sb", NULL, method_set_icon_name, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetChassis", "sb", NULL, method_set_chassis, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetDeployment", "sb", NULL, method_set_deployment, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetLocation", "sb", NULL, method_set_location, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("GetProductUUID", "b", "ay", method_get_product_uuid, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_PROPERTY("Deployment", "s", property_get_machine_info_field, offsetof(Context, data) + sizeof(char*) * PROP_DEPLOYMENT, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("Location", "s", property_get_machine_info_field, offsetof(Context, data) + sizeof(char*) * PROP_LOCATION, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("KernelName", "s", property_get_uname_field, offsetof(struct utsname, sysname), SD_BUS_VTABLE_ABSOLUTE_OFFSET|SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("KernelRelease", "s", property_get_uname_field, offsetof(struct utsname, release), SD_BUS_VTABLE_ABSOLUTE_OFFSET|SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("KernelVersion", "s", property_get_uname_field, offsetof(struct utsname, version), SD_BUS_VTABLE_ABSOLUTE_OFFSET|SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("OperatingSystemPrettyName", "s", property_get_os_release_field, offsetof(Context, data) + sizeof(char*) * PROP_OS_PRETTY_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("OperatingSystemCPEName", "s", property_get_os_release_field, offsetof(Context, data) + sizeof(char*) * PROP_OS_CPE_NAME, SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("HomeURL", "s", property_get_os_release_field, offsetof(Context, data) + sizeof(char*) * PROP_OS_HOME_URL, SD_BUS_VTABLE_PROPERTY_CONST),
+
+        SD_BUS_METHOD_WITH_NAMES("SetHostname",
+                                 "sb",
+                                 SD_BUS_PARAM(hostname)
+                                 SD_BUS_PARAM(interactive),
+                                 NULL,,
+                                 method_set_hostname,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("SetStaticHostname",
+                                 "sb",
+                                 SD_BUS_PARAM(hostname)
+                                 SD_BUS_PARAM(interactive),
+                                 NULL,,
+                                 method_set_static_hostname,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("SetPrettyHostname",
+                                 "sb",
+                                 SD_BUS_PARAM(hostname)
+                                 SD_BUS_PARAM(interactive),
+                                 NULL,,
+                                 method_set_pretty_hostname,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("SetIconName",
+                                 "sb",
+                                 SD_BUS_PARAM(icon)
+                                 SD_BUS_PARAM(interactive),
+                                 NULL,,
+                                 method_set_icon_name,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("SetChassis",
+                                 "sb",
+                                 SD_BUS_PARAM(chassis)
+                                 SD_BUS_PARAM(interactive),
+                                 NULL,,
+                                 method_set_chassis,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("SetDeployment",
+                                 "sb",
+                                 SD_BUS_PARAM(deployment)
+                                 SD_BUS_PARAM(interactive),
+                                 NULL,,
+                                 method_set_deployment,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("SetLocation",
+                                 "sb",
+                                 SD_BUS_PARAM(location)
+                                 SD_BUS_PARAM(interactive),
+                                 NULL,,
+                                 method_set_location,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("GetProductUUID",
+                                 "b",
+                                 SD_BUS_PARAM(interactive),
+                                 "ay",
+                                 SD_BUS_PARAM(uuid),
+                                 method_get_product_uuid,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+
         SD_BUS_VTABLE_END,
 };
 
+static const BusObjectImplementation manager_object = {
+        "/org/freedesktop/hostname1",
+        "org.freedesktop.hostname1",
+        .vtables = BUS_VTABLES(hostname_vtable),
+};
+
 static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         int r;
@@ -700,9 +922,13 @@ static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
         if (r < 0)
                 return log_error_errno(r, "Failed to get system bus connection: %m");
 
-        r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/hostname1", "org.freedesktop.hostname1", hostname_vtable, c);
+        r = bus_add_implementation(bus, &manager_object, c);
+        if (r < 0)
+                return r;
+
+        r = bus_log_control_api_register(bus);
         if (r < 0)
-                return log_error_errno(r, "Failed to register object: %m");
+                return r;
 
         r = sd_bus_request_name_async(bus, NULL, "org.freedesktop.hostname1", 0, NULL, NULL);
         if (r < 0)
@@ -718,20 +944,26 @@ static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
 }
 
 static int run(int argc, char *argv[]) {
-        _cleanup_(context_clear) Context context = {};
+        _cleanup_(context_destroy) Context context = {};
         _cleanup_(sd_event_unrefp) sd_event *event = NULL;
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         int r;
 
         log_setup_service();
 
+        r = service_parse_argv("systemd-hostnamed.service",
+                               "Manage the system hostname and related metadata.",
+                               BUS_IMPLEMENTATIONS(&manager_object,
+                                                   &log_control_object),
+                               argc, argv);
+        if (r <= 0)
+                return r;
+
         umask(0022);
-        mac_selinux_init();
 
-        if (argc != 1) {
-                log_error("This program takes no arguments.");
-                return -EINVAL;
-        }
+        r = mac_selinux_init();
+        if (r < 0)
+                return r;
 
         assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
 
@@ -753,10 +985,6 @@ static int run(int argc, char *argv[]) {
         if (r < 0)
                 return r;
 
-        r = context_read_data(&context);
-        if (r < 0)
-                return log_error_errno(r, "Failed to read hostname and machine information: %m");
-
         r = bus_event_loop_with_idle(event, bus, "org.freedesktop.hostname1", DEFAULT_EXIT_USEC, NULL, NULL);
         if (r < 0)
                 return log_error_errno(r, "Failed to run event loop: %m");
index 5bedc0b6942a37d4f06ac40ff21e8cd824b3aef9..5969a82b29917547b337e7cddc2a0b5fc8d8c863 100644 (file)
@@ -17,8 +17,8 @@
         <vendor_url>http://www.freedesktop.org/wiki/Software/systemd</vendor_url>
 
         <action id="org.freedesktop.hostname1.set-hostname">
-                <description gettext-domain="systemd">Set host name</description>
-                <message gettext-domain="systemd">Authentication is required to set the local host name.</message>
+                <description gettext-domain="systemd">Set hostname</description>
+                <message gettext-domain="systemd">Authentication is required to set the local hostname.</message>
                 <defaults>
                         <allow_any>auth_admin_keep</allow_any>
                         <allow_inactive>auth_admin_keep</allow_inactive>
@@ -27,8 +27,8 @@
         </action>
 
         <action id="org.freedesktop.hostname1.set-static-hostname">
-                <description gettext-domain="systemd">Set static host name</description>
-                <message gettext-domain="systemd">Authentication is required to set the statically configured local host name, as well as the pretty host name.</message>
+                <description gettext-domain="systemd">Set static hostname</description>
+                <message gettext-domain="systemd">Authentication is required to set the statically configured local hostname, as well as the pretty hostname.</message>
                 <defaults>
                         <allow_any>auth_admin_keep</allow_any>
                         <allow_inactive>auth_admin_keep</allow_inactive>
index 651647b3f0dd95972187c549710258f6da2317bb..eb038a8b55ea8d833b4b5b6dd6cbee78d927f9fd 100644 (file)
@@ -125,7 +125,9 @@ static int run(int argc, char *argv[]) {
         if (r <= 0)
                 return r;
 
-        mac_selinux_init();
+        r = mac_selinux_init();
+        if (r < 0)
+                return r;
 
         return hwdb_main(argc, argv);
 }
index 19435f80fea40726960b69848872e6dff57741fa..236043bf8a4c06c7aec0f2bc1405cc0977fb8f47 100644 (file)
@@ -139,7 +139,7 @@ static int verb_show(int argc, char **argv, void *userdata) {
         if (table) {
                 r = table_print(table, NULL);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to print table: %m");
+                        return table_log_print_error(r);
         }
 
         return 0;
@@ -249,9 +249,7 @@ static int id128_main(int argc, char *argv[]) {
 static int run(int argc, char *argv[]) {
         int r;
 
-        log_show_color(true);
-        log_parse_environment();
-        log_open();
+        log_setup_cli();
 
         r = parse_argv(argc, argv);
         if (r <= 0)
index 5f21033db5a04ab01aef226175528cef7219819b..261fbece7159640030bdb7c422adeffb498cffd6 100644 (file)
@@ -166,9 +166,8 @@ CurlGlue *curl_glue_unref(CurlGlue *g) {
         if (g->curl)
                 curl_multi_cleanup(g->curl);
 
-        while ((io = hashmap_steal_first(g->ios))) {
+        while ((io = hashmap_steal_first(g->ios)))
                 sd_event_source_unref(io);
-        }
 
         hashmap_free(g->ios);
 
index 1e50d31bc2bbf32023fd6060311030458cd847c6..5d2bf22fb25d7d5d27ee561aaf1edfa5506f9fb4 100644 (file)
@@ -220,13 +220,10 @@ static int tar_import_fork_tar(TarImport *i) {
 
         (void) mkdir_parents_label(i->temp_path, 0700);
 
-        r = btrfs_subvol_make(i->temp_path);
-        if (r == -ENOTTY) {
-                if (mkdir(i->temp_path, 0755) < 0)
-                        return log_error_errno(errno, "Failed to create directory %s: %m", i->temp_path);
-        } else if (r < 0)
-                return log_error_errno(r, "Failed to create subvolume %s: %m", i->temp_path);
-        else
+        r = btrfs_subvol_make_fallback(i->temp_path, 0755);
+        if (r < 0)
+                return log_error_errno(r, "Failed to create directory/subvolume %s: %m", i->temp_path);
+        if (r > 0) /* actually btrfs subvol */
                 (void) import_assign_pool_quota_and_warn(i->temp_path);
 
         i->tar_fd = import_fork_tar_x(i->temp_path, &i->tar_pid);
index 6e868ee50ae4bd488d092a6b225c605e4e4dff2a..038dd3a6c6187b15c2d5f3337e8ac8bbc9009c2e 100644 (file)
@@ -7,6 +7,8 @@
 
 #include "alloc-util.h"
 #include "bus-common-errors.h"
+#include "bus-get-properties.h"
+#include "bus-log-control-api.h"
 #include "bus-polkit.h"
 #include "def.h"
 #include "fd-util.h"
@@ -20,6 +22,7 @@
 #include "parse-util.h"
 #include "path-util.h"
 #include "process-util.h"
+#include "service-util.h"
 #include "signal-util.h"
 #include "socket-util.h"
 #include "stat-util.h"
@@ -487,11 +490,13 @@ static int transfer_start(Transfer *t) {
 
         t->stdin_fd = safe_close(t->stdin_fd);
 
-        r = sd_event_add_child(t->manager->event, &t->pid_event_source, t->pid, WEXITED, transfer_on_pid, t);
+        r = sd_event_add_child(t->manager->event, &t->pid_event_source,
+                               t->pid, WEXITED, transfer_on_pid, t);
         if (r < 0)
                 return r;
 
-        r = sd_event_add_io(t->manager->event, &t->log_event_source, t->log_fd, EPOLLIN, transfer_on_log, t);
+        r = sd_event_add_io(t->manager->event, &t->log_event_source,
+                            t->log_fd, EPOLLIN, transfer_on_log, t);
         if (r < 0)
                 return r;
 
@@ -545,20 +550,16 @@ static int manager_on_notify(sd_event_source *s, int fd, uint32_t revents, void
                 .iov_base = buf,
                 .iov_len = sizeof(buf)-1,
         };
-        union {
-                struct cmsghdr cmsghdr;
-                uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) +
-                            CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX)];
-        } control = {};
+        CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred)) +
+                         CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX)) control;
         struct msghdr msghdr = {
                 .msg_iov = &iovec,
                 .msg_iovlen = 1,
                 .msg_control = &control,
                 .msg_controllen = sizeof(control),
         };
-        struct ucred *ucred = NULL;
+        struct ucred *ucred;
         Manager *m = userdata;
-        struct cmsghdr *cmsg;
         char *p, *e;
         Transfer *t;
         Iterator i;
@@ -573,17 +574,12 @@ static int manager_on_notify(sd_event_source *s, int fd, uint32_t revents, void
 
         cmsg_close_all(&msghdr);
 
-        CMSG_FOREACH(cmsg, &msghdr)
-                if (cmsg->cmsg_level == SOL_SOCKET &&
-                    cmsg->cmsg_type == SCM_CREDENTIALS &&
-                    cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred)))
-                        ucred = (struct ucred*) CMSG_DATA(cmsg);
-
         if (msghdr.msg_flags & MSG_TRUNC) {
                 log_warning("Got overly long notification datagram, ignoring.");
                 return 0;
         }
 
+        ucred = CMSG_FIND_DATA(&msghdr, SOL_SOCKET, SCM_CREDENTIALS, struct ucred);
         if (!ucred || ucred->pid <= 0) {
                 log_warning("Got notification datagram lacking credential information, ignoring.");
                 return 0;
@@ -662,7 +658,8 @@ static int manager_new(Manager **ret) {
         if (r < 0)
                 return r;
 
-        r = sd_event_add_io(m->event, &m->notify_event_source, m->notify_fd, EPOLLIN, manager_on_notify, m);
+        r = sd_event_add_io(m->event, &m->notify_event_source,
+                            m->notify_fd, EPOLLIN, manager_on_notify, m);
         if (r < 0)
                 return r;
 
@@ -723,13 +720,15 @@ static int method_import_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_
                 return -EINVAL;
 
         if (!machine_name_is_valid(local))
-                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+                                         "Local name %s is invalid", local);
 
         r = setup_machine_directory(error);
         if (r < 0)
                 return r;
 
-        type = streq_ptr(sd_bus_message_get_member(msg), "ImportTar") ? TRANSFER_IMPORT_TAR : TRANSFER_IMPORT_RAW;
+        type = streq_ptr(sd_bus_message_get_member(msg), "ImportTar") ?
+                TRANSFER_IMPORT_TAR : TRANSFER_IMPORT_RAW;
 
         r = transfer_new(m, &t);
         if (r < 0)
@@ -791,7 +790,8 @@ static int method_import_fs(sd_bus_message *msg, void *userdata, sd_bus_error *e
                 return r;
 
         if (!machine_name_is_valid(local))
-                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+                                         "Local name %s is invalid", local);
 
         r = setup_machine_directory(error);
         if (r < 0)
@@ -855,7 +855,8 @@ static int method_export_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_
                 return r;
 
         if (!machine_name_is_valid(local))
-                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+                                         "Local name %s is invalid", local);
 
         if (fstat(fd, &st) < 0)
                 return -errno;
@@ -863,7 +864,8 @@ static int method_export_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_
         if (!S_ISREG(st.st_mode) && !S_ISFIFO(st.st_mode))
                 return -EINVAL;
 
-        type = streq_ptr(sd_bus_message_get_member(msg), "ExportTar") ? TRANSFER_EXPORT_TAR : TRANSFER_EXPORT_RAW;
+        type = streq_ptr(sd_bus_message_get_member(msg), "ExportTar") ?
+                TRANSFER_EXPORT_TAR : TRANSFER_EXPORT_RAW;
 
         r = transfer_new(m, &t);
         if (r < 0)
@@ -927,28 +929,33 @@ static int method_pull_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_er
                 return r;
 
         if (!http_url_is_valid(remote))
-                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "URL %s is invalid", remote);
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+                                         "URL %s is invalid", remote);
 
         if (isempty(local))
                 local = NULL;
         else if (!machine_name_is_valid(local))
-                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+                                         "Local name %s is invalid", local);
 
         if (isempty(verify))
                 v = IMPORT_VERIFY_SIGNATURE;
         else
                 v = import_verify_from_string(verify);
         if (v < 0)
-                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown verification mode %s", verify);
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+                                         "Unknown verification mode %s", verify);
 
         r = setup_machine_directory(error);
         if (r < 0)
                 return r;
 
-        type = streq_ptr(sd_bus_message_get_member(msg), "PullTar") ? TRANSFER_PULL_TAR : TRANSFER_PULL_RAW;
+        type = streq_ptr(sd_bus_message_get_member(msg), "PullTar") ?
+                TRANSFER_PULL_TAR : TRANSFER_PULL_RAW;
 
         if (manager_find(m, type, remote))
-                return sd_bus_error_setf(error, BUS_ERROR_TRANSFER_IN_PROGRESS, "Transfer for %s already in progress.", remote);
+                return sd_bus_error_setf(error, BUS_ERROR_TRANSFER_IN_PROGRESS,
+                                         "Transfer for %s already in progress.", remote);
 
         r = transfer_new(m, &t);
         if (r < 0)
@@ -1108,36 +1115,14 @@ static int property_get_progress(
 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, transfer_type, TransferType);
 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_verify, import_verify, ImportVerify);
 
-static const sd_bus_vtable transfer_vtable[] = {
-        SD_BUS_VTABLE_START(0),
-        SD_BUS_PROPERTY("Id", "u", NULL, offsetof(Transfer, id), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("Local", "s", NULL, offsetof(Transfer, local), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("Remote", "s", NULL, offsetof(Transfer, remote), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Transfer, type), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("Verify", "s", property_get_verify, offsetof(Transfer, verify), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("Progress", "d", property_get_progress, 0, 0),
-        SD_BUS_METHOD("Cancel", NULL, NULL, method_cancel, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_SIGNAL("LogMessage", "us", 0),
-        SD_BUS_VTABLE_END,
-};
-
-static const sd_bus_vtable manager_vtable[] = {
-        SD_BUS_VTABLE_START(0),
-        SD_BUS_METHOD("ImportTar", "hsbb", "uo", method_import_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("ImportRaw", "hsbb", "uo", method_import_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("ImportFileSystem", "hsbb", "uo", method_import_fs, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("ExportTar", "shs", "uo", method_export_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("ExportRaw", "shs", "uo", method_export_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("PullTar", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("PullRaw", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("ListTransfers", NULL, "a(usssdo)", method_list_transfers, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("CancelTransfer", "u", NULL, method_cancel_transfer, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_SIGNAL("TransferNew", "uo", 0),
-        SD_BUS_SIGNAL("TransferRemoved", "uos", 0),
-        SD_BUS_VTABLE_END,
-};
+static int transfer_object_find(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                void *userdata,
+                void **found,
+                sd_bus_error *error) {
 
-static int transfer_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
         Manager *m = userdata;
         Transfer *t;
         const char *p;
@@ -1166,7 +1151,13 @@ static int transfer_object_find(sd_bus *bus, const char *path, const char *inter
         return 1;
 }
 
-static int transfer_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
+static int transfer_node_enumerator(
+                sd_bus *bus,
+                const char *path,
+                void *userdata,
+                char ***nodes,
+                sd_bus_error *error) {
+
         _cleanup_strv_free_ char **l = NULL;
         Manager *m = userdata;
         Transfer *t;
@@ -1191,22 +1182,159 @@ static int transfer_node_enumerator(sd_bus *bus, const char *path, void *userdat
         return 1;
 }
 
+static const sd_bus_vtable transfer_vtable[] = {
+        SD_BUS_VTABLE_START(0),
+
+        SD_BUS_PROPERTY("Id", "u", NULL, offsetof(Transfer, id), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("Local", "s", NULL, offsetof(Transfer, local), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("Remote", "s", NULL, offsetof(Transfer, remote), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Transfer, type), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("Verify", "s", property_get_verify, offsetof(Transfer, verify), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("Progress", "d", property_get_progress, 0, 0),
+
+        SD_BUS_METHOD("Cancel", NULL, NULL, method_cancel, SD_BUS_VTABLE_UNPRIVILEGED),
+
+        SD_BUS_SIGNAL_WITH_NAMES("LogMessage",
+                                 "us",
+                                 SD_BUS_PARAM(priority)
+                                 SD_BUS_PARAM(line),
+                                 0),
+
+        SD_BUS_VTABLE_END,
+};
+
+static const BusObjectImplementation transfer_object = {
+        "/org/freedesktop/import1/transfer",
+        "org.freedesktop.import1.Transfer",
+        .fallback_vtables = BUS_FALLBACK_VTABLES({transfer_vtable, transfer_object_find}),
+        .node_enumerator = transfer_node_enumerator,
+};
+
+static const sd_bus_vtable manager_vtable[] = {
+        SD_BUS_VTABLE_START(0),
+
+        SD_BUS_METHOD_WITH_NAMES("ImportTar",
+                                 "hsbb",
+                                 SD_BUS_PARAM(fd)
+                                 SD_BUS_PARAM(local_name)
+                                 SD_BUS_PARAM(force)
+                                 SD_BUS_PARAM(read_only),
+                                 "uo",
+                                 SD_BUS_PARAM(transfer_id)
+                                 SD_BUS_PARAM(transfer_path),
+                                 method_import_tar_or_raw,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("ImportRaw",
+                                 "hsbb",
+                                 SD_BUS_PARAM(fd)
+                                 SD_BUS_PARAM(local_name)
+                                 SD_BUS_PARAM(force)
+                                 SD_BUS_PARAM(read_only),
+                                 "uo",
+                                 SD_BUS_PARAM(transfer_id)
+                                 SD_BUS_PARAM(transfer_path),
+                                 method_import_tar_or_raw,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("ImportFileSystem",
+                                 "hsbb",
+                                 SD_BUS_PARAM(fd)
+                                 SD_BUS_PARAM(local_name)
+                                 SD_BUS_PARAM(force)
+                                 SD_BUS_PARAM(read_only),
+                                 "uo",
+                                 SD_BUS_PARAM(transfer_id)
+                                 SD_BUS_PARAM(transfer_path),
+                                 method_import_fs,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("ExportTar",
+                                 "shs",
+                                 SD_BUS_PARAM(local_name)
+                                 SD_BUS_PARAM(fd)
+                                 SD_BUS_PARAM(format),
+                                 "uo",
+                                 SD_BUS_PARAM(transfer_id)
+                                 SD_BUS_PARAM(transfer_path),
+                                 method_export_tar_or_raw,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("ExportRaw",
+                                 "shs",
+                                 SD_BUS_PARAM(local_name)
+                                 SD_BUS_PARAM(fd)
+                                 SD_BUS_PARAM(format),
+                                 "uo",
+                                 SD_BUS_PARAM(transfer_id)
+                                 SD_BUS_PARAM(transfer_path),
+                                 method_export_tar_or_raw,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("PullTar",
+                                 "sssb",
+                                 SD_BUS_PARAM(url)
+                                 SD_BUS_PARAM(local_name)
+                                 SD_BUS_PARAM(verify_mode)
+                                 SD_BUS_PARAM(force),
+                                 "uo",
+                                 SD_BUS_PARAM(transfer_id)
+                                 SD_BUS_PARAM(transfer_path),
+                                 method_pull_tar_or_raw,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("PullRaw",
+                                 "sssb",
+                                 SD_BUS_PARAM(url)
+                                 SD_BUS_PARAM(local_name)
+                                 SD_BUS_PARAM(verify_mode)
+                                 SD_BUS_PARAM(force),
+                                 "uo",
+                                 SD_BUS_PARAM(transfer_id)
+                                 SD_BUS_PARAM(transfer_path),
+                                 method_pull_tar_or_raw,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("ListTransfers",
+                                 NULL,,
+                                 "a(usssdo)",
+                                 SD_BUS_PARAM(transfers),
+                                 method_list_transfers,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("CancelTransfer",
+                                 "u",
+                                 SD_BUS_PARAM(transfer_id),
+                                 NULL,,
+                                 method_cancel_transfer,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+
+        SD_BUS_SIGNAL_WITH_NAMES("TransferNew",
+                                 "uo",
+                                 SD_BUS_PARAM(transfer_id)
+                                 SD_BUS_PARAM(transfer_path),
+                                 0),
+        SD_BUS_SIGNAL_WITH_NAMES("TransferRemoved",
+                                 "uos",
+                                 SD_BUS_PARAM(transfer_id)
+                                 SD_BUS_PARAM(transfer_path)
+                                 SD_BUS_PARAM(result),
+                                 0),
+
+        SD_BUS_VTABLE_END,
+};
+
+static const BusObjectImplementation manager_object = {
+        "/org/freedesktop/import1",
+        "org.freedesktop.import1.Manager",
+        .vtables = BUS_VTABLES(manager_vtable),
+        .children = BUS_IMPLEMENTATIONS(&transfer_object),
+};
+
 static int manager_add_bus_objects(Manager *m) {
         int r;
 
         assert(m);
 
-        r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/import1", "org.freedesktop.import1.Manager", manager_vtable, m);
-        if (r < 0)
-                return log_error_errno(r, "Failed to register object: %m");
-
-        r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/import1/transfer", "org.freedesktop.import1.Transfer", transfer_vtable, transfer_object_find, m);
+        r = bus_add_implementation(m->bus, &manager_object, m);
         if (r < 0)
-                return log_error_errno(r, "Failed to register object: %m");
+                return r;
 
-        r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/import1/transfer", transfer_node_enumerator, m);
+        r = bus_log_control_api_register(m->bus);
         if (r < 0)
-                return log_error_errno(r, "Failed to add transfer enumerator: %m");
+                return r;
 
         r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.import1", 0, NULL, NULL);
         if (r < 0)
@@ -1243,12 +1371,15 @@ static int run(int argc, char *argv[]) {
 
         log_setup_service();
 
-        umask(0022);
+        r = service_parse_argv("systemd-importd.service",
+                               "VM and container image import and export service.",
+                               BUS_IMPLEMENTATIONS(&manager_object,
+                                                   &log_control_object),
+                               argc, argv);
+        if (r <= 0)
+                return r;
 
-        if (argc != 1) {
-                log_error("This program takes no arguments.");
-                return -EINVAL;
-        }
+        umask(0022);
 
         assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0);
 
index 3930578a8cee3a6f58ac6970266cffbf2ee8e079..ede28bee1b1d0c88a856bb75b881062af3f0f218 100644 (file)
@@ -415,13 +415,10 @@ static int tar_pull_job_on_open_disk_tar(PullJob *j) {
 
         mkdir_parents_label(i->temp_path, 0700);
 
-        r = btrfs_subvol_make(i->temp_path);
-        if (r == -ENOTTY) {
-                if (mkdir(i->temp_path, 0755) < 0)
-                        return log_error_errno(errno, "Failed to create directory %s: %m", i->temp_path);
-        } else if (r < 0)
-                return log_error_errno(r, "Failed to create subvolume %s: %m", i->temp_path);
-        else
+        r = btrfs_subvol_make_fallback(i->temp_path, 0755);
+        if (r < 0)
+                return log_error_errno(r, "Failed to create directory/subvolume %s: %m", i->temp_path);
+        if (r > 0) /* actually btrfs subvol */
                 (void) import_assign_pool_quota_and_warn(i->temp_path);
 
         j->disk_fd = import_fork_tar_x(i->temp_path, &i->tar_pid);
index 150d0fb1990950b303f674493cbca21350d4317c..7eee197665f2eef51b657b269404b3b911611ae5 100644 (file)
 #include "alloc-util.h"
 #include "bus-error.h"
 #include "bus-util.h"
+#include "daemon-util.h"
 #include "def.h"
 #include "fd-util.h"
 #include "format-util.h"
 #include "initreq.h"
 #include "list.h"
 #include "log.h"
+#include "main-func.h"
 #include "memory-util.h"
 #include "process-util.h"
 #include "special.h"
@@ -68,11 +70,9 @@ static const char *translate_runlevel(int runlevel, bool *isolate) {
                 { '6', SPECIAL_REBOOT_TARGET,     false },
         };
 
-        unsigned i;
-
         assert(isolate);
 
-        for (i = 0; i < ELEMENTSOF(table); i++)
+        for (size_t i = 0; i < ELEMENTSOF(table); i++)
                 if (table[i].runlevel == runlevel) {
                         *isolate = table[i].isolate;
                         if (runlevel == '6' && kexec_loaded())
@@ -228,6 +228,7 @@ static void fifo_free(Fifo *f) {
 
         free(f);
 }
+DEFINE_TRIVIAL_CLEANUP_FUNC(Fifo*, fifo_free);
 
 static void server_done(Server *s) {
         assert(s);
@@ -241,77 +242,49 @@ static void server_done(Server *s) {
 
 static int server_init(Server *s, unsigned n_sockets) {
         int r;
-        unsigned i;
+
+        /* This function will leave s partially initialized on failure. Caller needs to clean up. */
 
         assert(s);
         assert(n_sockets > 0);
 
-        zero(*s);
-
         s->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
-        if (s->epoll_fd < 0) {
-                r = log_error_errno(errno,
-                                    "Failed to create epoll object: %m");
-                goto fail;
-        }
+        if (s->epoll_fd < 0)
+                return log_error_errno(errno, "Failed to create epoll object: %m");
 
-        for (i = 0; i < n_sockets; i++) {
-                struct epoll_event ev;
-                Fifo *f;
-                int fd;
-
-                fd = SD_LISTEN_FDS_START+i;
+        for (unsigned i = 0; i < n_sockets; i++) {
+                _cleanup_(fifo_freep) Fifo *f = NULL;
+                int fd = SD_LISTEN_FDS_START + i;
 
                 r = sd_is_fifo(fd, NULL);
-                if (r < 0) {
-                        log_error_errno(r, "Failed to determine file descriptor type: %m");
-                        goto fail;
-                }
-
-                if (!r) {
-                        log_error("Wrong file descriptor type.");
-                        r = -EINVAL;
-                        goto fail;
-                }
+                if (r < 0)
+                        return log_error_errno(r, "Failed to determine file descriptor type: %m");
+                if (!r)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Wrong file descriptor type.");
 
                 f = new0(Fifo, 1);
-                if (!f) {
-                        r = -ENOMEM;
-                        log_error_errno(errno, "Failed to create fifo object: %m");
-                        goto fail;
-                }
+                if (!f)
+                        return log_oom();
 
-                f->fd = -1;
+                struct epoll_event ev = {
+                        .events = EPOLLIN,
+                        .data.ptr = f,
+                };
 
-                zero(ev);
-                ev.events = EPOLLIN;
-                ev.data.ptr = f;
-                if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
-                        r = -errno;
-                        fifo_free(f);
-                        log_error_errno(errno, "Failed to add fifo fd to epoll object: %m");
-                        goto fail;
-                }
+                if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0)
+                        return log_error_errno(errno, "Failed to add fifo fd to epoll object: %m");
 
                 f->fd = fd;
-                LIST_PREPEND(fifo, s->fifos, f);
                 f->server = s;
+                LIST_PREPEND(fifo, s->fifos, TAKE_PTR(f));
                 s->n_fifos++;
         }
 
         r = bus_connect_system_systemd(&s->bus);
-        if (r < 0) {
-                log_error_errno(r, "Failed to get D-Bus connection: %m");
-                r = -EIO;
-                goto fail;
-        }
+        if (r < 0)
+                return log_error_errno(r, "Failed to get D-Bus connection: %m");
 
         return 0;
-
-fail:
-        server_done(s);
-
-        return r;
 }
 
 static int process_event(Server *s, struct epoll_event *ev) {
@@ -335,43 +308,33 @@ static int process_event(Server *s, struct epoll_event *ev) {
         return 0;
 }
 
-int main(int argc, char *argv[]) {
-        Server server;
-        int r = EXIT_FAILURE, n;
+static int run(int argc, char *argv[]) {
+        _cleanup_(server_done) Server server = { .epoll_fd = -1 };
+        _cleanup_(notify_on_cleanup) const char *notify_stop = NULL;
+        int r, n;
 
-        if (getppid() != 1) {
-                log_error("This program should be invoked by init only.");
-                return EXIT_FAILURE;
-        }
-
-        if (argc > 1) {
-                log_error("This program does not take arguments.");
-                return EXIT_FAILURE;
-        }
+        if (argc > 1)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "This program does not take arguments.");
 
         log_setup_service();
 
         umask(0022);
 
         n = sd_listen_fds(true);
-        if (n < 0) {
-                log_error_errno(r, "Failed to read listening file descriptors from environment: %m");
-                return EXIT_FAILURE;
-        }
+        if (n < 0)
+                return log_error_errno(errno,
+                                       "Failed to read listening file descriptors from environment: %m");
 
-        if (n <= 0 || n > SERVER_FD_MAX) {
-                log_error("No or too many file descriptors passed.");
-                return EXIT_FAILURE;
-        }
-
-        if (server_init(&server, (unsigned) n) < 0)
-                return EXIT_FAILURE;
+        if (n <= 0 || n > SERVER_FD_MAX)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "No or too many file descriptors passed.");
 
-        log_debug("systemd-initctl running as pid "PID_FMT, getpid_cached());
+        r = server_init(&server, (unsigned) n);
+        if (r < 0)
+                return r;
 
-        sd_notify(false,
-                  "READY=1\n"
-                  "STATUS=Processing requests...");
+        notify_stop = notify_start(NOTIFY_READY, NOTIFY_STOPPING);
 
         while (!server.quit) {
                 struct epoll_event event;
@@ -381,27 +344,17 @@ int main(int argc, char *argv[]) {
                 if (k < 0) {
                         if (errno == EINTR)
                                 continue;
-                        log_error_errno(errno, "epoll_wait() failed: %m");
-                        goto fail;
+                        return log_error_errno(errno, "epoll_wait() failed: %m");
                 }
-
-                if (k <= 0)
+                if (k == 0)
                         break;
 
-                if (process_event(&server, &event) < 0)
-                        goto fail;
+                r = process_event(&server, &event);
+                if (r < 0)
+                        return r;
         }
 
-        r = EXIT_SUCCESS;
-
-        log_debug("systemd-initctl stopped as pid "PID_FMT, getpid_cached());
-
-fail:
-        sd_notify(false,
-                  "STOPPING=1\n"
-                  "STATUS=Shutting down...");
-
-        server_done(&server);
-
-        return r;
+        return 0;
 }
+
+DEFINE_MAIN_FUNCTION(run);
index 825f43ce6817cc60eb170fa89e82ace5e3f02d24..48106d1bdbb75e08fd996ee8dac0c5d82eb21dda 100644 (file)
@@ -123,17 +123,15 @@ static int request_meta_ensure_tmp(RequestMeta *m) {
         if (m->tmp)
                 rewind(m->tmp);
         else {
-                int fd;
+                _cleanup_close_ int fd = -1;
 
                 fd = open_tmpfile_unlinkable("/tmp", O_RDWR|O_CLOEXEC);
                 if (fd < 0)
                         return fd;
 
-                m->tmp = fdopen(fd, "w+");
-                if (!m->tmp) {
-                        safe_close(fd);
+                m->tmp = take_fdopen(&fd, "w+");
+                if (!m->tmp)
                         return -errno;
-                }
         }
 
         return 0;
@@ -908,7 +906,7 @@ static int parse_argv(int argc, char *argv[]) {
                         if (arg_key_pem)
                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                                        "Key file specified twice");
-                        r = read_full_file(optarg, &arg_key_pem, NULL);
+                        r = read_full_file_full(AT_FDCWD, optarg, READ_FULL_FILE_CONNECT_SOCKET, &arg_key_pem, NULL);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to read key file: %m");
                         assert(arg_key_pem);
@@ -918,7 +916,7 @@ static int parse_argv(int argc, char *argv[]) {
                         if (arg_cert_pem)
                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                                        "Certificate file specified twice");
-                        r = read_full_file(optarg, &arg_cert_pem, NULL);
+                        r = read_full_file_full(AT_FDCWD, optarg, READ_FULL_FILE_CONNECT_SOCKET, &arg_cert_pem, NULL);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to read certificate file: %m");
                         assert(arg_cert_pem);
@@ -929,7 +927,7 @@ static int parse_argv(int argc, char *argv[]) {
                         if (arg_trust_pem)
                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                                        "CA certificate file specified twice");
-                        r = read_full_file(optarg, &arg_trust_pem, NULL);
+                        r = read_full_file_full(AT_FDCWD, optarg, READ_FULL_FILE_CONNECT_SOCKET, &arg_trust_pem, NULL);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to read CA certificate file: %m");
                         assert(arg_trust_pem);
index 1c8e7ab6352cee4c9790d5100592914a96fb4f85..77dfdefd64cb2578cf92d0448b237701772cadd9 100644 (file)
@@ -762,10 +762,14 @@ static int parse_config(void) {
                 {}
         };
 
-        return config_parse_many_nulstr(PKGSYSCONFDIR "/journal-remote.conf",
-                                        CONF_PATHS_NULSTR("systemd/journal-remote.conf.d"),
-                                        "Remote\0", config_item_table_lookup, items,
-                                        CONFIG_PARSE_WARN, NULL);
+        return config_parse_many_nulstr(
+                        PKGSYSCONFDIR "/journal-remote.conf",
+                        CONF_PATHS_NULSTR("systemd/journal-remote.conf.d"),
+                        "Remote\0",
+                        config_item_table_lookup, items,
+                        CONFIG_PARSE_WARN,
+                        NULL,
+                        NULL);
 }
 
 static int help(void) {
@@ -1073,12 +1077,12 @@ static int parse_argv(int argc, char *argv[]) {
 static int load_certificates(char **key, char **cert, char **trust) {
         int r;
 
-        r = read_full_file(arg_key ?: PRIV_KEY_FILE, key, NULL);
+        r = read_full_file_full(AT_FDCWD, arg_key ?: PRIV_KEY_FILE, READ_FULL_FILE_CONNECT_SOCKET, key, NULL);
         if (r < 0)
                 return log_error_errno(r, "Failed to read key from file '%s': %m",
                                        arg_key ?: PRIV_KEY_FILE);
 
-        r = read_full_file(arg_cert ?: CERT_FILE, cert, NULL);
+        r = read_full_file_full(AT_FDCWD, arg_cert ?: CERT_FILE, READ_FULL_FILE_CONNECT_SOCKET, cert, NULL);
         if (r < 0)
                 return log_error_errno(r, "Failed to read certificate from file '%s': %m",
                                        arg_cert ?: CERT_FILE);
@@ -1086,7 +1090,7 @@ static int load_certificates(char **key, char **cert, char **trust) {
         if (arg_trust_all)
                 log_info("Certificate checking disabled.");
         else {
-                r = read_full_file(arg_trust ?: TRUST_FILE, trust, NULL);
+                r = read_full_file_full(AT_FDCWD, arg_trust ?: TRUST_FILE, READ_FULL_FILE_CONNECT_SOCKET, trust, NULL);
                 if (r < 0)
                         return log_error_errno(r, "Failed to read CA certificate file '%s': %m",
                                                arg_trust ?: TRUST_FILE);
@@ -1100,13 +1104,13 @@ static int load_certificates(char **key, char **cert, char **trust) {
 }
 
 static int run(int argc, char **argv) {
-        _cleanup_(notify_on_cleanup) const char *notify_message = NULL;
         _cleanup_(journal_remote_server_destroy) RemoteServer s = {};
+        _cleanup_(notify_on_cleanup) const char *notify_message = NULL;
         _cleanup_free_ char *key = NULL, *cert = NULL, *trust = NULL;
         int r;
 
         log_show_color(true);
-        log_parse_environment();
+        log_parse_environment_cli();
 
         /* The journal merging logic potentially needs a lot of fds. */
         (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE);
index 221421af465e55107f8fdb61e9dd6ae493b66d9b..13ca90f957f6c4eb3812a423aaf1866a6a5a27e9 100644 (file)
@@ -23,6 +23,7 @@
 #include "main-func.h"
 #include "mkdir.h"
 #include "parse-util.h"
+#include "path-util.h"
 #include "pretty-print.h"
 #include "process-util.h"
 #include "rlimit-util.h"
@@ -240,14 +241,14 @@ int start_upload(Uploader *u,
                             "systemd-journal-upload " GIT_VERSION,
                             LOG_WARNING, );
 
-                if (arg_key || startswith(u->url, "https://")) {
+                if (!streq_ptr(arg_key, "-") && (arg_key || startswith(u->url, "https://"))) {
                         easy_setopt(curl, CURLOPT_SSLKEY, arg_key ?: PRIV_KEY_FILE,
                                     LOG_ERR, return -EXFULL);
                         easy_setopt(curl, CURLOPT_SSLCERT, arg_cert ?: CERT_FILE,
                                     LOG_ERR, return -EXFULL);
                 }
 
-                if (streq_ptr(arg_trust, "all"))
+                if (STRPTR_IN_SET(arg_trust, "-", "all"))
                         easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0,
                                     LOG_ERR, return -EUCLEAN);
                 else if (arg_trust || startswith(u->url, "https://"))
@@ -515,18 +516,63 @@ static int perform_upload(Uploader *u) {
         return update_cursor_state(u);
 }
 
+static int config_parse_path_or_ignore(
+                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_ char *n = NULL;
+        bool fatal = ltype;
+        char **s = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if (isempty(rvalue))
+                goto finalize;
+
+        n = strdup(rvalue);
+        if (!n)
+                return log_oom();
+
+        if (streq(n, "-"))
+                goto finalize;
+
+        r = path_simplify_and_warn(n, PATH_CHECK_ABSOLUTE | (fatal ? PATH_CHECK_FATAL : 0), unit, filename, line, lvalue);
+        if (r < 0)
+                return fatal ? -ENOEXEC : 0;
+
+finalize:
+        return free_and_replace(*s, n);
+}
+
 static int parse_config(void) {
         const ConfigTableItem items[] = {
-                { "Upload",  "URL",                    config_parse_string, 0, &arg_url    },
-                { "Upload",  "ServerKeyFile",          config_parse_path,   0, &arg_key    },
-                { "Upload",  "ServerCertificateFile",  config_parse_path,   0, &arg_cert   },
-                { "Upload",  "TrustedCertificateFile", config_parse_path,   0, &arg_trust  },
-                {}};
-
-        return config_parse_many_nulstr(PKGSYSCONFDIR "/journal-upload.conf",
-                                        CONF_PATHS_NULSTR("systemd/journal-upload.conf.d"),
-                                        "Upload\0", config_item_table_lookup, items,
-                                        CONFIG_PARSE_WARN, NULL);
+                { "Upload",  "URL",                    config_parse_string,         0, &arg_url    },
+                { "Upload",  "ServerKeyFile",          config_parse_path_or_ignore, 0, &arg_key    },
+                { "Upload",  "ServerCertificateFile",  config_parse_path_or_ignore, 0, &arg_cert   },
+                { "Upload",  "TrustedCertificateFile", config_parse_path_or_ignore, 0, &arg_trust  },
+                {}
+        };
+
+        return config_parse_many_nulstr(
+                        PKGSYSCONFDIR "/journal-upload.conf",
+                        CONF_PATHS_NULSTR("systemd/journal-upload.conf.d"),
+                        "Upload\0",
+                        config_item_table_lookup, items,
+                        CONFIG_PARSE_WARN,
+                        NULL,
+                        NULL);
 }
 
 static int help(void) {
@@ -769,13 +815,13 @@ static int open_journal(sd_journal **j) {
 }
 
 static int run(int argc, char **argv) {
-        _cleanup_(notify_on_cleanup) const char *notify_message = NULL;
         _cleanup_(destroy_uploader) Uploader u = {};
+        _cleanup_(notify_on_cleanup) const char *notify_message = NULL;
         bool use_journal;
         int r;
 
         log_show_color(true);
-        log_parse_environment();
+        log_parse_environment_cli();
 
         /* The journal merging logic potentially needs a lot of fds. */
         (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE);
index 939af12572be47d932b0a3bf88ae94ea26b2443a..027f2c8ff5802c41655d94a3aaa397c66aead8c3 100644 (file)
@@ -78,10 +78,9 @@ int mhd_respondf(struct MHD_Connection *connection,
         errno = -error;
         fmt = strjoina(format, "\n");
         va_start(ap, format);
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+        DISABLE_WARNING_FORMAT_NONLITERAL;
         r = vasprintf(&m, fmt, ap);
-#pragma GCC diagnostic pop
+        REENABLE_WARNING;
         va_end(ap);
 
         if (r < 0)
index 500b6745050f25b7b5524200bd0737d667c61c16..2faaa2e28488d77149b78942125ee0f8a6f3a4ad 100644 (file)
@@ -129,9 +129,7 @@ static int run(int argc, char *argv[]) {
         _cleanup_close_ int outfd = -1, errfd = -1, saved_stderr = -1;
         int r;
 
-        log_show_color(true);
-        log_parse_environment();
-        log_open();
+        log_setup_cli();
 
         r = parse_argv(argc, argv);
         if (r <= 0)
index 70b2c8b46c4e445821f87ac206d7eaaf855a424c..b2589271cacf2c0d6ff3014a6ebc429d653f2ead 100644 (file)
@@ -55,7 +55,7 @@ typedef struct CatalogItem {
 
 static void catalog_hash_func(const CatalogItem *i, struct siphash *state) {
         siphash24_compress(&i->id, sizeof(i->id), state);
-        siphash24_compress(i->language, strlen(i->language), state);
+        siphash24_compress_string(i->language, state);
 }
 
 static int catalog_compare_func(const CatalogItem *a, const CatalogItem *b) {
index 4e00e4fc5e721c07f922905abd03dce6b62abc02..a59c2b7a88397d4a1d21314f8dcf3063e16ebc90 100644 (file)
 #include <lz4frame.h>
 #endif
 
+#if HAVE_ZSTD
+#include <zstd.h>
+#include <zstd_errors.h>
+#endif
+
 #include "alloc-util.h"
 #include "compress.h"
 #include "fd-util.h"
@@ -33,11 +38,30 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(LZ4F_compressionContext_t, LZ4F_freeCompressionConte
 DEFINE_TRIVIAL_CLEANUP_FUNC(LZ4F_decompressionContext_t, LZ4F_freeDecompressionContext);
 #endif
 
+#if HAVE_ZSTD
+DEFINE_TRIVIAL_CLEANUP_FUNC(ZSTD_CCtx *, ZSTD_freeCCtx);
+DEFINE_TRIVIAL_CLEANUP_FUNC(ZSTD_DCtx *, ZSTD_freeDCtx);
+
+static int zstd_ret_to_errno(size_t ret) {
+        switch (ZSTD_getErrorCode(ret)) {
+        case ZSTD_error_dstSize_tooSmall:
+                return -ENOBUFS;
+        case ZSTD_error_memory_allocation:
+                return -ENOMEM;
+        default:
+                return -EBADMSG;
+        }
+}
+#endif
+
 #define ALIGN_8(l) ALIGN_TO(l, sizeof(size_t))
 
 static const char* const object_compressed_table[_OBJECT_COMPRESSED_MAX] = {
-        [OBJECT_COMPRESSED_XZ] = "XZ",
-        [OBJECT_COMPRESSED_LZ4] = "LZ4",
+        [OBJECT_COMPRESSED_XZ]   = "XZ",
+        [OBJECT_COMPRESSED_LZ4]  = "LZ4",
+        [OBJECT_COMPRESSED_ZSTD] = "ZSTD",
+        /* If we add too many more entries here, it's going to grow quite large (and be mostly sparse), since
+         * the array key is actually a bitmask, not a plain enum */
 };
 
 DEFINE_STRING_TABLE_LOOKUP(object_compressed, int);
@@ -110,6 +134,29 @@ int compress_blob_lz4(const void *src, uint64_t src_size,
 #endif
 }
 
+int compress_blob_zstd(
+                const void *src, uint64_t src_size,
+                void *dst, size_t dst_alloc_size, size_t *dst_size) {
+#if HAVE_ZSTD
+        size_t k;
+
+        assert(src);
+        assert(src_size > 0);
+        assert(dst);
+        assert(dst_alloc_size > 0);
+        assert(dst_size);
+
+        k = ZSTD_compress(dst, dst_alloc_size, src, src_size, 0);
+        if (ZSTD_isError(k))
+                return zstd_ret_to_errno(k);
+
+        *dst_size = k;
+        return 0;
+#else
+        return -EPROTONOSUPPORT;
+#endif
+}
+
 int decompress_blob_xz(const void *src, uint64_t src_size,
                        void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max) {
 
@@ -210,17 +257,78 @@ int decompress_blob_lz4(const void *src, uint64_t src_size,
 #endif
 }
 
-int decompress_blob(int compression,
-                    const void *src, uint64_t src_size,
-                    void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max) {
+int decompress_blob_zstd(
+                const void *src, uint64_t src_size,
+                void **dst, size_t *dst_alloc_size, size_t *dst_size, size_t dst_max) {
+
+#if HAVE_ZSTD
+        uint64_t size;
+
+        assert(src);
+        assert(src_size > 0);
+        assert(dst);
+        assert(dst_alloc_size);
+        assert(dst_size);
+        assert(*dst_alloc_size == 0 || *dst);
+
+        size = ZSTD_getFrameContentSize(src, src_size);
+        if (IN_SET(size, ZSTD_CONTENTSIZE_ERROR, ZSTD_CONTENTSIZE_UNKNOWN))
+                return -EBADMSG;
+
+        if (dst_max > 0 && size > dst_max)
+                size = dst_max;
+        if (size > SIZE_MAX)
+                return -E2BIG;
+
+        if (!(greedy_realloc(dst, dst_alloc_size, MAX(ZSTD_DStreamOutSize(), size), 1)))
+                return -ENOMEM;
+
+        _cleanup_(ZSTD_freeDCtxp) ZSTD_DCtx *dctx = ZSTD_createDCtx();
+        if (!dctx)
+                return -ENOMEM;
+
+        ZSTD_inBuffer input = {
+                .src = src,
+                .size = src_size,
+        };
+        ZSTD_outBuffer output = {
+                .dst = *dst,
+                .size = *dst_alloc_size,
+        };
+
+        size_t k = ZSTD_decompressStream(dctx, &output, &input);
+        if (ZSTD_isError(k)) {
+                log_debug("ZSTD decoder failed: %s", ZSTD_getErrorName(k));
+                return zstd_ret_to_errno(k);
+        }
+        assert(output.pos >= size);
+
+        *dst_size = size;
+        return 0;
+#else
+        return -EPROTONOSUPPORT;
+#endif
+}
+
+int decompress_blob(
+                int compression,
+                const void *src, uint64_t src_size,
+                void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max) {
+
         if (compression == OBJECT_COMPRESSED_XZ)
-                return decompress_blob_xz(src, src_size,
-                                          dst, dst_alloc_size, dst_size, dst_max);
+                return decompress_blob_xz(
+                                src, src_size,
+                                dst, dst_alloc_size, dst_size, dst_max);
         else if (compression == OBJECT_COMPRESSED_LZ4)
-                return decompress_blob_lz4(src, src_size,
-                                           dst, dst_alloc_size, dst_size, dst_max);
+                return decompress_blob_lz4(
+                                src, src_size,
+                                dst, dst_alloc_size, dst_size, dst_max);
+        else if (compression == OBJECT_COMPRESSED_ZSTD)
+                return decompress_blob_zstd(
+                                src, src_size,
+                                dst, dst_alloc_size, dst_size, dst_max);
         else
-                return -EBADMSG;
+                return -EPROTONOSUPPORT;
 }
 
 int decompress_startswith_xz(const void *src, uint64_t src_size,
@@ -344,21 +452,83 @@ int decompress_startswith_lz4(const void *src, uint64_t src_size,
 #endif
 }
 
-int decompress_startswith(int compression,
-                          const void *src, uint64_t src_size,
-                          void **buffer, size_t *buffer_size,
-                          const void *prefix, size_t prefix_len,
-                          uint8_t extra) {
+int decompress_startswith_zstd(
+                const void *src, uint64_t src_size,
+                void **buffer, size_t *buffer_size,
+                const void *prefix, size_t prefix_len,
+                uint8_t extra) {
+#if HAVE_ZSTD
+        assert(src);
+        assert(src_size > 0);
+        assert(buffer);
+        assert(buffer_size);
+        assert(prefix);
+        assert(*buffer_size == 0 || *buffer);
+
+        uint64_t size = ZSTD_getFrameContentSize(src, src_size);
+        if (IN_SET(size, ZSTD_CONTENTSIZE_ERROR, ZSTD_CONTENTSIZE_UNKNOWN))
+                return -EBADMSG;
+
+        if (size < prefix_len + 1)
+                return 0; /* Decompressed text too short to match the prefix and extra */
+
+        _cleanup_(ZSTD_freeDCtxp) ZSTD_DCtx *dctx = ZSTD_createDCtx();
+        if (!dctx)
+                return -ENOMEM;
+
+        if (!(greedy_realloc(buffer, buffer_size, MAX(ZSTD_DStreamOutSize(), prefix_len + 1), 1)))
+                return -ENOMEM;
+
+        ZSTD_inBuffer input = {
+                .src = src,
+                .size = src_size,
+        };
+        ZSTD_outBuffer output = {
+                .dst = *buffer,
+                .size = *buffer_size,
+        };
+        size_t k;
+
+        k = ZSTD_decompressStream(dctx, &output, &input);
+        if (ZSTD_isError(k)) {
+                log_debug("ZSTD decoder failed: %s", ZSTD_getErrorName(k));
+                return zstd_ret_to_errno(k);
+        }
+        assert(output.pos >= prefix_len + 1);
+
+        return memcmp(*buffer, prefix, prefix_len) == 0 &&
+                ((const uint8_t*) *buffer)[prefix_len] == extra;
+#else
+        return -EPROTONOSUPPORT;
+#endif
+}
+
+int decompress_startswith(
+                int compression,
+                const void *src, uint64_t src_size,
+                void **buffer, size_t *buffer_size,
+                const void *prefix, size_t prefix_len,
+                uint8_t extra) {
+
         if (compression == OBJECT_COMPRESSED_XZ)
-                return decompress_startswith_xz(src, src_size,
-                                                buffer, buffer_size,
-                                                prefix, prefix_len,
-                                                extra);
+                return decompress_startswith_xz(
+                                src, src_size,
+                                buffer, buffer_size,
+                                prefix, prefix_len,
+                                extra);
+
         else if (compression == OBJECT_COMPRESSED_LZ4)
-                return decompress_startswith_lz4(src, src_size,
-                                                 buffer, buffer_size,
-                                                 prefix, prefix_len,
-                                                 extra);
+                return decompress_startswith_lz4(
+                                src, src_size,
+                                buffer, buffer_size,
+                                prefix, prefix_len,
+                                extra);
+        else if (compression == OBJECT_COMPRESSED_ZSTD)
+                return decompress_startswith_zstd(
+                                src, src_size,
+                                buffer, buffer_size,
+                                prefix, prefix_len,
+                                extra);
         else
                 return -EBADMSG;
 }
@@ -668,12 +838,223 @@ int decompress_stream_lz4(int in, int out, uint64_t max_bytes) {
 #endif
 }
 
+int compress_stream_zstd(int fdf, int fdt, uint64_t max_bytes) {
+#if HAVE_ZSTD
+        _cleanup_(ZSTD_freeCCtxp) ZSTD_CCtx *cctx = NULL;
+        _cleanup_free_ void *in_buff = NULL, *out_buff = NULL;
+        size_t in_allocsize, out_allocsize;
+        size_t z;
+        uint64_t left = max_bytes, in_bytes = 0;
+
+        assert(fdf >= 0);
+        assert(fdt >= 0);
+
+        /* Create the context and buffers */
+        in_allocsize = ZSTD_CStreamInSize();
+        out_allocsize = ZSTD_CStreamOutSize();
+        in_buff = malloc(in_allocsize);
+        out_buff = malloc(out_allocsize);
+        cctx = ZSTD_createCCtx();
+        if (!cctx || !out_buff || !in_buff)
+                return -ENOMEM;
+
+        z = ZSTD_CCtx_setParameter(cctx, ZSTD_c_checksumFlag, 1);
+        if (ZSTD_isError(z))
+                log_debug("Failed to enable ZSTD checksum, ignoring: %s", ZSTD_getErrorName(z));
+
+        /* This loop read from the input file, compresses that entire chunk,
+         * and writes all output produced to the output file.
+         */
+        for (;;) {
+                bool is_last_chunk;
+                ZSTD_inBuffer input = {
+                        .src = in_buff,
+                        .size = 0,
+                        .pos = 0
+                };
+                ssize_t red;
+
+                red = loop_read(fdf, in_buff, in_allocsize, true);
+                if (red < 0)
+                        return red;
+                is_last_chunk = red == 0;
+
+                in_bytes += (size_t) red;
+                input.size = (size_t) red;
+
+                for (bool finished = false; !finished;) {
+                        ZSTD_outBuffer output = {
+                                .dst = out_buff,
+                                .size = out_allocsize,
+                                .pos = 0
+                        };
+                        size_t remaining;
+                        ssize_t wrote;
+
+                        /* Compress into the output buffer and write all of the
+                         * output to the file so we can reuse the buffer next
+                         * iteration.
+                         */
+                        remaining = ZSTD_compressStream2(
+                                cctx, &output, &input,
+                                is_last_chunk ? ZSTD_e_end : ZSTD_e_continue);
+
+                        if (ZSTD_isError(remaining)) {
+                                log_debug("ZSTD encoder failed: %s", ZSTD_getErrorName(remaining));
+                                return zstd_ret_to_errno(remaining);
+                        }
+
+                        if (left < output.pos)
+                                return -EFBIG;
+
+                        wrote = loop_write(fdt, output.dst, output.pos, 1);
+                        if (wrote < 0)
+                                return wrote;
+
+                        left -= output.pos;
+
+                        /* If we're on the last chunk we're finished when zstd
+                         * returns 0, which means its consumed all the input AND
+                         * finished the frame. Otherwise, we're finished when
+                         * we've consumed all the input.
+                         */
+                        finished = is_last_chunk ? (remaining == 0) : (input.pos == input.size);
+                }
+
+                /* zstd only returns 0 when the input is completely consumed */
+                assert(input.pos == input.size);
+                if (is_last_chunk)
+                        break;
+        }
+
+        log_debug(
+                "ZSTD compression finished (%" PRIu64 " -> %" PRIu64 " bytes, %.1f%%)",
+                in_bytes,
+                max_bytes - left,
+                (double) (max_bytes - left) / in_bytes * 100);
+
+        return 0;
+#else
+        return -EPROTONOSUPPORT;
+#endif
+}
+
+int decompress_stream_zstd(int fdf, int fdt, uint64_t max_bytes) {
+#if HAVE_ZSTD
+        _cleanup_(ZSTD_freeDCtxp) ZSTD_DCtx *dctx = NULL;
+        _cleanup_free_ void *in_buff = NULL, *out_buff = NULL;
+        size_t in_allocsize, out_allocsize;
+        size_t last_result = 0;
+        uint64_t left = max_bytes, in_bytes = 0;
+
+        assert(fdf >= 0);
+        assert(fdt >= 0);
+
+        /* Create the context and buffers */
+        in_allocsize = ZSTD_DStreamInSize();
+        out_allocsize = ZSTD_DStreamOutSize();
+        in_buff = malloc(in_allocsize);
+        out_buff = malloc(out_allocsize);
+        dctx = ZSTD_createDCtx();
+        if (!dctx || !out_buff || !in_buff)
+                return -ENOMEM;
+
+        /* This loop assumes that the input file is one or more concatenated
+         * zstd streams. This example won't work if there is trailing non-zstd
+         * data at the end, but streaming decompression in general handles this
+         * case. ZSTD_decompressStream() returns 0 exactly when the frame is
+         * completed, and doesn't consume input after the frame.
+         */
+        for (;;) {
+                bool has_error = false;
+                ZSTD_inBuffer input = {
+                        .src = in_buff,
+                        .size = 0,
+                        .pos = 0
+                };
+                ssize_t red;
+
+                red = loop_read(fdf, in_buff, in_allocsize, true);
+                if (red < 0)
+                        return red;
+                if (red == 0)
+                        break;
+
+                in_bytes += (size_t) red;
+                input.size = (size_t) red;
+                input.pos = 0;
+
+                /* Given a valid frame, zstd won't consume the last byte of the
+                 * frame until it has flushed all of the decompressed data of
+                 * the frame. So input.pos < input.size means frame is not done
+                 * or there is still output available.
+                 */
+                while (input.pos < input.size) {
+                        ZSTD_outBuffer output = {
+                                .dst = out_buff,
+                                .size = out_allocsize,
+                                .pos = 0
+                        };
+                        ssize_t wrote;
+                        /* The return code is zero if the frame is complete, but
+                         * there may be multiple frames concatenated together.
+                         * Zstd will automatically reset the context when a
+                         * frame is complete. Still, calling ZSTD_DCtx_reset()
+                         * can be useful to reset the context to a clean state,
+                         * for instance if the last decompression call returned
+                         * an error.
+                         */
+                        last_result = ZSTD_decompressStream(dctx, &output, &input);
+                        if (ZSTD_isError(last_result)) {
+                                has_error = true;
+                                break;
+                        }
+
+                        if (left < output.pos)
+                                return -EFBIG;
+
+                        wrote = loop_write(fdt, output.dst, output.pos, 1);
+                        if (wrote < 0)
+                                return wrote;
+
+                        left -= output.pos;
+                }
+                if (has_error)
+                        break;
+        }
+
+        if (in_bytes == 0)
+                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "ZSTD decoder failed: no data read");
+
+        if (last_result != 0) {
+                /* The last return value from ZSTD_decompressStream did not end
+                 * on a frame, but we reached the end of the file! We assume
+                 * this is an error, and the input was truncated.
+                 */
+                log_debug("ZSTD decoder failed: %s", ZSTD_getErrorName(last_result));
+                return zstd_ret_to_errno(last_result);
+        }
+
+        log_debug(
+                "ZSTD decompression finished (%" PRIu64 " -> %" PRIu64 " bytes, %.1f%%)",
+                in_bytes,
+                max_bytes - left,
+                (double) (max_bytes - left) / in_bytes * 100);
+        return 0;
+#else
+        log_debug("Cannot decompress file. Compiled without ZSTD support.");
+        return -EPROTONOSUPPORT;
+#endif
+}
+
 int decompress_stream(const char *filename, int fdf, int fdt, uint64_t max_bytes) {
 
         if (endswith(filename, ".lz4"))
                 return decompress_stream_lz4(fdf, fdt, max_bytes);
         else if (endswith(filename, ".xz"))
                 return decompress_stream_xz(fdf, fdt, max_bytes);
+        else if (endswith(filename, ".zst"))
+                return decompress_stream_zstd(fdf, fdt, max_bytes);
         else
                 return -EPROTONOSUPPORT;
 }
index 56411484cee71acc7e448e274ea0d91488c1f395..ab44ff06ede87fee8a568d27610813e17d94d44f 100644 (file)
@@ -12,18 +12,26 @@ int compress_blob_xz(const void *src, uint64_t src_size,
                      void *dst, size_t dst_alloc_size, size_t *dst_size);
 int compress_blob_lz4(const void *src, uint64_t src_size,
                       void *dst, size_t dst_alloc_size, size_t *dst_size);
+int compress_blob_zstd(const void *src, uint64_t src_size,
+                       void *dst, size_t dst_alloc_size, size_t *dst_size);
 
 static inline int compress_blob(const void *src, uint64_t src_size,
                                 void *dst, size_t dst_alloc_size, size_t *dst_size) {
         int r;
-#if HAVE_LZ4
+#if HAVE_ZSTD
+        r = compress_blob_zstd(src, src_size, dst, dst_alloc_size, dst_size);
+        if (r == 0)
+                return OBJECT_COMPRESSED_ZSTD;
+#elif HAVE_LZ4
         r = compress_blob_lz4(src, src_size, dst, dst_alloc_size, dst_size);
         if (r == 0)
                 return OBJECT_COMPRESSED_LZ4;
-#else
+#elif HAVE_XZ
         r = compress_blob_xz(src, src_size, dst, dst_alloc_size, dst_size);
         if (r == 0)
                 return OBJECT_COMPRESSED_XZ;
+#else
+        r = -EOPNOTSUPP;
 #endif
         return r;
 }
@@ -32,6 +40,8 @@ int decompress_blob_xz(const void *src, uint64_t src_size,
                        void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max);
 int decompress_blob_lz4(const void *src, uint64_t src_size,
                         void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max);
+int decompress_blob_zstd(const void *src, uint64_t src_size,
+                        void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max);
 int decompress_blob(int compression,
                     const void *src, uint64_t src_size,
                     void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max);
@@ -44,6 +54,10 @@ int decompress_startswith_lz4(const void *src, uint64_t src_size,
                               void **buffer, size_t *buffer_size,
                               const void *prefix, size_t prefix_len,
                               uint8_t extra);
+int decompress_startswith_zstd(const void *src, uint64_t src_size,
+                               void **buffer, size_t *buffer_size,
+                               const void *prefix, size_t prefix_len,
+                               uint8_t extra);
 int decompress_startswith(int compression,
                           const void *src, uint64_t src_size,
                           void **buffer, size_t *buffer_size,
@@ -52,16 +66,26 @@ int decompress_startswith(int compression,
 
 int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes);
 int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes);
+int compress_stream_zstd(int fdf, int fdt, uint64_t max_bytes);
 
 int decompress_stream_xz(int fdf, int fdt, uint64_t max_size);
 int decompress_stream_lz4(int fdf, int fdt, uint64_t max_size);
+int decompress_stream_zstd(int fdf, int fdt, uint64_t max_size);
 
-#if HAVE_LZ4
+#if HAVE_ZSTD
+#  define compress_stream compress_stream_zstd
+#  define COMPRESSED_EXT ".zst"
+#elif HAVE_LZ4
 #  define compress_stream compress_stream_lz4
 #  define COMPRESSED_EXT ".lz4"
-#else
+#elif HAVE_XZ
 #  define compress_stream compress_stream_xz
 #  define COMPRESSED_EXT ".xz"
+#else
+static inline int compress_stream(int fdf, int fdt, uint64_t max_size) {
+        return -EOPNOTSUPP;
+}
+#  define COMPRESSED_EXT ""
 #endif
 
 int decompress_stream(const char *filename, int fdf, int fdt, uint64_t max_bytes);
index ff4e71a31ba44fef6891a2a304cdf7db8101867d..e9ddbb9dabbe8f364c14b31d8ce21076f393ce14 100644 (file)
@@ -9,7 +9,7 @@
 /*
  * If you change this file you probably should also change its documentation:
  *
- * http://www.freedesktop.org/wiki/Software/systemd/journal-files
+ * https://systemd.io/JOURNAL_FILE_FORMAT
  */
 
 typedef struct Header Header;
@@ -44,13 +44,13 @@ typedef enum ObjectType {
 
 /* Object flags */
 enum {
-        OBJECT_COMPRESSED_XZ = 1 << 0,
-        OBJECT_COMPRESSED_LZ4 = 1 << 1,
-        _OBJECT_COMPRESSED_MAX
+        OBJECT_COMPRESSED_XZ   = 1 << 0,
+        OBJECT_COMPRESSED_LZ4  = 1 << 1,
+        OBJECT_COMPRESSED_ZSTD = 1 << 2,
+        OBJECT_COMPRESSION_MASK = (OBJECT_COMPRESSED_XZ | OBJECT_COMPRESSED_LZ4 | OBJECT_COMPRESSED_ZSTD),
+        _OBJECT_COMPRESSED_MAX = OBJECT_COMPRESSION_MASK,
 };
 
-#define OBJECT_COMPRESSION_MASK (OBJECT_COMPRESSED_XZ | OBJECT_COMPRESSED_LZ4)
-
 struct ObjectHeader {
         uint8_t type;
         uint8_t flags;
@@ -74,13 +74,17 @@ struct DataObject DataObject__contents;
 struct DataObject__packed DataObject__contents _packed_;
 assert_cc(sizeof(struct DataObject) == sizeof(struct DataObject__packed));
 
-struct FieldObject {
-        ObjectHeader object;
-        le64_t hash;
-        le64_t next_hash_offset;
-        le64_t head_data_offset;
-        uint8_t payload[];
-} _packed_;
+#define FieldObject__contents {                 \
+        ObjectHeader object;                    \
+        le64_t hash;                            \
+        le64_t next_hash_offset;                \
+        le64_t head_data_offset;                \
+        uint8_t payload[];                      \
+}
+
+struct FieldObject FieldObject__contents;
+struct FieldObject__packed FieldObject__contents _packed_;
+assert_cc(sizeof(struct FieldObject) == sizeof(struct FieldObject__packed));
 
 struct EntryItem {
         le64_t object_offset;
@@ -145,24 +149,38 @@ enum {
 
 /* Header flags */
 enum {
-        HEADER_INCOMPATIBLE_COMPRESSED_XZ = 1 << 0,
-        HEADER_INCOMPATIBLE_COMPRESSED_LZ4 = 1 << 1,
+        HEADER_INCOMPATIBLE_COMPRESSED_XZ   = 1 << 0,
+        HEADER_INCOMPATIBLE_COMPRESSED_LZ4  = 1 << 1,
+        HEADER_INCOMPATIBLE_KEYED_HASH      = 1 << 2,
+        HEADER_INCOMPATIBLE_COMPRESSED_ZSTD = 1 << 3,
 };
 
-#define HEADER_INCOMPATIBLE_ANY (HEADER_INCOMPATIBLE_COMPRESSED_XZ|HEADER_INCOMPATIBLE_COMPRESSED_LZ4)
+#define HEADER_INCOMPATIBLE_ANY               \
+        (HEADER_INCOMPATIBLE_COMPRESSED_XZ |  \
+         HEADER_INCOMPATIBLE_COMPRESSED_LZ4 | \
+         HEADER_INCOMPATIBLE_KEYED_HASH |     \
+         HEADER_INCOMPATIBLE_COMPRESSED_ZSTD)
 
-#if HAVE_XZ && HAVE_LZ4
+#if HAVE_XZ && HAVE_LZ4 && HAVE_ZSTD
 #  define HEADER_INCOMPATIBLE_SUPPORTED HEADER_INCOMPATIBLE_ANY
+#elif HAVE_XZ && HAVE_LZ4
+#  define HEADER_INCOMPATIBLE_SUPPORTED (HEADER_INCOMPATIBLE_COMPRESSED_XZ|HEADER_INCOMPATIBLE_COMPRESSED_LZ4|HEADER_INCOMPATIBLE_KEYED_HASH)
+#elif HAVE_XZ && HAVE_ZSTD
+#  define HEADER_INCOMPATIBLE_SUPPORTED (HEADER_INCOMPATIBLE_COMPRESSED_XZ|HEADER_INCOMPATIBLE_COMPRESSED_ZSTD|HEADER_INCOMPATIBLE_KEYED_HASH)
+#elif HAVE_LZ4 && HAVE_ZSTD
+#  define HEADER_INCOMPATIBLE_SUPPORTED (HEADER_INCOMPATIBLE_COMPRESSED_LZ4|HEADER_INCOMPATIBLE_COMPRESSED_ZSTD|HEADER_INCOMPATIBLE_KEYED_HASH)
 #elif HAVE_XZ
-#  define HEADER_INCOMPATIBLE_SUPPORTED HEADER_INCOMPATIBLE_COMPRESSED_XZ
+#  define HEADER_INCOMPATIBLE_SUPPORTED (HEADER_INCOMPATIBLE_COMPRESSED_XZ|HEADER_INCOMPATIBLE_KEYED_HASH)
 #elif HAVE_LZ4
-#  define HEADER_INCOMPATIBLE_SUPPORTED HEADER_INCOMPATIBLE_COMPRESSED_LZ4
+#  define HEADER_INCOMPATIBLE_SUPPORTED (HEADER_INCOMPATIBLE_COMPRESSED_LZ4|HEADER_INCOMPATIBLE_KEYED_HASH)
+#elif HAVE_ZSTD
+#  define HEADER_INCOMPATIBLE_SUPPORTED (HEADER_INCOMPATIBLE_COMPRESSED_ZSTD|HEADER_INCOMPATIBLE_KEYED_HASH)
 #else
-#  define HEADER_INCOMPATIBLE_SUPPORTED 0
+#  define HEADER_INCOMPATIBLE_SUPPORTED HEADER_INCOMPATIBLE_KEYED_HASH
 #endif
 
 enum {
-        HEADER_COMPATIBLE_SEALED = 1
+        HEADER_COMPATIBLE_SEALED = 1 << 0,
 };
 
 #define HEADER_COMPATIBLE_ANY HEADER_COMPATIBLE_SEALED
@@ -172,7 +190,8 @@ enum {
 #  define HEADER_COMPATIBLE_SUPPORTED 0
 #endif
 
-#define HEADER_SIGNATURE ((char[]) { 'L', 'P', 'K', 'S', 'H', 'H', 'R', 'H' })
+#define HEADER_SIGNATURE                                                \
+        ((const char[]) { 'L', 'P', 'K', 'S', 'H', 'H', 'R', 'H' })
 
 #define struct_Header__contents {                       \
         uint8_t signature[8]; /* "LPKSHHRH" */          \
@@ -205,14 +224,18 @@ enum {
         /* Added in 189 */                              \
         le64_t n_tags;                                  \
         le64_t n_entry_arrays;                          \
+        /* Added in 246 */                              \
+        le64_t data_hash_chain_depth;                   \
+        le64_t field_hash_chain_depth;                  \
         }
 
 struct Header struct_Header__contents;
 struct Header__packed struct_Header__contents _packed_;
 assert_cc(sizeof(struct Header) == sizeof(struct Header__packed));
-assert_cc(sizeof(struct Header) == 240);
+assert_cc(sizeof(struct Header) == 256);
 
-#define FSS_HEADER_SIGNATURE ((char[]) { 'K', 'S', 'H', 'H', 'R', 'H', 'L', 'P' })
+#define FSS_HEADER_SIGNATURE                                            \
+        ((const char[]) { 'K', 'S', 'H', 'H', 'R', 'H', 'L', 'P' })
 
 struct FSSHeader {
         uint8_t signature[8]; /* "KSHHRHLP" */
index bd5363586077bda3a7d3b724a70ccbe098807935..cdcded2e24b4805a247f24b9ae7cc117381804d5 100644 (file)
@@ -16,6 +16,7 @@
 #include "btrfs-util.h"
 #include "chattr-util.h"
 #include "compress.h"
+#include "env-util.h"
 #include "fd-util.h"
 #include "format-util.h"
 #include "fs-util.h"
@@ -81,6 +82,9 @@
 /* The mmap context to use for the header we pick as one above the last defined typed */
 #define CONTEXT_HEADER _OBJECT_TYPE_MAX
 
+/* Longest hash chain to rotate after */
+#define HASH_CHAIN_DEPTH_MAX 100
+
 #ifdef __clang__
 #  pragma GCC diagnostic ignored "-Waddress-of-packed-member"
 #endif
@@ -388,7 +392,7 @@ JournalFile* journal_file_close(JournalFile *f) {
 
         ordered_hashmap_free_free(f->chain_cache);
 
-#if HAVE_XZ || HAVE_LZ4
+#if HAVE_COMPRESSION
         free(f->compress_buffer);
 #endif
 
@@ -419,7 +423,9 @@ static int journal_file_init_header(JournalFile *f, JournalFile *template) {
 
         h.incompatible_flags |= htole32(
                 f->compress_xz * HEADER_INCOMPATIBLE_COMPRESSED_XZ |
-                f->compress_lz4 * HEADER_INCOMPATIBLE_COMPRESSED_LZ4);
+                f->compress_lz4 * HEADER_INCOMPATIBLE_COMPRESSED_LZ4 |
+                f->compress_zstd * HEADER_INCOMPATIBLE_COMPRESSED_ZSTD |
+                f->keyed_hash * HEADER_INCOMPATIBLE_KEYED_HASH);
 
         h.compatible_flags = htole32(
                 f->seal * HEADER_COMPATIBLE_SEALED);
@@ -445,7 +451,6 @@ static int journal_file_init_header(JournalFile *f, JournalFile *template) {
 }
 
 static int journal_file_refresh_header(JournalFile *f) {
-        sd_id128_t boot_id;
         int r;
 
         assert(f);
@@ -458,12 +463,10 @@ static int journal_file_refresh_header(JournalFile *f) {
         else if (r < 0)
                 return r;
 
-        r = sd_id128_get_boot(&boot_id);
+        r = sd_id128_get_boot(&f->header->boot_id);
         if (r < 0)
                 return r;
 
-        f->header->boot_id = boot_id;
-
         r = journal_file_set_online(f);
 
         /* Sync the online state to disk */
@@ -489,16 +492,23 @@ static bool warn_wrong_flags(const JournalFile *f, bool compatible) {
                                   f->path, type, flags & ~any);
                 flags = (flags & any) & ~supported;
                 if (flags) {
-                        const char* strv[3];
+                        const char* strv[5];
                         unsigned n = 0;
                         _cleanup_free_ char *t = NULL;
 
-                        if (compatible && (flags & HEADER_COMPATIBLE_SEALED))
-                                strv[n++] = "sealed";
-                        if (!compatible && (flags & HEADER_INCOMPATIBLE_COMPRESSED_XZ))
-                                strv[n++] = "xz-compressed";
-                        if (!compatible && (flags & HEADER_INCOMPATIBLE_COMPRESSED_LZ4))
-                                strv[n++] = "lz4-compressed";
+                        if (compatible) {
+                                if (flags & HEADER_COMPATIBLE_SEALED)
+                                        strv[n++] = "sealed";
+                        } else {
+                                if (flags & HEADER_INCOMPATIBLE_COMPRESSED_XZ)
+                                        strv[n++] = "xz-compressed";
+                                if (flags & HEADER_INCOMPATIBLE_COMPRESSED_LZ4)
+                                        strv[n++] = "lz4-compressed";
+                                if (flags & HEADER_INCOMPATIBLE_COMPRESSED_ZSTD)
+                                        strv[n++] = "zstd-compressed";
+                                if (flags & HEADER_INCOMPATIBLE_KEYED_HASH)
+                                        strv[n++] = "keyed-hash";
+                        }
                         strv[n] = NULL;
                         assert(n < ELEMENTSOF(strv));
 
@@ -533,7 +543,7 @@ static int journal_file_verify_header(JournalFile *f) {
         if (f->header->state >= _STATE_MAX)
                 return -EBADMSG;
 
-        header_size = le64toh(f->header->header_size);
+        header_size = le64toh(READ_NOW(f->header->header_size));
 
         /* The first addition was n_data, so check that we are at least this large */
         if (header_size < HEADER_SIZE_MIN)
@@ -542,7 +552,7 @@ static int journal_file_verify_header(JournalFile *f) {
         if (JOURNAL_HEADER_SEALED(f->header) && !JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays))
                 return -EBADMSG;
 
-        arena_size = le64toh(f->header->arena_size);
+        arena_size = le64toh(READ_NOW(f->header->arena_size));
 
         if (UINT64_MAX - header_size < arena_size || header_size + arena_size > (uint64_t) f->last_stat.st_size)
                 return -ENODATA;
@@ -595,9 +605,12 @@ static int journal_file_verify_header(JournalFile *f) {
 
         f->compress_xz = JOURNAL_HEADER_COMPRESSED_XZ(f->header);
         f->compress_lz4 = JOURNAL_HEADER_COMPRESSED_LZ4(f->header);
+        f->compress_zstd = JOURNAL_HEADER_COMPRESSED_ZSTD(f->header);
 
         f->seal = JOURNAL_HEADER_SEALED(f->header);
 
+        f->keyed_hash = JOURNAL_HEADER_KEYED_HASH(f->header);
+
         return 0;
 }
 
@@ -612,7 +625,7 @@ int journal_file_fstat(JournalFile *f) {
 
         f->last_stat_usec = now(CLOCK_MONOTONIC);
 
-        /* Refuse dealing with with files that aren't regular */
+        /* Refuse dealing with files that aren't regular */
         r = stat_verify_regular(&f->last_stat);
         if (r < 0)
                 return r;
@@ -625,26 +638,29 @@ int journal_file_fstat(JournalFile *f) {
 }
 
 static int journal_file_allocate(JournalFile *f, uint64_t offset, uint64_t size) {
-        uint64_t old_size, new_size;
+        uint64_t old_size, new_size, old_header_size, old_arena_size;
         int r;
 
         assert(f);
         assert(f->header);
 
-        /* We assume that this file is not sparse, and we know that
-         * for sure, since we always call posix_fallocate()
-         * ourselves */
+        /* We assume that this file is not sparse, and we know that for sure, since we always call
+         * posix_fallocate() ourselves */
+
+        if (size > PAGE_ALIGN_DOWN(UINT64_MAX) - offset)
+                return -EINVAL;
 
         if (mmap_cache_got_sigbus(f->mmap, f->cache_fd))
                 return -EIO;
 
-        old_size =
-                le64toh(f->header->header_size) +
-                le64toh(f->header->arena_size);
+        old_header_size = le64toh(READ_NOW(f->header->header_size));
+        old_arena_size = le64toh(READ_NOW(f->header->arena_size));
+        if (old_arena_size > PAGE_ALIGN_DOWN(UINT64_MAX) - old_header_size)
+                return -EBADMSG;
 
-        new_size = PAGE_ALIGN(offset + size);
-        if (new_size < le64toh(f->header->header_size))
-                new_size = le64toh(f->header->header_size);
+        old_size = old_header_size + old_arena_size;
+
+        new_size = MAX(PAGE_ALIGN(offset + size), old_header_size);
 
         if (new_size <= old_size) {
 
@@ -690,7 +706,7 @@ static int journal_file_allocate(JournalFile *f, uint64_t offset, uint64_t size)
         if (r != 0)
                 return -r;
 
-        f->header->arena_size = htole64(new_size - le64toh(f->header->header_size));
+        f->header->arena_size = htole64(new_size - old_header_size);
 
         return journal_file_fstat(f);
 }
@@ -702,7 +718,15 @@ static unsigned type_to_context(ObjectType type) {
         return type > OBJECT_UNUSED && type < _OBJECT_TYPE_MAX ? type : 0;
 }
 
-static int journal_file_move_to(JournalFile *f, ObjectType type, bool keep_always, uint64_t offset, uint64_t size, void **ret, size_t *ret_size) {
+static int journal_file_move_to(
+                JournalFile *f,
+                ObjectType type,
+                bool keep_always,
+                uint64_t offset,
+                uint64_t size,
+                void **ret,
+                size_t *ret_size) {
+
         int r;
 
         assert(f);
@@ -711,6 +735,9 @@ static int journal_file_move_to(JournalFile *f, ObjectType type, bool keep_alway
         if (size <= 0)
                 return -EINVAL;
 
+        if (size > UINT64_MAX - offset)
+                return -EBADMSG;
+
         /* Avoid SIGBUS on invalid accesses */
         if (offset + size > (uint64_t) f->last_stat.st_size) {
                 /* Hmm, out of range? Let's refresh the fstat() data
@@ -760,7 +787,7 @@ static int journal_file_check_object(JournalFile *f, uint64_t offset, Object *o)
                                                le64toh(o->data.n_entries),
                                                offset);
 
-                if (le64toh(o->object.size) - offsetof(DataObject, payload) <= 0)
+                if (le64toh(o->object.size) <= offsetof(DataObject, payload))
                         return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
                                                "Bad object size (<= %zu): %" PRIu64 ": %" PRIu64,
                                                offsetof(DataObject, payload),
@@ -782,7 +809,7 @@ static int journal_file_check_object(JournalFile *f, uint64_t offset, Object *o)
                 break;
 
         case OBJECT_FIELD:
-                if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0)
+                if (le64toh(o->object.size) <= offsetof(FieldObject, payload))
                         return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
                                                "Bad field size (<= %zu): %" PRIu64 ": %" PRIu64,
                                                offsetof(FieldObject, payload),
@@ -798,18 +825,22 @@ static int journal_file_check_object(JournalFile *f, uint64_t offset, Object *o)
                                                offset);
                 break;
 
-        case OBJECT_ENTRY:
-                if ((le64toh(o->object.size) - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0)
+        case OBJECT_ENTRY: {
+                uint64_t sz;
+
+                sz = le64toh(READ_NOW(o->object.size));
+                if (sz < offsetof(EntryObject, items) ||
+                    (sz - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0)
                         return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
                                                "Bad entry size (<= %zu): %" PRIu64 ": %" PRIu64,
                                                offsetof(EntryObject, items),
-                                               le64toh(o->object.size),
+                                               sz,
                                                offset);
 
-                if ((le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0)
+                if ((sz - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0)
                         return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
                                                "Invalid number items in entry: %" PRIu64 ": %" PRIu64,
-                                               (le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem),
+                                               (sz - offsetof(EntryObject, items)) / sizeof(EntryItem),
                                                offset);
 
                 if (le64toh(o->entry.seqnum) <= 0)
@@ -831,25 +862,35 @@ static int journal_file_check_object(JournalFile *f, uint64_t offset, Object *o)
                                                offset);
 
                 break;
+        }
 
         case OBJECT_DATA_HASH_TABLE:
-        case OBJECT_FIELD_HASH_TABLE:
-                if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0 ||
-                    (le64toh(o->object.size) - offsetof(HashTableObject, items)) / sizeof(HashItem) <= 0)
+        case OBJECT_FIELD_HASH_TABLE: {
+                uint64_t sz;
+
+                sz = le64toh(READ_NOW(o->object.size));
+                if (sz < offsetof(HashTableObject, items) ||
+                    (sz - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0 ||
+                    (sz - offsetof(HashTableObject, items)) / sizeof(HashItem) <= 0)
                         return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
                                                "Invalid %s hash table size: %" PRIu64 ": %" PRIu64,
                                                o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field",
-                                               le64toh(o->object.size),
+                                               sz,
                                                offset);
 
                 break;
+        }
 
-        case OBJECT_ENTRY_ARRAY:
-                if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0 ||
-                    (le64toh(o->object.size) - offsetof(EntryArrayObject, items)) / sizeof(le64_t) <= 0)
+        case OBJECT_ENTRY_ARRAY: {
+                uint64_t sz;
+
+                sz = le64toh(READ_NOW(o->object.size));
+                if (sz < offsetof(EntryArrayObject, items) ||
+                    (sz - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0 ||
+                    (sz - offsetof(EntryArrayObject, items)) / sizeof(le64_t) <= 0)
                         return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
                                                "Invalid object entry array size: %" PRIu64 ": %" PRIu64,
-                                               le64toh(o->object.size),
+                                               sz,
                                                offset);
 
                 if (!VALID64(le64toh(o->entry_array.next_entry_array_offset)))
@@ -859,6 +900,7 @@ static int journal_file_check_object(JournalFile *f, uint64_t offset, Object *o)
                                                offset);
 
                 break;
+        }
 
         case OBJECT_TAG:
                 if (le64toh(o->object.size) != sizeof(TagObject))
@@ -905,7 +947,7 @@ int journal_file_move_to_object(JournalFile *f, ObjectType type, uint64_t offset
                 return r;
 
         o = (Object*) t;
-        s = le64toh(o->object.size);
+        s = le64toh(READ_NOW(o->object.size));
 
         if (s == 0)
                 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
@@ -974,7 +1016,13 @@ static uint64_t journal_file_entry_seqnum(JournalFile *f, uint64_t *seqnum) {
         return r;
 }
 
-int journal_file_append_object(JournalFile *f, ObjectType type, uint64_t size, Object **ret, uint64_t *offset) {
+int journal_file_append_object(
+                JournalFile *f,
+                ObjectType type,
+                uint64_t size,
+                Object **ret,
+                uint64_t *ret_offset) {
+
         int r;
         uint64_t p;
         Object *tail, *o;
@@ -984,8 +1032,6 @@ int journal_file_append_object(JournalFile *f, ObjectType type, uint64_t size, O
         assert(f->header);
         assert(type > OBJECT_UNUSED && type < _OBJECT_TYPE_MAX);
         assert(size >= sizeof(ObjectHeader));
-        assert(offset);
-        assert(ret);
 
         r = journal_file_set_online(f);
         if (r < 0)
@@ -995,11 +1041,21 @@ int journal_file_append_object(JournalFile *f, ObjectType type, uint64_t size, O
         if (p == 0)
                 p = le64toh(f->header->header_size);
         else {
+                uint64_t sz;
+
                 r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &tail);
                 if (r < 0)
                         return r;
 
-                p += ALIGN64(le64toh(tail->object.size));
+                sz = le64toh(READ_NOW(tail->object.size));
+                if (sz > UINT64_MAX - sizeof(uint64_t) + 1)
+                        return -EBADMSG;
+
+                sz = ALIGN64(sz);
+                if (p > UINT64_MAX - sz)
+                        return -EBADMSG;
+
+                p += sz;
         }
 
         r = journal_file_allocate(f, p, size);
@@ -1011,16 +1067,19 @@ int journal_file_append_object(JournalFile *f, ObjectType type, uint64_t size, O
                 return r;
 
         o = (Object*) t;
-
-        zero(o->object);
-        o->object.type = type;
-        o->object.size = htole64(size);
+        o->object = (ObjectHeader) {
+                .type = type,
+                .size = htole64(size),
+        };
 
         f->header->tail_object_offset = htole64(p);
         f->header->n_objects = htole64(le64toh(f->header->n_objects) + 1);
 
-        *ret = o;
-        *offset = p;
+        if (ret)
+                *ret = o;
+
+        if (ret_offset)
+                *ret_offset = p;
 
         return 0;
 }
@@ -1042,7 +1101,7 @@ static int journal_file_setup_data_hash_table(JournalFile *f) {
         if (s < DEFAULT_DATA_HASH_TABLE_SIZE)
                 s = DEFAULT_DATA_HASH_TABLE_SIZE;
 
-        log_debug("Reserving %"PRIu64" entries in hash table.", s / sizeof(HashItem));
+        log_debug("Reserving %"PRIu64" entries in data hash table.", s / sizeof(HashItem));
 
         r = journal_file_append_object(f,
                                        OBJECT_DATA_HASH_TABLE,
@@ -1071,6 +1130,8 @@ static int journal_file_setup_field_hash_table(JournalFile *f) {
          * number should grow very slowly only */
 
         s = DEFAULT_FIELD_HASH_TABLE_SIZE;
+        log_debug("Reserving %"PRIu64" entries in field hash table.", s / sizeof(HashItem));
+
         r = journal_file_append_object(f,
                                        OBJECT_FIELD_HASH_TABLE,
                                        offsetof(Object, hash_table.items) + s,
@@ -1156,7 +1217,7 @@ static int journal_file_link_field(
         if (o->object.type != OBJECT_FIELD)
                 return -EINVAL;
 
-        m = le64toh(f->header->field_hash_table_size) / sizeof(HashItem);
+        m = le64toh(READ_NOW(f->header->field_hash_table_size)) / sizeof(HashItem);
         if (m <= 0)
                 return -EBADMSG;
 
@@ -1201,7 +1262,7 @@ static int journal_file_link_data(
         if (o->object.type != OBJECT_DATA)
                 return -EINVAL;
 
-        m = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
+        m = le64toh(READ_NOW(f->header->data_hash_table_size)) / sizeof(HashItem);
         if (m <= 0)
                 return -EBADMSG;
 
@@ -1234,12 +1295,38 @@ static int journal_file_link_data(
         return 0;
 }
 
+static int next_hash_offset(
+                JournalFile *f,
+                uint64_t *p,
+                le64_t *next_hash_offset,
+                uint64_t *depth,
+                le64_t *header_max_depth) {
+
+        uint64_t nextp;
+
+        nextp = le64toh(READ_NOW(*next_hash_offset));
+        if (nextp > 0) {
+                if (nextp <= *p) /* Refuse going in loops */
+                        return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
+                                               "Detected hash item loop in %s, refusing.", f->path);
+
+                (*depth)++;
+
+                /* If the depth of this hash chain is larger than all others we have seen so far, record it */
+                if (header_max_depth && f->writable)
+                        *header_max_depth = htole64(MAX(*depth, le64toh(*header_max_depth)));
+        }
+
+        *p = nextp;
+        return 0;
+}
+
 int journal_file_find_field_object_with_hash(
                 JournalFile *f,
                 const void *field, uint64_t size, uint64_t hash,
-                Object **ret, uint64_t *offset) {
+                Object **ret, uint64_t *ret_offset) {
 
-        uint64_t p, osize, h, m;
+        uint64_t p, osize, h, m, depth = 0;
         int r;
 
         assert(f);
@@ -1257,13 +1344,12 @@ int journal_file_find_field_object_with_hash(
 
         osize = offsetof(Object, field.payload) + size;
 
-        m = le64toh(f->header->field_hash_table_size) / sizeof(HashItem);
+        m = le64toh(READ_NOW(f->header->field_hash_table_size)) / sizeof(HashItem);
         if (m <= 0)
                 return -EBADMSG;
 
         h = hash % m;
         p = le64toh(f->field_hash_table[h].head_hash_offset);
-
         while (p > 0) {
                 Object *o;
 
@@ -1277,41 +1363,63 @@ int journal_file_find_field_object_with_hash(
 
                         if (ret)
                                 *ret = o;
-                        if (offset)
-                                *offset = p;
+                        if (ret_offset)
+                                *ret_offset = p;
 
                         return 1;
                 }
 
-                p = le64toh(o->field.next_hash_offset);
+                r = next_hash_offset(
+                                f,
+                                &p,
+                                &o->field.next_hash_offset,
+                                &depth,
+                                JOURNAL_HEADER_CONTAINS(f->header, field_hash_chain_depth) ? &f->header->field_hash_chain_depth : NULL);
+                if (r < 0)
+                        return r;
         }
 
         return 0;
 }
 
+uint64_t journal_file_hash_data(
+                JournalFile *f,
+                const void *data,
+                size_t sz) {
+
+        assert(f);
+        assert(data || sz == 0);
+
+        /* We try to unify our codebase on siphash, hence new-styled journal files utilizing the keyed hash
+         * function use siphash. Old journal files use the Jenkins hash. */
+
+        if (JOURNAL_HEADER_KEYED_HASH(f->header))
+                return siphash24(data, sz, f->header->file_id.bytes);
+
+        return jenkins_hash64(data, sz);
+}
+
 int journal_file_find_field_object(
                 JournalFile *f,
                 const void *field, uint64_t size,
-                Object **ret, uint64_t *offset) {
-
-        uint64_t hash;
+                Object **ret, uint64_t *ret_offset) {
 
         assert(f);
         assert(field && size > 0);
 
-        hash = hash64(field, size);
-
-        return journal_file_find_field_object_with_hash(f,
-                                                        field, size, hash,
-                                                        ret, offset);
+        return journal_file_find_field_object_with_hash(
+                        f,
+                        field, size,
+                        journal_file_hash_data(f, field, size),
+                        ret, ret_offset);
 }
 
 int journal_file_find_data_object_with_hash(
                 JournalFile *f,
                 const void *data, uint64_t size, uint64_t hash,
-                Object **ret, uint64_t *offset) {
+                Object **ret, uint64_t *ret_offset) {
 
-        uint64_t p, osize, h, m;
+        uint64_t p, osize, h, m, depth = 0;
         int r;
 
         assert(f);
@@ -1329,7 +1437,7 @@ int journal_file_find_data_object_with_hash(
 
         osize = offsetof(Object, data.payload) + size;
 
-        m = le64toh(f->header->data_hash_table_size) / sizeof(HashItem);
+        m = le64toh(READ_NOW(f->header->data_hash_table_size)) / sizeof(HashItem);
         if (m <= 0)
                 return -EBADMSG;
 
@@ -1347,11 +1455,11 @@ int journal_file_find_data_object_with_hash(
                         goto next;
 
                 if (o->object.flags & OBJECT_COMPRESSION_MASK) {
-#if HAVE_XZ || HAVE_LZ4
+#if HAVE_COMPRESSION
                         uint64_t l;
                         size_t rsize = 0;
 
-                        l = le64toh(o->object.size);
+                        l = le64toh(READ_NOW(o->object.size));
                         if (l <= offsetof(Object, data.payload))
                                 return -EBADMSG;
 
@@ -1368,8 +1476,8 @@ int journal_file_find_data_object_with_hash(
                                 if (ret)
                                         *ret = o;
 
-                                if (offset)
-                                        *offset = p;
+                                if (ret_offset)
+                                        *ret_offset = p;
 
                                 return 1;
                         }
@@ -1382,14 +1490,21 @@ int journal_file_find_data_object_with_hash(
                         if (ret)
                                 *ret = o;
 
-                        if (offset)
-                                *offset = p;
+                        if (ret_offset)
+                                *ret_offset = p;
 
                         return 1;
                 }
 
         next:
-                p = le64toh(o->data.next_hash_offset);
+                r = next_hash_offset(
+                                f,
+                                &p,
+                                &o->data.next_hash_offset,
+                                &depth,
+                                JOURNAL_HEADER_CONTAINS(f->header, data_hash_chain_depth) ? &f->header->data_hash_chain_depth : NULL);
+                if (r < 0)
+                        return r;
         }
 
         return 0;
@@ -1398,24 +1513,22 @@ int journal_file_find_data_object_with_hash(
 int journal_file_find_data_object(
                 JournalFile *f,
                 const void *data, uint64_t size,
-                Object **ret, uint64_t *offset) {
-
-        uint64_t hash;
+                Object **ret, uint64_t *ret_offset) {
 
         assert(f);
         assert(data || size == 0);
 
-        hash = hash64(data, size);
-
-        return journal_file_find_data_object_with_hash(f,
-                                                       data, size, hash,
-                                                       ret, offset);
+        return journal_file_find_data_object_with_hash(
+                        f,
+                        data, size,
+                        journal_file_hash_data(f, data, size),
+                        ret, ret_offset);
 }
 
 static int journal_file_append_field(
                 JournalFile *f,
                 const void *field, uint64_t size,
-                Object **ret, uint64_t *offset) {
+                Object **ret, uint64_t *ret_offset) {
 
         uint64_t hash, p;
         uint64_t osize;
@@ -1425,7 +1538,7 @@ static int journal_file_append_field(
         assert(f);
         assert(field && size > 0);
 
-        hash = hash64(field, size);
+        hash = journal_file_hash_data(f, field, size);
 
         r = journal_file_find_field_object_with_hash(f, field, size, hash, &o, &p);
         if (r < 0)
@@ -1435,8 +1548,8 @@ static int journal_file_append_field(
                 if (ret)
                         *ret = o;
 
-                if (offset)
-                        *offset = p;
+                if (ret_offset)
+                        *ret_offset = p;
 
                 return 0;
         }
@@ -1468,8 +1581,8 @@ static int journal_file_append_field(
         if (ret)
                 *ret = o;
 
-        if (offset)
-                *offset = p;
+        if (ret_offset)
+                *ret_offset = p;
 
         return 0;
 }
@@ -1477,7 +1590,7 @@ static int journal_file_append_field(
 static int journal_file_append_data(
                 JournalFile *f,
                 const void *data, uint64_t size,
-                Object **ret, uint64_t *offset) {
+                Object **ret, uint64_t *ret_offset) {
 
         uint64_t hash, p;
         uint64_t osize;
@@ -1488,7 +1601,7 @@ static int journal_file_append_data(
         assert(f);
         assert(data || size == 0);
 
-        hash = hash64(data, size);
+        hash = journal_file_hash_data(f, data, size);
 
         r = journal_file_find_data_object_with_hash(f, data, size, hash, &o, &p);
         if (r < 0)
@@ -1498,8 +1611,8 @@ static int journal_file_append_data(
                 if (ret)
                         *ret = o;
 
-                if (offset)
-                        *offset = p;
+                if (ret_offset)
+                        *ret_offset = p;
 
                 return 0;
         }
@@ -1511,7 +1624,7 @@ static int journal_file_append_data(
 
         o->data.hash = htole64(hash);
 
-#if HAVE_XZ || HAVE_LZ4
+#if HAVE_COMPRESSION
         if (JOURNAL_FILE_COMPRESS(f) && size >= f->compress_threshold_bytes) {
                 size_t rsize = 0;
 
@@ -1569,37 +1682,54 @@ static int journal_file_append_data(
         if (ret)
                 *ret = o;
 
-        if (offset)
-                *offset = p;
+        if (ret_offset)
+                *ret_offset = p;
 
         return 0;
 }
 
 uint64_t journal_file_entry_n_items(Object *o) {
+        uint64_t sz;
         assert(o);
 
         if (o->object.type != OBJECT_ENTRY)
                 return 0;
 
-        return (le64toh(o->object.size) - offsetof(Object, entry.items)) / sizeof(EntryItem);
+        sz = le64toh(READ_NOW(o->object.size));
+        if (sz < offsetof(Object, entry.items))
+                return 0;
+
+        return (sz - offsetof(Object, entry.items)) / sizeof(EntryItem);
 }
 
 uint64_t journal_file_entry_array_n_items(Object *o) {
+        uint64_t sz;
+
         assert(o);
 
         if (o->object.type != OBJECT_ENTRY_ARRAY)
                 return 0;
 
-        return (le64toh(o->object.size) - offsetof(Object, entry_array.items)) / sizeof(uint64_t);
+        sz = le64toh(READ_NOW(o->object.size));
+        if (sz < offsetof(Object, entry_array.items))
+                return 0;
+
+        return (sz - offsetof(Object, entry_array.items)) / sizeof(uint64_t);
 }
 
 uint64_t journal_file_hash_table_n_items(Object *o) {
+        uint64_t sz;
+
         assert(o);
 
         if (!IN_SET(o->object.type, OBJECT_DATA_HASH_TABLE, OBJECT_FIELD_HASH_TABLE))
                 return 0;
 
-        return (le64toh(o->object.size) - offsetof(Object, hash_table.items)) / sizeof(HashItem);
+        sz = le64toh(READ_NOW(o->object.size));
+        if (sz < offsetof(Object, hash_table.items))
+                return 0;
+
+        return (sz - offsetof(Object, hash_table.items)) / sizeof(HashItem);
 }
 
 static int link_entry_into_array(JournalFile *f,
@@ -1617,7 +1747,7 @@ static int link_entry_into_array(JournalFile *f,
         assert(p > 0);
 
         a = le64toh(*first);
-        i = hidx = le64toh(*idx);
+        i = hidx = le64toh(READ_NOW(*idx));
         while (a > 0) {
 
                 r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o);
@@ -1682,6 +1812,7 @@ static int link_entry_into_array_plus_one(JournalFile *f,
                                           le64_t *idx,
                                           uint64_t p) {
 
+        uint64_t hidx;
         int r;
 
         assert(f);
@@ -1690,32 +1821,33 @@ static int link_entry_into_array_plus_one(JournalFile *f,
         assert(idx);
         assert(p > 0);
 
-        if (*idx == 0)
+        hidx = le64toh(READ_NOW(*idx));
+        if (hidx == UINT64_MAX)
+                return -EBADMSG;
+        if (hidx == 0)
                 *extra = htole64(p);
         else {
                 le64_t i;
 
-                i = htole64(le64toh(*idx) - 1);
+                i = htole64(hidx - 1);
                 r = link_entry_into_array(f, first, &i, p);
                 if (r < 0)
                         return r;
         }
 
-        *idx = htole64(le64toh(*idx) + 1);
+        *idx = htole64(hidx + 1);
         return 0;
 }
 
 static int journal_file_link_entry_item(JournalFile *f, Object *o, uint64_t offset, uint64_t i) {
         uint64_t p;
         int r;
+
         assert(f);
         assert(o);
         assert(offset > 0);
 
         p = le64toh(o->entry.items[i].object_offset);
-        if (p == 0)
-                return -EINVAL;
-
         r = journal_file_move_to_object(f, OBJECT_DATA, p, &o);
         if (r < 0)
                 return r;
@@ -1775,7 +1907,7 @@ static int journal_file_append_entry_internal(
                 uint64_t xor_hash,
                 const EntryItem items[], unsigned n_items,
                 uint64_t *seqnum,
-                Object **ret, uint64_t *offset) {
+                Object **ret, uint64_t *ret_offset) {
         uint64_t np;
         uint64_t osize;
         Object *o;
@@ -1814,8 +1946,8 @@ static int journal_file_append_entry_internal(
         if (ret)
                 *ret = o;
 
-        if (offset)
-                *offset = np;
+        if (ret_offset)
+                *ret_offset = np;
 
         return 0;
 }
@@ -1919,7 +2051,7 @@ int journal_file_append_entry(
                 const sd_id128_t *boot_id,
                 const struct iovec iovec[], unsigned n_iovec,
                 uint64_t *seqnum,
-                Object **ret, uint64_t *offset) {
+                Object **ret, uint64_t *ret_offset) {
 
         unsigned i;
         EntryItem *items;
@@ -1962,7 +2094,20 @@ int journal_file_append_entry(
                 if (r < 0)
                         return r;
 
-                xor_hash ^= le64toh(o->data.hash);
+                /* When calculating the XOR hash field, we need to take special care if the "keyed-hash"
+                 * journal file flag is on. We use the XOR hash field to quickly determine the identity of a
+                 * specific record, and give records with otherwise identical position (i.e. match in seqno,
+                 * timestamp, …) a stable ordering. But for that we can't have it that the hash of the
+                 * objects in each file is different since they are keyed. Hence let's calculate the Jenkins
+                 * hash here for that. This also has the benefit that cursors for old and new journal files
+                 * are completely identical (they include the XOR hash after all). For classic Jenkins-hash
+                 * files things are easier, we can just take the value from the stored record directly. */
+
+                if (JOURNAL_HEADER_KEYED_HASH(f->header))
+                        xor_hash ^= jenkins_hash64(iovec[i].iov_base, iovec[i].iov_len);
+                else
+                        xor_hash ^= le64toh(o->data.hash);
+
                 items[i].object_offset = htole64(p);
                 items[i].hash = o->data.hash;
         }
@@ -1971,7 +2116,7 @@ int journal_file_append_entry(
          * times for rotating media. */
         typesafe_qsort(items, n_iovec, entry_item_cmp);
 
-        r = journal_file_append_entry_internal(f, ts, boot_id, xor_hash, items, n_iovec, seqnum, ret, offset);
+        r = journal_file_append_entry_internal(f, ts, boot_id, xor_hash, items, n_iovec, seqnum, ret, ret_offset);
 
         /* If the memory mapping triggered a SIGBUS then we return an
          * IO error and ignore the error code passed down to us, since
@@ -2040,7 +2185,7 @@ static int generic_array_get(
                 JournalFile *f,
                 uint64_t first,
                 uint64_t i,
-                Object **ret, uint64_t *offset) {
+                Object **ret, uint64_t *ret_offset) {
 
         Object *o;
         uint64_t p = 0, a, t = 0;
@@ -2090,8 +2235,8 @@ found:
         if (ret)
                 *ret = o;
 
-        if (offset)
-                *offset = p;
+        if (ret_offset)
+                *ret_offset = p;
 
         return 1;
 }
@@ -2101,7 +2246,7 @@ static int generic_array_get_plus_one(
                 uint64_t extra,
                 uint64_t first,
                 uint64_t i,
-                Object **ret, uint64_t *offset) {
+                Object **ret, uint64_t *ret_offset) {
 
         Object *o;
 
@@ -2117,13 +2262,13 @@ static int generic_array_get_plus_one(
                 if (ret)
                         *ret = o;
 
-                if (offset)
-                        *offset = extra;
+                if (ret_offset)
+                        *ret_offset = extra;
 
                 return 1;
         }
 
-        return generic_array_get(f, first, i-1, ret, offset);
+        return generic_array_get(f, first, i-1, ret, ret_offset);
 }
 
 enum {
@@ -2140,8 +2285,8 @@ static int generic_array_bisect(
                 int (*test_object)(JournalFile *f, uint64_t p, uint64_t needle),
                 direction_t direction,
                 Object **ret,
-                uint64_t *offset,
-                uint64_t *idx) {
+                uint64_t *ret_offset,
+                uint64_t *ret_idx) {
 
         uint64_t a, p, t = 0, i = 0, last_p = 0, last_index = (uint64_t) -1;
         bool subtract_one = false;
@@ -2340,11 +2485,11 @@ found:
         if (ret)
                 *ret = o;
 
-        if (offset)
-                *offset = p;
+        if (ret_offset)
+                *ret_offset = p;
 
-        if (idx)
-                *idx = t + i + (subtract_one ? -1 : 0);
+        if (ret_idx)
+                *ret_idx = t + i + (subtract_one ? -1 : 0);
 
         return 1;
 }
@@ -2358,8 +2503,8 @@ static int generic_array_bisect_plus_one(
                 int (*test_object)(JournalFile *f, uint64_t p, uint64_t needle),
                 direction_t direction,
                 Object **ret,
-                uint64_t *offset,
-                uint64_t *idx) {
+                uint64_t *ret_offset,
+                uint64_t *ret_idx) {
 
         int r;
         bool step_back = false;
@@ -2395,13 +2540,13 @@ static int generic_array_bisect_plus_one(
                         return 0;
         }
 
-        r = generic_array_bisect(f, first, n-1, needle, test_object, direction, ret, offset, idx);
+        r = generic_array_bisect(f, first, n-1, needle, test_object, direction, ret, ret_offset, ret_idx);
 
         if (r == 0 && step_back)
                 goto found;
 
-        if (r > 0 && idx)
-                (*idx)++;
+        if (r > 0 && ret_idx)
+                (*ret_idx)++;
 
         return r;
 
@@ -2413,11 +2558,11 @@ found:
         if (ret)
                 *ret = o;
 
-        if (offset)
-                *offset = extra;
+        if (ret_offset)
+                *ret_offset = extra;
 
-        if (idx)
-                *idx = 0;
+        if (ret_idx)
+                *ret_idx = 0;
 
         return 1;
 }
@@ -2435,6 +2580,7 @@ _pure_ static int test_object_offset(JournalFile *f, uint64_t p, uint64_t needle
 }
 
 static int test_object_seqnum(JournalFile *f, uint64_t p, uint64_t needle) {
+        uint64_t sq;
         Object *o;
         int r;
 
@@ -2445,9 +2591,10 @@ static int test_object_seqnum(JournalFile *f, uint64_t p, uint64_t needle) {
         if (r < 0)
                 return r;
 
-        if (le64toh(o->entry.seqnum) == needle)
+        sq = le64toh(READ_NOW(o->entry.seqnum));
+        if (sq == needle)
                 return TEST_FOUND;
-        else if (le64toh(o->entry.seqnum) < needle)
+        else if (sq < needle)
                 return TEST_LEFT;
         else
                 return TEST_RIGHT;
@@ -2458,21 +2605,23 @@ int journal_file_move_to_entry_by_seqnum(
                 uint64_t seqnum,
                 direction_t direction,
                 Object **ret,
-                uint64_t *offset) {
+                uint64_t *ret_offset) {
         assert(f);
         assert(f->header);
 
-        return generic_array_bisect(f,
-                                    le64toh(f->header->entry_array_offset),
-                                    le64toh(f->header->n_entries),
-                                    seqnum,
-                                    test_object_seqnum,
-                                    direction,
-                                    ret, offset, NULL);
+        return generic_array_bisect(
+                        f,
+                        le64toh(f->header->entry_array_offset),
+                        le64toh(f->header->n_entries),
+                        seqnum,
+                        test_object_seqnum,
+                        direction,
+                        ret, ret_offset, NULL);
 }
 
 static int test_object_realtime(JournalFile *f, uint64_t p, uint64_t needle) {
         Object *o;
+        uint64_t rt;
         int r;
 
         assert(f);
@@ -2482,9 +2631,10 @@ static int test_object_realtime(JournalFile *f, uint64_t p, uint64_t needle) {
         if (r < 0)
                 return r;
 
-        if (le64toh(o->entry.realtime) == needle)
+        rt = le64toh(READ_NOW(o->entry.realtime));
+        if (rt == needle)
                 return TEST_FOUND;
-        else if (le64toh(o->entry.realtime) < needle)
+        else if (rt < needle)
                 return TEST_LEFT;
         else
                 return TEST_RIGHT;
@@ -2495,21 +2645,23 @@ int journal_file_move_to_entry_by_realtime(
                 uint64_t realtime,
                 direction_t direction,
                 Object **ret,
-                uint64_t *offset) {
+                uint64_t *ret_offset) {
         assert(f);
         assert(f->header);
 
-        return generic_array_bisect(f,
-                                    le64toh(f->header->entry_array_offset),
-                                    le64toh(f->header->n_entries),
-                                    realtime,
-                                    test_object_realtime,
-                                    direction,
-                                    ret, offset, NULL);
+        return generic_array_bisect(
+                        f,
+                        le64toh(f->header->entry_array_offset),
+                        le64toh(f->header->n_entries),
+                        realtime,
+                        test_object_realtime,
+                        direction,
+                        ret, ret_offset, NULL);
 }
 
 static int test_object_monotonic(JournalFile *f, uint64_t p, uint64_t needle) {
         Object *o;
+        uint64_t m;
         int r;
 
         assert(f);
@@ -2519,9 +2671,10 @@ static int test_object_monotonic(JournalFile *f, uint64_t p, uint64_t needle) {
         if (r < 0)
                 return r;
 
-        if (le64toh(o->entry.monotonic) == needle)
+        m = le64toh(READ_NOW(o->entry.monotonic));
+        if (m == needle)
                 return TEST_FOUND;
-        else if (le64toh(o->entry.monotonic) < needle)
+        else if (m < needle)
                 return TEST_LEFT;
         else
                 return TEST_RIGHT;
@@ -2545,7 +2698,7 @@ int journal_file_move_to_entry_by_monotonic(
                 uint64_t monotonic,
                 direction_t direction,
                 Object **ret,
-                uint64_t *offset) {
+                uint64_t *ret_offset) {
 
         Object *o;
         int r;
@@ -2558,14 +2711,15 @@ int journal_file_move_to_entry_by_monotonic(
         if (r == 0)
                 return -ENOENT;
 
-        return generic_array_bisect_plus_one(f,
-                                             le64toh(o->data.entry_offset),
-                                             le64toh(o->data.entry_array_offset),
-                                             le64toh(o->data.n_entries),
-                                             monotonic,
-                                             test_object_monotonic,
-                                             direction,
-                                             ret, offset, NULL);
+        return generic_array_bisect_plus_one(
+                        f,
+                        le64toh(o->data.entry_offset),
+                        le64toh(o->data.entry_array_offset),
+                        le64toh(o->data.n_entries),
+                        monotonic,
+                        test_object_monotonic,
+                        direction,
+                        ret, ret_offset, NULL);
 }
 
 void journal_file_reset_location(JournalFile *f) {
@@ -2671,7 +2825,7 @@ int journal_file_next_entry(
                 JournalFile *f,
                 uint64_t p,
                 direction_t direction,
-                Object **ret, uint64_t *offset) {
+                Object **ret, uint64_t *ret_offset) {
 
         uint64_t i, n, ofs;
         int r;
@@ -2679,7 +2833,7 @@ int journal_file_next_entry(
         assert(f);
         assert(f->header);
 
-        n = le64toh(f->header->n_entries);
+        n = le64toh(READ_NOW(f->header->n_entries));
         if (n <= 0)
                 return 0;
 
@@ -2728,8 +2882,8 @@ int journal_file_next_entry(
                                        "%s: entry array not properly ordered at entry %" PRIu64,
                                        f->path, i);
 
-        if (offset)
-                *offset = ofs;
+        if (ret_offset)
+                *ret_offset = ofs;
 
         return 1;
 }
@@ -2739,7 +2893,7 @@ int journal_file_next_entry_for_data(
                 Object *o, uint64_t p,
                 uint64_t data_offset,
                 direction_t direction,
-                Object **ret, uint64_t *offset) {
+                Object **ret, uint64_t *ret_offset) {
 
         uint64_t i, n, ofs;
         Object *d;
@@ -2752,7 +2906,7 @@ int journal_file_next_entry_for_data(
         if (r < 0)
                 return r;
 
-        n = le64toh(d->data.n_entries);
+        n = le64toh(READ_NOW(d->data.n_entries));
         if (n <= 0)
                 return n;
 
@@ -2804,8 +2958,8 @@ int journal_file_next_entry_for_data(
                                        "%s data entry array not properly ordered at entry %" PRIu64,
                                        f->path, i);
 
-        if (offset)
-                *offset = ofs;
+        if (ret_offset)
+                *ret_offset = ofs;
 
         return 1;
 }
@@ -2815,7 +2969,7 @@ int journal_file_move_to_entry_by_offset_for_data(
                 uint64_t data_offset,
                 uint64_t p,
                 direction_t direction,
-                Object **ret, uint64_t *offset) {
+                Object **ret, uint64_t *ret_offset) {
 
         int r;
         Object *d;
@@ -2826,14 +2980,15 @@ int journal_file_move_to_entry_by_offset_for_data(
         if (r < 0)
                 return r;
 
-        return generic_array_bisect_plus_one(f,
-                                             le64toh(d->data.entry_offset),
-                                             le64toh(d->data.entry_array_offset),
-                                             le64toh(d->data.n_entries),
-                                             p,
-                                             test_object_offset,
-                                             direction,
-                                             ret, offset, NULL);
+        return generic_array_bisect_plus_one(
+                        f,
+                        le64toh(d->data.entry_offset),
+                        le64toh(d->data.entry_array_offset),
+                        le64toh(d->data.n_entries),
+                        p,
+                        test_object_offset,
+                        direction,
+                        ret, ret_offset, NULL);
 }
 
 int journal_file_move_to_entry_by_monotonic_for_data(
@@ -2842,7 +2997,7 @@ int journal_file_move_to_entry_by_monotonic_for_data(
                 sd_id128_t boot_id,
                 uint64_t monotonic,
                 direction_t direction,
-                Object **ret, uint64_t *offset) {
+                Object **ret, uint64_t *ret_offset) {
 
         Object *o, *d;
         int r;
@@ -2909,8 +3064,8 @@ int journal_file_move_to_entry_by_monotonic_for_data(
                 if (p == q) {
                         if (ret)
                                 *ret = qo;
-                        if (offset)
-                                *offset = q;
+                        if (ret_offset)
+                                *ret_offset = q;
 
                         return 1;
                 }
@@ -2924,7 +3079,7 @@ int journal_file_move_to_entry_by_seqnum_for_data(
                 uint64_t data_offset,
                 uint64_t seqnum,
                 direction_t direction,
-                Object **ret, uint64_t *offset) {
+                Object **ret, uint64_t *ret_offset) {
 
         Object *d;
         int r;
@@ -2935,14 +3090,15 @@ int journal_file_move_to_entry_by_seqnum_for_data(
         if (r < 0)
                 return r;
 
-        return generic_array_bisect_plus_one(f,
-                                             le64toh(d->data.entry_offset),
-                                             le64toh(d->data.entry_array_offset),
-                                             le64toh(d->data.n_entries),
-                                             seqnum,
-                                             test_object_seqnum,
-                                             direction,
-                                             ret, offset, NULL);
+        return generic_array_bisect_plus_one(
+                        f,
+                        le64toh(d->data.entry_offset),
+                        le64toh(d->data.entry_array_offset),
+                        le64toh(d->data.n_entries),
+                        seqnum,
+                        test_object_seqnum,
+                        direction,
+                        ret, ret_offset, NULL);
 }
 
 int journal_file_move_to_entry_by_realtime_for_data(
@@ -2950,7 +3106,7 @@ int journal_file_move_to_entry_by_realtime_for_data(
                 uint64_t data_offset,
                 uint64_t realtime,
                 direction_t direction,
-                Object **ret, uint64_t *offset) {
+                Object **ret, uint64_t *ret_offset) {
 
         Object *d;
         int r;
@@ -2961,14 +3117,15 @@ int journal_file_move_to_entry_by_realtime_for_data(
         if (r < 0)
                 return r;
 
-        return generic_array_bisect_plus_one(f,
-                                             le64toh(d->data.entry_offset),
-                                             le64toh(d->data.entry_array_offset),
-                                             le64toh(d->data.n_entries),
-                                             realtime,
-                                             test_object_realtime,
-                                             direction,
-                                             ret, offset, NULL);
+        return generic_array_bisect_plus_one(
+                        f,
+                        le64toh(d->data.entry_offset),
+                        le64toh(d->data.entry_array_offset),
+                        le64toh(d->data.n_entries),
+                        realtime,
+                        test_object_realtime,
+                        direction,
+                        ret, ret_offset, NULL);
 }
 
 void journal_file_dump(JournalFile *f) {
@@ -2981,7 +3138,7 @@ void journal_file_dump(JournalFile *f) {
 
         journal_file_print_header(f);
 
-        p = le64toh(f->header->header_size);
+        p = le64toh(READ_NOW(f->header->header_size));
         while (p != 0) {
                 r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &o);
                 if (r < 0)
@@ -3038,7 +3195,7 @@ void journal_file_dump(JournalFile *f) {
                 if (p == le64toh(f->header->tail_object_offset))
                         p = 0;
                 else
-                        p = p + ALIGN64(le64toh(o->object.size));
+                        p += ALIGN64(le64toh(o->object.size));
         }
 
         return;
@@ -3071,7 +3228,7 @@ void journal_file_print_header(JournalFile *f) {
                "Sequential number ID: %s\n"
                "State: %s\n"
                "Compatible flags:%s%s\n"
-               "Incompatible flags:%s%s%s\n"
+               "Incompatible flags:%s%s%s%s%s\n"
                "Header size: %"PRIu64"\n"
                "Arena size: %"PRIu64"\n"
                "Data hash table size: %"PRIu64"\n"
@@ -3096,6 +3253,8 @@ void journal_file_print_header(JournalFile *f) {
                (le32toh(f->header->compatible_flags) & ~HEADER_COMPATIBLE_ANY) ? " ???" : "",
                JOURNAL_HEADER_COMPRESSED_XZ(f->header) ? " COMPRESSED-XZ" : "",
                JOURNAL_HEADER_COMPRESSED_LZ4(f->header) ? " COMPRESSED-LZ4" : "",
+               JOURNAL_HEADER_COMPRESSED_ZSTD(f->header) ? " COMPRESSED-ZSTD" : "",
+               JOURNAL_HEADER_KEYED_HASH(f->header) ? " KEYED-HASH" : "",
                (le32toh(f->header->incompatible_flags) & ~HEADER_INCOMPATIBLE_ANY) ? " ???" : "",
                le64toh(f->header->header_size),
                le64toh(f->header->arena_size),
@@ -3129,6 +3288,14 @@ void journal_file_print_header(JournalFile *f) {
                 printf("Entry array objects: %"PRIu64"\n",
                        le64toh(f->header->n_entry_arrays));
 
+        if (JOURNAL_HEADER_CONTAINS(f->header, field_hash_chain_depth))
+                printf("Deepest field hash chain: %" PRIu64"\n",
+                       f->header->field_hash_chain_depth);
+
+        if (JOURNAL_HEADER_CONTAINS(f->header, data_hash_chain_depth))
+                printf("Deepest data hash chain: %" PRIu64"\n",
+                       f->header->data_hash_chain_depth);
+
         if (fstat(f->fd, &st) >= 0)
                 printf("Disk usage: %s\n", format_bytes(bytes, sizeof(bytes), (uint64_t) st.st_blocks * 512ULL));
 }
@@ -3208,7 +3375,9 @@ int journal_file_open(
                 .prot = prot_from_flags(flags),
                 .writable = (flags & O_ACCMODE) != O_RDONLY,
 
-#if HAVE_LZ4
+#if HAVE_ZSTD
+                .compress_zstd = compress,
+#elif HAVE_LZ4
                 .compress_lz4 = compress,
 #elif HAVE_XZ
                 .compress_xz = compress,
@@ -3221,19 +3390,31 @@ int journal_file_open(
 #endif
         };
 
+        /* We turn on keyed hashes by default, but provide an environment variable to turn them off, if
+         * people really want that */
+        r = getenv_bool("SYSTEMD_JOURNAL_KEYED_HASH");
+        if (r < 0) {
+                if (r != -ENXIO)
+                        log_debug_errno(r, "Failed to parse $SYSTEMD_JOURNAL_KEYED_HASH environment variable, ignoring.");
+                f->keyed_hash = true;
+        } else
+                f->keyed_hash = r;
+
         if (DEBUG_LOGGING) {
-                static int last_seal = -1, last_compress = -1;
+                static int last_seal = -1, last_compress = -1, last_keyed_hash = -1;
                 static uint64_t last_bytes = UINT64_MAX;
                 char bytes[FORMAT_BYTES_MAX];
 
                 if (last_seal != f->seal ||
+                    last_keyed_hash != f->keyed_hash ||
                     last_compress != JOURNAL_FILE_COMPRESS(f) ||
                     last_bytes != f->compress_threshold_bytes) {
 
-                        log_debug("Journal effective settings seal=%s compress=%s compress_threshold_bytes=%s",
-                                  yes_no(f->seal), yes_no(JOURNAL_FILE_COMPRESS(f)),
+                        log_debug("Journal effective settings seal=%s keyed_hash=%s compress=%s compress_threshold_bytes=%s",
+                                  yes_no(f->seal), yes_no(f->keyed_hash), yes_no(JOURNAL_FILE_COMPRESS(f)),
                                   format_bytes(bytes, sizeof bytes, f->compress_threshold_bytes));
                         last_seal = f->seal;
+                        last_keyed_hash = f->keyed_hash;
                         last_compress = JOURNAL_FILE_COMPRESS(f);
                         last_bytes = f->compress_threshold_bytes;
                 }
@@ -3659,7 +3840,11 @@ int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint6
                 if (le_hash != o->data.hash)
                         return -EBADMSG;
 
-                l = le64toh(o->object.size) - offsetof(Object, data.payload);
+                l = le64toh(READ_NOW(o->object.size));
+                if (l < offsetof(Object, data.payload))
+                        return -EBADMSG;
+
+                l -= offsetof(Object, data.payload);
                 t = (size_t) l;
 
                 /* We hit the limit on 32bit machines */
@@ -3667,7 +3852,7 @@ int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint6
                         return -E2BIG;
 
                 if (o->object.flags & OBJECT_COMPRESSION_MASK) {
-#if HAVE_XZ || HAVE_LZ4
+#if HAVE_COMPRESSION
                         size_t rsize = 0;
 
                         r = decompress_blob(o->object.flags & OBJECT_COMPRESSION_MASK,
@@ -3687,7 +3872,11 @@ int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint6
                 if (r < 0)
                         return r;
 
-                xor_hash ^= le64toh(u->data.hash);
+                if (JOURNAL_HEADER_KEYED_HASH(to->header))
+                        xor_hash ^= jenkins_hash64(data, l);
+                else
+                        xor_hash ^= le64toh(u->data.hash);
+
                 items[i].object_offset = htole64(h);
                 items[i].hash = u->data.hash;
 
@@ -3874,11 +4063,9 @@ bool journal_file_rotate_suggested(JournalFile *f, usec_t max_file_usec) {
                 return true;
         }
 
-        /* Let's check if the hash tables grew over a certain fill
-         * level (75%, borrowing this value from Java's hash table
-         * implementation), and if so suggest a rotation. To calculate
-         * the fill level we need the n_data field, which only exists
-         * in newer versions. */
+        /* Let's check if the hash tables grew over a certain fill level (75%, borrowing this value from
+         * Java's hash table implementation), and if so suggest a rotation. To calculate the fill level we
+         * need the n_data field, which only exists in newer versions. */
 
         if (JOURNAL_HEADER_CONTAINS(f->header, n_data))
                 if (le64toh(f->header->n_data) * 4ULL > (le64toh(f->header->data_hash_table_size) / sizeof(HashItem)) * 3ULL) {
@@ -3902,6 +4089,22 @@ bool journal_file_rotate_suggested(JournalFile *f, usec_t max_file_usec) {
                         return true;
                 }
 
+        /* If there are too many hash collisions somebody is most likely playing games with us. Hence, if our
+         * longest chain is longer than some threshold, let's suggest rotation. */
+        if (JOURNAL_HEADER_CONTAINS(f->header, data_hash_chain_depth) &&
+            le64toh(f->header->data_hash_chain_depth) > HASH_CHAIN_DEPTH_MAX) {
+                log_debug("Data hash table of %s has deepest hash chain of length %" PRIu64 ", suggesting rotation.",
+                          f->path, le64toh(f->header->data_hash_chain_depth));
+                return true;
+        }
+
+        if (JOURNAL_HEADER_CONTAINS(f->header, field_hash_chain_depth) &&
+            le64toh(f->header->field_hash_chain_depth) > HASH_CHAIN_DEPTH_MAX) {
+                log_debug("Field hash table of %s has deepest hash chain of length at %" PRIu64 ", suggesting rotation.",
+                          f->path, le64toh(f->header->field_hash_chain_depth));
+                return true;
+        }
+
         /* Are the data objects properly indexed by field objects? */
         if (JOURNAL_HEADER_CONTAINS(f->header, n_data) &&
             JOURNAL_HEADER_CONTAINS(f->header, n_fields) &&
index cf0f7691fb187f933d8b39467ea56dafd88b2fc5..f80bf5d26b1371c5fbf989f70cfa9769ed0a3914 100644 (file)
@@ -67,10 +67,12 @@ typedef struct JournalFile {
         bool writable:1;
         bool compress_xz:1;
         bool compress_lz4:1;
+        bool compress_zstd:1;
         bool seal:1;
         bool defrag_on_close:1;
         bool close_fd:1;
         bool archive:1;
+        bool keyed_hash:1;
 
         direction_t last_direction;
         LocationType location_type;
@@ -105,7 +107,7 @@ typedef struct JournalFile {
         unsigned last_seen_generation;
 
         uint64_t compress_threshold_bytes;
-#if HAVE_XZ || HAVE_LZ4
+#if HAVE_COMPRESSION
         void *compress_buffer;
         size_t compress_buffer_size;
 #endif
@@ -187,13 +189,19 @@ static inline bool VALID_EPOCH(uint64_t u) {
         (le64toh((h)->header_size) >= offsetof(Header, field) + sizeof((h)->field))
 
 #define JOURNAL_HEADER_SEALED(h) \
-        (!!(le32toh((h)->compatible_flags) & HEADER_COMPATIBLE_SEALED))
+        FLAGS_SET(le32toh((h)->compatible_flags), HEADER_COMPATIBLE_SEALED)
 
 #define JOURNAL_HEADER_COMPRESSED_XZ(h) \
-        (!!(le32toh((h)->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED_XZ))
+        FLAGS_SET(le32toh((h)->incompatible_flags), HEADER_INCOMPATIBLE_COMPRESSED_XZ)
 
 #define JOURNAL_HEADER_COMPRESSED_LZ4(h) \
-        (!!(le32toh((h)->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED_LZ4))
+        FLAGS_SET(le32toh((h)->incompatible_flags), HEADER_INCOMPATIBLE_COMPRESSED_LZ4)
+
+#define JOURNAL_HEADER_COMPRESSED_ZSTD(h) \
+        FLAGS_SET(le32toh((h)->incompatible_flags), HEADER_INCOMPATIBLE_COMPRESSED_ZSTD)
+
+#define JOURNAL_HEADER_KEYED_HASH(h) \
+        FLAGS_SET(le32toh((h)->incompatible_flags), HEADER_INCOMPATIBLE_KEYED_HASH)
 
 int journal_file_move_to_object(JournalFile *f, ObjectType type, uint64_t offset, Object **ret);
 
@@ -260,5 +268,7 @@ int journal_file_map_field_hash_table(JournalFile *f);
 
 static inline bool JOURNAL_FILE_COMPRESS(JournalFile *f) {
         assert(f);
-        return f->compress_xz || f->compress_lz4;
+        return f->compress_xz || f->compress_lz4 || f->compress_zstd;
 }
+
+uint64_t journal_file_hash_data(JournalFile *f, const void *data, size_t sz);
index 1454df602b8594ad8466d8f2121c59c74328d3b9..d87b0a11e5899dc55d49500554ba001ab06ea555 100644 (file)
@@ -32,7 +32,7 @@ struct Match {
         /* For concrete matches */
         char *data;
         size_t size;
-        le64_t le_hash;
+        uint64_t hash; /* old-style jenkins hash. New-style siphash is different per file, hence won't be cached here */
 
         /* For terms */
         LIST_HEAD(Match, matches);
@@ -41,10 +41,10 @@ struct Match {
 struct Location {
         LocationType type;
 
-        bool seqnum_set;
-        bool realtime_set;
-        bool monotonic_set;
-        bool xor_hash_set;
+        bool seqnum_set:1;
+        bool realtime_set:1;
+        bool monotonic_set:1;
+        bool xor_hash_set:1;
 
         uint64_t seqnum;
         sd_id128_t seqnum_id;
@@ -127,3 +127,12 @@ void journal_print_header(sd_journal *j);
 
 #define JOURNAL_FOREACH_DATA_RETVAL(j, data, l, retval)                     \
         for (sd_journal_restart_data(j); ((retval) = sd_journal_enumerate_data((j), &(data), &(l))) > 0; )
+
+/* All errors that we might encounter while extracting a field that are not real errors,
+ * but only mean that the field is too large or we don't support the compression. */
+static inline bool JOURNAL_ERRNO_IS_UNAVAILABLE_FIELD(int r) {
+        return IN_SET(abs(r),
+                      ENOBUFS,          /* Field or decompressed field too large */
+                      E2BIG,            /* Field too large for pointer width */
+                      EPROTONOSUPPORT); /* Unsupported compression */
+}
index 912ecef73cce0d13a5a8967e0752d1acc624fa15..d51d03acd9de09ccecb815b6002b4224e55ed2d3 100644 (file)
@@ -15,6 +15,7 @@
 #include "errno-util.h"
 #include "fd-util.h"
 #include "io-util.h"
+#include "fileio.h"
 #include "memfd-util.h"
 #include "socket-util.h"
 #include "stdio-util.h"
@@ -73,12 +74,12 @@ _public_ int sd_journal_print(int priority, const char *format, ...) {
 }
 
 _public_ int sd_journal_printv(int priority, const char *format, va_list ap) {
-
-        /* FIXME: Instead of limiting things to LINE_MAX we could do a
-           C99 variable-length array on the stack here in a loop. */
-
-        char buffer[8 + LINE_MAX], p[STRLEN("PRIORITY=") + DECIMAL_STR_MAX(int) + 1];
+        char p[STRLEN("PRIORITY=") + DECIMAL_STR_MAX(int) + 1];
+        char sbuf[LINE_MAX + 8] = "MESSAGE=";
         struct iovec iov[2];
+        int len;
+        va_list aq;
+        char *buffer = sbuf;
 
         assert_return(priority >= 0, -EINVAL);
         assert_return(priority <= 7, -EINVAL);
@@ -86,14 +87,25 @@ _public_ int sd_journal_printv(int priority, const char *format, va_list ap) {
 
         xsprintf(p, "PRIORITY=%i", priority & LOG_PRIMASK);
 
-        memcpy(buffer, "MESSAGE=", 8);
-        vsnprintf(buffer+8, sizeof(buffer) - 8, format, ap);
+        va_copy(aq, ap);
+        len = vsnprintf(buffer + 8, LINE_MAX, format, aq);
+        va_end(aq);
+
+        if (len >= (int)LONG_LINE_MAX - 8)
+                return -ENOBUFS;
+
+        /* Allocate large buffer to accommodate big message */
+        if (len >= LINE_MAX) {
+                buffer = alloca(len + 9);
+                memcpy(buffer, "MESSAGE=", 8);
+                assert_se(vsnprintf(buffer + 8, len + 1, format, ap) == len);
+        }
 
         /* Strip trailing whitespace, keep prefix whitespace. */
         (void) strstrip(buffer);
 
         /* Suppress empty lines */
-        if (isempty(buffer+8))
+        if (isempty(buffer + 8))
                 return 0;
 
         iov[0] = IOVEC_MAKE_STRING(buffer);
@@ -437,9 +449,13 @@ _public_ int sd_journal_print_with_location(int priority, const char *file, cons
 }
 
 _public_ int sd_journal_printv_with_location(int priority, const char *file, const char *line, const char *func, const char *format, va_list ap) {
-        char buffer[8 + LINE_MAX], p[STRLEN("PRIORITY=") + DECIMAL_STR_MAX(int) + 1];
+        char p[STRLEN("PRIORITY=") + DECIMAL_STR_MAX(int) + 1];
+        char sbuf[LINE_MAX + 8] = "MESSAGE=";
         struct iovec iov[5];
         char *f;
+        int len;
+        char *buffer = sbuf;
+        va_list aq;
 
         assert_return(priority >= 0, -EINVAL);
         assert_return(priority <= 7, -EINVAL);
@@ -447,14 +463,25 @@ _public_ int sd_journal_printv_with_location(int priority, const char *file, con
 
         xsprintf(p, "PRIORITY=%i", priority & LOG_PRIMASK);
 
-        memcpy(buffer, "MESSAGE=", 8);
-        vsnprintf(buffer+8, sizeof(buffer) - 8, format, ap);
+        va_copy(aq, ap);
+        len = vsnprintf(buffer + 8, LINE_MAX, format, aq);
+        va_end(aq);
+
+        if (len >= (int)LONG_LINE_MAX - 8)
+                return -ENOBUFS;
+
+        /* Allocate large buffer to accommodate big message */
+        if (len >= LINE_MAX) {
+                buffer = alloca(len + 9);
+                memcpy(buffer, "MESSAGE=", 8);
+                assert_se(vsnprintf(buffer + 8, len + 1, format, ap) == len);
+        }
 
         /* Strip trailing whitespace, keep prefixing whitespace */
         (void) strstrip(buffer);
 
         /* Suppress empty lines */
-        if (isempty(buffer+8))
+        if (isempty(buffer + 8))
                 return 0;
 
         /* func is initialized from __func__ which is not a macro, but
index 344b7b019a57c3148bccf6c5e1cfa3305ebcb8a6..eddb8054bf7dbc84d6b679c22b1cb13bd80b2422 100644 (file)
@@ -163,9 +163,9 @@ static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o
                                 return r;
                         }
 
-                        h2 = hash64(b, b_size);
+                        h2 = journal_file_hash_data(f, b, b_size);
                 } else
-                        h2 = hash64(o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload));
+                        h2 = journal_file_hash_data(f, o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload));
 
                 if (h1 != h2) {
                         error(offset, "Invalid hash (%08"PRIx64" vs. %08"PRIx64, h1, h2);
@@ -925,9 +925,10 @@ int journal_file_verify(
                         goto fail;
                 }
 
-                if ((o->object.flags & OBJECT_COMPRESSED_XZ) &&
-                    (o->object.flags & OBJECT_COMPRESSED_LZ4)) {
-                        error(p, "Objected with double compression");
+                if (!!(o->object.flags & OBJECT_COMPRESSED_XZ) +
+                    !!(o->object.flags & OBJECT_COMPRESSED_LZ4) +
+                    !!(o->object.flags & OBJECT_COMPRESSED_ZSTD) > 1) {
+                        error(p, "Object has multiple compression flags set");
                         r = -EINVAL;
                         goto fail;
                 }
@@ -944,6 +945,12 @@ int journal_file_verify(
                         goto fail;
                 }
 
+                if ((o->object.flags & OBJECT_COMPRESSED_ZSTD) && !JOURNAL_HEADER_COMPRESSED_ZSTD(f->header)) {
+                        error(p, "ZSTD compressed object in file without ZSTD compression");
+                        r = -EBADMSG;
+                        goto fail;
+                }
+
                 switch (o->object.type) {
 
                 case OBJECT_DATA:
index 25492dad1c4a7551bef45abbadcd1230641e6f82..8d4897b942f778807e168415deecec262bec92cf 100644 (file)
@@ -351,7 +351,7 @@ static int help(void) {
                "  -p --priority=RANGE        Show entries with the specified priority\n"
                "     --facility=FACILITY...  Show entries with the specified facilities\n"
                "  -g --grep=PATTERN          Show entries with MESSAGE matching PATTERN\n"
-               "     --case-sensitive[=BOOL] Force case sensitive or insenstive matching\n"
+               "     --case-sensitive[=BOOL] Force case sensitive or insensitive 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"
@@ -875,12 +875,7 @@ static int parse_argv(int argc, char *argv[]) {
                                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                                                "Bad --facility= argument \"%s\".", fac);
 
-                                r = set_ensure_allocated(&arg_facilities, NULL);
-                                if (r < 0)
-                                        return log_oom();
-
-                                r = set_put(arg_facilities, INT_TO_PTR(num));
-                                if (r < 0)
+                                if (set_ensure_put(&arg_facilities, NULL, INT_TO_PTR(num)) < 0)
                                         return log_oom();
                         }
 
@@ -1783,7 +1778,6 @@ static int setup_keys(void) {
         int fd = -1, r;
         sd_id128_t machine, boot;
         char *p = NULL, *k = NULL;
-        struct FSSHeader h;
         uint64_t n;
         struct stat st;
 
@@ -1873,15 +1867,17 @@ static int setup_keys(void) {
         if (r < 0)
                 log_warning_errno(r, "Failed to set file attributes: %m");
 
-        zero(h);
+        struct FSSHeader h = {
+                .machine_id = machine,
+                .boot_id = boot,
+                .header_size = htole64(sizeof(h)),
+                .start_usec = htole64(n * arg_interval),
+                .interval_usec = htole64(arg_interval),
+                .fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR),
+                .fsprg_state_size = htole64(state_size),
+        };
+
         memcpy(h.signature, "KSHHRHLP", 8);
-        h.machine_id = machine;
-        h.boot_id = boot;
-        h.header_size = htole64(sizeof(h));
-        h.start_usec = htole64(n * arg_interval);
-        h.interval_usec = htole64(arg_interval);
-        h.fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR);
-        h.fsprg_state_size = htole64(state_size);
 
         r = loop_write(fd, &h, sizeof(h), false);
         if (r < 0) {
@@ -2092,10 +2088,13 @@ static int wait_for_change(sd_journal *j, int poll_fd) {
                 return log_error_errno(errno, "Couldn't wait for journal event: %m");
         }
 
-        if (pollfds[1].revents & (POLLHUP|POLLERR)) /* STDOUT has been closed? */
+        if (pollfds[1].revents & (POLLHUP|POLLERR|POLLNVAL)) /* STDOUT has been closed? */
                 return log_debug_errno(SYNTHETIC_ERRNO(ECANCELED),
                                        "Standard output has been closed.");
 
+        if (pollfds[0].revents & POLLNVAL)
+                return log_debug_errno(SYNTHETIC_ERRNO(EBADF), "Change fd closed?");
+
         r = sd_journal_process(j);
         if (r < 0)
                 return log_error_errno(r, "Failed to process journal events: %m");
@@ -2111,9 +2110,7 @@ int main(int argc, char *argv[]) {
         int n_shown = 0, r, poll_fd = -1;
 
         setlocale(LC_ALL, "");
-        log_show_color(true);
-        log_parse_environment();
-        log_open();
+        log_setup_cli();
 
         /* Increase max number of open files if we can, we might needs this when browsing journal files, which might be
          * split up into many files. */
index 5c31c43705b586869513f0d2b282513727e77e93..a5a78b7746948cdc23c108ea6f63e40371c4d34a 100644 (file)
@@ -2,6 +2,7 @@
 
 #include "alloc-util.h"
 #include "audit-type.h"
+#include "errno-util.h"
 #include "fd-util.h"
 #include "hexdecoct.h"
 #include "io-util.h"
@@ -512,7 +513,7 @@ int server_open_audit(Server *s) {
 
                 s->audit_fd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_AUDIT);
                 if (s->audit_fd < 0) {
-                        if (IN_SET(errno, EAFNOSUPPORT, EPROTONOSUPPORT))
+                        if (ERRNO_IS_NOT_SUPPORTED(errno))
                                 log_debug("Audit not supported in the kernel.");
                         else
                                 log_warning_errno(errno, "Failed to create audit socket, ignoring: %m");
index ec404145eec906c53616fb4bbbf85f99ee9b6a0f..6e7c806fd8218bde2f51a36f395dd607579504c4 100644 (file)
@@ -315,7 +315,7 @@ static int server_read_dev_kmsg(Server *s) {
                 if (IN_SET(errno, EAGAIN, EINTR, EPIPE))
                         return 0;
 
-                return log_error_errno(errno, "Failed to read from kernel: %m");
+                return log_error_errno(errno, "Failed to read from /dev/kmsg: %m");
         }
 
         dev_kmsg_record(s, buffer, l);
@@ -423,7 +423,7 @@ int server_open_kernel_seqnum(Server *s) {
 
         assert(s);
 
-        /* We store the seqnum we last read in an mmaped file. That way we can just use it like a variable,
+        /* We store the seqnum we last read in an mmapped file. That way we can just use it like a variable,
          * but it is persistent and automatically flushed at reboot. */
 
         if (!s->read_kmsg)
index 072b3d5ae2140f111b0f6301bad35b0d2f0e15aa..5865bf980962e7a73b7608fd54afcb090b61ff80 100644 (file)
@@ -1268,21 +1268,14 @@ int server_process_datagram(
         int *fds = NULL, v = 0;
         size_t n_fds = 0;
 
-        union {
-                struct cmsghdr cmsghdr;
-
-                /* We use NAME_MAX space for the SELinux label
-                 * here. The kernel currently enforces no
-                 * limit, but according to suggestions from
-                 * the SELinux people this will change and it
-                 * will probably be identical to NAME_MAX. For
-                 * now we use that, but this should be updated
-                 * one day when the final limit is known. */
-                uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) +
-                            CMSG_SPACE(sizeof(struct timeval)) +
-                            CMSG_SPACE(sizeof(int)) + /* fd */
-                            CMSG_SPACE(NAME_MAX)]; /* selinux label */
-        } control = {};
+        /* We use NAME_MAX space for the SELinux label here. The kernel currently enforces no limit, but
+         * according to suggestions from the SELinux people this will change and it will probably be
+         * identical to NAME_MAX. For now we use that, but this should be updated one day when the final
+         * limit is known. */
+        CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred)) +
+                         CMSG_SPACE(sizeof(struct timeval)) +
+                         CMSG_SPACE(sizeof(int)) + /* fd */
+                         CMSG_SPACE(NAME_MAX) /* selinux label */) control;
 
         union sockaddr_union sa = {};
 
@@ -1640,23 +1633,24 @@ static int server_parse_config_file(Server *s) {
                 /* If we are running in namespace mode, load the namespace specific configuration file, and nothing else */
                 namespaced = strjoina(PKGSYSCONFDIR "/journald@", s->namespace, ".conf");
 
-                r = config_parse(
-                                NULL,
-                                namespaced, NULL,
-                                "Journal\0",
-                                config_item_perf_lookup, journald_gperf_lookup,
-                                CONFIG_PARSE_WARN, s);
+                r = config_parse(NULL,
+                                 namespaced, NULL,
+                                 "Journal\0",
+                                 config_item_perf_lookup, journald_gperf_lookup,
+                                 CONFIG_PARSE_WARN, s,
+                                 NULL);
                 if (r < 0)
                         return r;
 
                 return 0;
         }
 
-        return config_parse_many_nulstr(PKGSYSCONFDIR "/journald.conf",
-                                        CONF_PATHS_NULSTR("systemd/journald.conf.d"),
-                                        "Journal\0",
-                                        config_item_perf_lookup, journald_gperf_lookup,
-                                        CONFIG_PARSE_WARN, s);
+        return config_parse_many_nulstr(
+                        PKGSYSCONFDIR "/journald.conf",
+                        CONF_PATHS_NULSTR("systemd/journald.conf.d"),
+                        "Journal\0",
+                        config_item_perf_lookup, journald_gperf_lookup,
+                        CONFIG_PARSE_WARN, s, NULL);
 }
 
 static int server_dispatch_sync(sd_event_source *es, usec_t t, void *userdata) {
@@ -1752,7 +1746,7 @@ static int server_open_hostname(Server *s) {
 
         r = sd_event_source_set_priority(s->hostname_event_source, SD_EVENT_PRIORITY_IMPORTANT-10);
         if (r < 0)
-                return log_error_errno(r, "Failed to adjust priority of host name event source: %m");
+                return log_error_errno(r, "Failed to adjust priority of hostname event source: %m");
 
         return 0;
 }
@@ -1956,7 +1950,7 @@ static int vl_method_synchronize(Varlink *link, JsonVariant *parameters, Varlink
         if (r < 0)
                 return log_error_errno(r, "Failed to set event source destroy callback: %m");
 
-        varlink_ref(link); /* The varlink object is now left to the destroy callack to unref */
+        varlink_ref(link); /* The varlink object is now left to the destroy callback to unref */
 
         r = sd_event_source_set_priority(event_source, SD_EVENT_PRIORITY_NORMAL+15);
         if (r < 0)
index ad90062176846e7f7a07bc64df067d77b3ec97d8..241e2572e6de40702cd3b6dca744a52cc262ee1f 100644 (file)
@@ -58,6 +58,9 @@ typedef enum LineBreak {
         LINE_BREAK_NUL,
         LINE_BREAK_LINE_MAX,
         LINE_BREAK_EOF,
+        LINE_BREAK_PID_CHANGE,
+        _LINE_BREAK_MAX,
+        _LINE_BREAK_INVALID = -1,
 } LineBreak;
 
 struct StdoutStream {
@@ -238,7 +241,11 @@ fail:
         return log_error_errno(r, "Failed to save stream data %s: %m", s->state_file);
 }
 
-static int stdout_stream_log(StdoutStream *s, const char *p, LineBreak line_break) {
+static int stdout_stream_log(
+                StdoutStream *s,
+                const char *p,
+                LineBreak line_break) {
+
         struct iovec *iovec;
         int priority;
         char syslog_priority[] = "PRIORITY=\0";
@@ -250,6 +257,9 @@ static int stdout_stream_log(StdoutStream *s, const char *p, LineBreak line_brea
         assert(s);
         assert(p);
 
+        assert(line_break >= 0);
+        assert(line_break < _LINE_BREAK_MAX);
+
         if (s->context)
                 (void) client_context_maybe_refresh(s->server, s->context, NULL, NULL, 0, NULL, USEC_INFINITY);
         else if (pid_is_valid(s->ucred.pid)) {
@@ -301,17 +311,20 @@ static int stdout_stream_log(StdoutStream *s, const char *p, LineBreak line_brea
                         iovec[n++] = IOVEC_MAKE_STRING(syslog_identifier);
         }
 
-        if (line_break != LINE_BREAK_NEWLINE) {
-                const char *c;
+        static const char * const line_break_field_table[_LINE_BREAK_MAX] = {
+                [LINE_BREAK_NEWLINE]    = NULL, /* Do not add field if traditional newline */
+                [LINE_BREAK_NUL]        = "_LINE_BREAK=nul",
+                [LINE_BREAK_LINE_MAX]   = "_LINE_BREAK=line-max",
+                [LINE_BREAK_EOF]        = "_LINE_BREAK=eof",
+                [LINE_BREAK_PID_CHANGE] = "_LINE_BREAK=pid-change",
+        };
 
-                /* If this log message was generated due to an uncommon line break then mention this in the log
-                 * entry */
+        const char *c = line_break_field_table[line_break];
 
-                c =     line_break == LINE_BREAK_NUL ?      "_LINE_BREAK=nul" :
-                        line_break == LINE_BREAK_LINE_MAX ? "_LINE_BREAK=line-max" :
-                                                            "_LINE_BREAK=eof";
+        /* If this log message was generated due to an uncommon line break then mention this in the log
+         * entry */
+        if (c)
                 iovec[n++] = IOVEC_MAKE_STRING(c);
-        }
 
         message = strjoin("MESSAGE=", p);
         if (message)
@@ -322,8 +335,8 @@ static int stdout_stream_log(StdoutStream *s, const char *p, LineBreak line_brea
 }
 
 static int stdout_stream_line(StdoutStream *s, char *p, LineBreak line_break) {
-        int r;
         char *orig;
+        int r;
 
         assert(s);
         assert(p);
@@ -332,10 +345,9 @@ static int stdout_stream_line(StdoutStream *s, char *p, LineBreak line_break) {
         p = strstrip(p);
 
         /* line breaks by NUL, line max length or EOF are not permissible during the negotiation part of the protocol */
-        if (line_break != LINE_BREAK_NEWLINE && s->state != STDOUT_STREAM_RUNNING) {
-                log_warning("Control protocol line not properly terminated.");
-                return -EINVAL;
-        }
+        if (line_break != LINE_BREAK_NEWLINE && s->state != STDOUT_STREAM_RUNNING)
+                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+                                         "Control protocol line not properly terminated.");
 
         switch (s->state) {
 
@@ -425,21 +437,43 @@ static int stdout_stream_line(StdoutStream *s, char *p, LineBreak line_break) {
         assert_not_reached("Unknown stream state");
 }
 
-static int stdout_stream_scan(StdoutStream *s, bool force_flush) {
-        char *p;
-        size_t remaining;
+static int stdout_stream_found(
+                StdoutStream *s,
+                char *p,
+                size_t l,
+                LineBreak line_break) {
+
+        char saved;
         int r;
 
         assert(s);
+        assert(p);
+
+        /* Let's NUL terminate the specified buffer for this call, and revert back afterwards */
+        saved = p[l];
+        p[l] = 0;
+        r = stdout_stream_line(s, p, line_break);
+        p[l] = saved;
+
+        return r;
+}
+
+static int stdout_stream_scan(
+                StdoutStream *s,
+                char *p,
+                size_t remaining,
+                LineBreak force_flush,
+                size_t *ret_consumed) {
 
-        p = s->buffer;
-        remaining = s->length;
+        size_t consumed = 0;
+        int r;
 
-        /* XXX: This function does nothing if (s->length == 0) */
+        assert(s);
+        assert(p);
 
         for (;;) {
                 LineBreak line_break;
-                size_t skip;
+                size_t skip, found;
                 char *end1, *end2;
 
                 end1 = memchr(p, '\n', remaining);
@@ -447,62 +481,59 @@ static int stdout_stream_scan(StdoutStream *s, bool force_flush) {
 
                 if (end2) {
                         /* We found a NUL terminator */
-                        skip = end2 - p + 1;
+                        found = end2 - p;
+                        skip = found + 1;
                         line_break = LINE_BREAK_NUL;
                 } else if (end1) {
                         /* We found a \n terminator */
-                        *end1 = 0;
-                        skip = end1 - p + 1;
+                        found = end1 - p;
+                        skip = found + 1;
                         line_break = LINE_BREAK_NEWLINE;
                 } else if (remaining >= s->server->line_max) {
                         /* Force a line break after the maximum line length */
-                        *(p + s->server->line_max) = 0;
-                        skip = remaining;
+                        found = skip = s->server->line_max;
                         line_break = LINE_BREAK_LINE_MAX;
                 } else
                         break;
 
-                r = stdout_stream_line(s, p, line_break);
+                r = stdout_stream_found(s, p, found, line_break);
                 if (r < 0)
                         return r;
 
-                remaining -= skip;
                 p += skip;
+                consumed += skip;
+                remaining -= skip;
         }
 
-        if (force_flush && remaining > 0) {
-                p[remaining] = 0;
-                r = stdout_stream_line(s, p, LINE_BREAK_EOF);
+        if (force_flush >= 0 && remaining > 0) {
+                r = stdout_stream_found(s, p, remaining, force_flush);
                 if (r < 0)
                         return r;
 
-                p += remaining;
-                remaining = 0;
+                consumed += remaining;
         }
 
-        if (p > s->buffer) {
-                memmove(s->buffer, p, remaining);
-                s->length = remaining;
-        }
+        if (ret_consumed)
+                *ret_consumed = consumed;
 
         return 0;
 }
 
 static int stdout_stream_process(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
-        uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
+        CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control;
         StdoutStream *s = userdata;
-        struct ucred *ucred = NULL;
-        struct cmsghdr *cmsg;
+        size_t limit, consumed;
+        struct ucred *ucred;
         struct iovec iovec;
-        size_t limit;
         ssize_t l;
+        char *p;
         int r;
 
         struct msghdr msghdr = {
                 .msg_iov = &iovec,
                 .msg_iovlen = 1,
-                .msg_control = buf,
-                .msg_controllen = sizeof(buf),
+                .msg_control = &control,
+                .msg_controllen = sizeof(control),
         };
 
         assert(s);
@@ -523,7 +554,7 @@ static int stdout_stream_process(sd_event_source *es, int fd, uint32_t revents,
         /* Try to make use of the allocated buffer in full, but never read more than the configured line size. Also,
          * always leave room for a terminating NUL we might need to add. */
         limit = MIN(s->allocated - 1, s->server->line_max);
-
+        assert(s->length <= limit);
         iovec = IOVEC_MAKE(s->buffer + s->length, limit - s->length);
 
         l = recvmsg(s->fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
@@ -537,43 +568,42 @@ static int stdout_stream_process(sd_event_source *es, int fd, uint32_t revents,
         cmsg_close_all(&msghdr);
 
         if (l == 0) {
-                stdout_stream_scan(s, true);
+                (void) stdout_stream_scan(s, s->buffer, s->length, /* force_flush = */ LINE_BREAK_EOF, NULL);
                 goto terminate;
         }
 
-        CMSG_FOREACH(cmsg, &msghdr)
-                if (cmsg->cmsg_level == SOL_SOCKET &&
-                    cmsg->cmsg_type == SCM_CREDENTIALS &&
-                    cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
-                        assert(!ucred);
-                        ucred = (struct ucred *)CMSG_DATA(cmsg);
-                        break;
-                }
-
-        /* Invalidate the context if the pid of the sender changed.
-         * This happens when a forked process inherits stdout / stderr
-         * from a parent. In this case getpeercred returns the ucred
-         * of the parent, which can be invalid if the parent has exited
-         * in the meantime.
-         */
+        /* Invalidate the context if the PID of the sender changed. This happens when a forked process
+         * inherits stdout/stderr from a parent. In this case getpeercred() returns the ucred of the parent,
+         * which can be invalid if the parent has exited in the meantime. */
+        ucred = CMSG_FIND_DATA(&msghdr, SOL_SOCKET, SCM_CREDENTIALS, struct ucred);
         if (ucred && ucred->pid != s->ucred.pid) {
-                /* force out any previously half-written lines from a
-                 * different process, before we switch to the new ucred
-                 * structure for everything we just added */
-                r = stdout_stream_scan(s, true);
+                /* Force out any previously half-written lines from a different process, before we switch to
+                 * the new ucred structure for everything we just added */
+                r = stdout_stream_scan(s, s->buffer, s->length, /* force_flush = */ LINE_BREAK_PID_CHANGE, NULL);
                 if (r < 0)
                         goto terminate;
 
-                s->ucred = *ucred;
-                client_context_release(s->server, s->context);
-                s->context = NULL;
+                s->context = client_context_release(s->server, s->context);
+
+                p = s->buffer + s->length;
+        } else {
+                p = s->buffer;
+                l += s->length;
         }
 
-        s->length += l;
-        r = stdout_stream_scan(s, false);
+        /* Always copy in the new credentials */
+        if (ucred)
+                s->ucred = *ucred;
+
+        r = stdout_stream_scan(s, p, l, _LINE_BREAK_INVALID, &consumed);
         if (r < 0)
                 goto terminate;
 
+        /* Move what wasn't consumed to the front of the buffer */
+        assert(consumed <= (size_t) l);
+        s->length = l - consumed;
+        memmove(s->buffer, p + consumed, s->length);
+
         return 1;
 
 terminate:
index 1325b563854f6f5da2f9b41def2dc3abdb7712c6..46013c38784d2f1c88b7278bac8f70839f702114 100644 (file)
@@ -39,10 +39,7 @@ static void forward_syslog_iovec(
                 .msg_iovlen = n_iovec,
         };
         struct cmsghdr *cmsg;
-        union {
-                struct cmsghdr cmsghdr;
-                uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
-        } control;
+        CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control;
         const char *j;
         int r;
 
index 787921ffbf767c56053c373f8723bd8488823f7e..0a01269e433980b1c89f933e5f06521d70e2728f 100644 (file)
@@ -13,7 +13,7 @@ void jenkins_hashlittle2(const void *key, size_t length, uint32_t *pc, uint32_t
 
 uint32_t jenkins_hashbig(const void *key, size_t length, uint32_t initval) _pure_;
 
-static inline uint64_t hash64(const void *data, size_t length) {
+static inline uint64_t jenkins_hash64(const void *data, size_t length) {
         uint32_t a = 0, b = 0;
 
         jenkins_hashlittle2(data, length, &a, &b);
index e2f7bd7ec45761e2e9369f9f256a9c1ef802d425..9eb3e1a62625d5510f2480c8e7bff55e61ee7ba2 100644 (file)
@@ -162,7 +162,7 @@ static Window *window_add(MMapCache *m, MMapFileDescriptor *f, int prot, bool ke
         if (!m->last_unused || m->n_windows <= WINDOWS_MIN) {
 
                 /* Allocate a new window */
-                w = new0(Window, 1);
+                w = new(Window, 1);
                 if (!w)
                         return NULL;
                 m->n_windows++;
@@ -171,16 +171,17 @@ static Window *window_add(MMapCache *m, MMapFileDescriptor *f, int prot, bool ke
                 /* Reuse an existing one */
                 w = m->last_unused;
                 window_unlink(w);
-                zero(*w);
         }
 
-        w->cache = m;
-        w->fd = f;
-        w->prot = prot;
-        w->keep_always = keep_always;
-        w->offset = offset;
-        w->size = size;
-        w->ptr = ptr;
+        *w = (Window) {
+                .cache = m,
+                .fd = f,
+                .prot = prot,
+                .keep_always = keep_always,
+                .offset = offset,
+                .size = size,
+                .ptr = ptr,
+        };
 
         LIST_PREPEND(by_fd, f->windows, w);
 
index e968b9de0adb3db06cb5049e6ef149dbe30d15fb..6fb0abb419d9e97f23083071537d813818c91c51 100644 (file)
@@ -45,7 +45,9 @@
 
 #define JOURNAL_FILES_RECHECK_USEC (2 * USEC_PER_SEC)
 
-#define REPLACE_VAR_MAX 256
+/* The maximum size of variable values we'll expand in catalog entries. We bind this to PATH_MAX for now, as
+ * we want to be able to show all officially valid paths at least */
+#define REPLACE_VAR_MAX PATH_MAX
 
 #define DEFAULT_DATA_THRESHOLD (64*1024)
 
@@ -115,28 +117,24 @@ static void detach_location(sd_journal *j) {
                 journal_file_reset_location(f);
 }
 
-static void reset_location(sd_journal *j) {
-        assert(j);
-
-        detach_location(j);
-        zero(j->current_location);
-}
-
 static void init_location(Location *l, LocationType type, JournalFile *f, Object *o) {
         assert(l);
         assert(IN_SET(type, LOCATION_DISCRETE, LOCATION_SEEK));
         assert(f);
-        assert(o->object.type == OBJECT_ENTRY);
-
-        l->type = type;
-        l->seqnum = le64toh(o->entry.seqnum);
-        l->seqnum_id = f->header->seqnum_id;
-        l->realtime = le64toh(o->entry.realtime);
-        l->monotonic = le64toh(o->entry.monotonic);
-        l->boot_id = o->entry.boot_id;
-        l->xor_hash = le64toh(o->entry.xor_hash);
 
-        l->seqnum_set = l->realtime_set = l->monotonic_set = l->xor_hash_set = true;
+        *l = (Location) {
+                .type = type,
+                .seqnum = le64toh(o->entry.seqnum),
+                .seqnum_id = f->header->seqnum_id,
+                .realtime = le64toh(o->entry.realtime),
+                .monotonic = le64toh(o->entry.monotonic),
+                .boot_id = o->entry.boot_id,
+                .xor_hash = le64toh(o->entry.xor_hash),
+                .seqnum_set = true,
+                .realtime_set = true,
+                .monotonic_set = true,
+                .xor_hash_set = true,
+        };
 }
 
 static void set_location(sd_journal *j, JournalFile *f, Object *o) {
@@ -242,7 +240,7 @@ static void match_free_if_empty(Match *m) {
 
 _public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) {
         Match *l3, *l4, *add_here = NULL, *m;
-        le64_t le_hash;
+        uint64_t hash;
 
         assert_return(j, -EINVAL);
         assert_return(!journal_pid_changed(j), -ECHILD);
@@ -281,7 +279,9 @@ _public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size)
         assert(j->level1->type == MATCH_OR_TERM);
         assert(j->level2->type == MATCH_AND_TERM);
 
-        le_hash = htole64(hash64(data, size));
+        /* Old-style Jenkins (unkeyed) hashing only here. We do not cover new-style siphash (keyed) hashing
+         * here, since it's different for each file, and thus can't be pre-calculated in the Match object. */
+        hash = jenkins_hash64(data, size);
 
         LIST_FOREACH(matches, l3, j->level2->matches) {
                 assert(l3->type == MATCH_OR_TERM);
@@ -291,7 +291,7 @@ _public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size)
 
                         /* Exactly the same match already? Then ignore
                          * this addition */
-                        if (l4->le_hash == le_hash &&
+                        if (l4->hash == hash &&
                             l4->size == size &&
                             memcmp(l4->data, data, size) == 0)
                                 return 0;
@@ -317,7 +317,7 @@ _public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size)
         if (!m)
                 goto fail;
 
-        m->le_hash = le_hash;
+        m->hash = hash;
         m->size = size;
         m->data = memdup(data, size);
         if (!m->data)
@@ -503,9 +503,16 @@ static int next_for_match(
         assert(f);
 
         if (m->type == MATCH_DISCRETE) {
-                uint64_t dp;
+                uint64_t dp, hash;
 
-                r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
+                /* If the keyed hash logic is used, we need to calculate the hash fresh per file. Otherwise
+                 * we can use what we pre-calculated. */
+                if (JOURNAL_HEADER_KEYED_HASH(f->header))
+                        hash = journal_file_hash_data(f, m->data, m->size);
+                else
+                        hash = m->hash;
+
+                r = journal_file_find_data_object_with_hash(f, m->data, m->size, hash, NULL, &dp);
                 if (r <= 0)
                         return r;
 
@@ -592,9 +599,14 @@ static int find_location_for_match(
         assert(f);
 
         if (m->type == MATCH_DISCRETE) {
-                uint64_t dp;
+                uint64_t dp, hash;
+
+                if (JOURNAL_HEADER_KEYED_HASH(f->header))
+                        hash = journal_file_hash_data(f, m->data, m->size);
+                else
+                        hash = m->hash;
 
-                r = journal_file_find_data_object_with_hash(f, m->data, m->size, le64toh(m->le_hash), NULL, &dp);
+                r = journal_file_find_data_object_with_hash(f, m->data, m->size, hash, NULL, &dp);
                 if (r <= 0)
                         return r;
 
@@ -1014,9 +1026,10 @@ _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) {
             !realtime_set)
                 return -EINVAL;
 
-        reset_location(j);
-
-        j->current_location.type = LOCATION_SEEK;
+        detach_location(j);
+        j->current_location = (Location) {
+                .type = LOCATION_SEEK,
+        };
 
         if (realtime_set) {
                 j->current_location.realtime = (uint64_t) realtime;
@@ -1129,11 +1142,14 @@ _public_ int sd_journal_seek_monotonic_usec(sd_journal *j, sd_id128_t boot_id, u
         assert_return(j, -EINVAL);
         assert_return(!journal_pid_changed(j), -ECHILD);
 
-        reset_location(j);
-        j->current_location.type = LOCATION_SEEK;
-        j->current_location.boot_id = boot_id;
-        j->current_location.monotonic = usec;
-        j->current_location.monotonic_set = true;
+        detach_location(j);
+
+        j->current_location = (Location) {
+                .type = LOCATION_SEEK,
+                .boot_id = boot_id,
+                .monotonic = usec,
+                .monotonic_set = true,
+        };
 
         return 0;
 }
@@ -1142,10 +1158,13 @@ _public_ int sd_journal_seek_realtime_usec(sd_journal *j, uint64_t usec) {
         assert_return(j, -EINVAL);
         assert_return(!journal_pid_changed(j), -ECHILD);
 
-        reset_location(j);
-        j->current_location.type = LOCATION_SEEK;
-        j->current_location.realtime = usec;
-        j->current_location.realtime_set = true;
+        detach_location(j);
+
+        j->current_location = (Location) {
+                .type = LOCATION_SEEK,
+                .realtime = usec,
+                .realtime_set = true,
+        };
 
         return 0;
 }
@@ -1154,8 +1173,11 @@ _public_ int sd_journal_seek_head(sd_journal *j) {
         assert_return(j, -EINVAL);
         assert_return(!journal_pid_changed(j), -ECHILD);
 
-        reset_location(j);
-        j->current_location.type = LOCATION_HEAD;
+        detach_location(j);
+
+        j->current_location = (Location) {
+                .type = LOCATION_HEAD,
+        };
 
         return 0;
 }
@@ -1164,8 +1186,11 @@ _public_ int sd_journal_seek_tail(sd_journal *j) {
         assert_return(j, -EINVAL);
         assert_return(!journal_pid_changed(j), -ECHILD);
 
-        reset_location(j);
-        j->current_location.type = LOCATION_TAIL;
+        detach_location(j);
+
+        j->current_location = (Location) {
+                .type = LOCATION_TAIL,
+        };
 
         return 0;
 }
@@ -1719,7 +1744,7 @@ static int add_root_directory(sd_journal *j, const char *p, bool missing_ok) {
                         goto fail;
                 }
         } else {
-                int dfd;
+                _cleanup_close_ int dfd = -1;
 
                 /* If there's no path specified, then we use the top-level fd itself. We duplicate the fd here, since
                  * opendir() will take possession of the fd, and close it, which we don't want. */
@@ -1732,10 +1757,9 @@ static int add_root_directory(sd_journal *j, const char *p, bool missing_ok) {
                         goto fail;
                 }
 
-                d = fdopendir(dfd);
+                d = take_fdopendir(&dfd);
                 if (!d) {
                         r = -errno;
-                        safe_close(dfd);
                         goto fail;
                 }
 
@@ -2303,7 +2327,7 @@ _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void **
 
                 compression = o->object.flags & OBJECT_COMPRESSION_MASK;
                 if (compression) {
-#if HAVE_XZ || HAVE_LZ4
+#if HAVE_COMPRESSION
                         r = decompress_startswith(compression,
                                                   o->data.payload, l,
                                                   &f->compress_buffer, &f->compress_buffer_size,
@@ -2358,7 +2382,10 @@ static int return_data(sd_journal *j, JournalFile *f, Object *o, const void **da
         uint64_t l;
         int compression;
 
-        l = le64toh(o->object.size) - offsetof(Object, data.payload);
+        l = le64toh(READ_NOW(o->object.size));
+        if (l < offsetof(Object, data.payload))
+                return -EBADMSG;
+        l -= offsetof(Object, data.payload);
         t = (size_t) l;
 
         /* We can't read objects larger than 4G on a 32bit machine */
@@ -2367,7 +2394,7 @@ static int return_data(sd_journal *j, JournalFile *f, Object *o, const void **da
 
         compression = o->object.flags & OBJECT_COMPRESSION_MASK;
         if (compression) {
-#if HAVE_XZ || HAVE_LZ4
+#if HAVE_COMPRESSION
                 size_t rsize;
                 int r;
 
@@ -2435,6 +2462,19 @@ _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t
         return 1;
 }
 
+_public_ int sd_journal_enumerate_available_data(sd_journal *j, const void **data, size_t *size) {
+        for (;;) {
+                int r;
+
+                r = sd_journal_enumerate_data(j, data, size);
+                if (r >= 0)
+                        return r;
+                if (!JOURNAL_ERRNO_IS_UNAVAILABLE_FIELD(r))
+                        return r;
+                j->current_field++; /* Try with the next field */
+        }
+}
+
 _public_ void sd_journal_restart_data(sd_journal *j) {
         if (!j)
                 return;
@@ -2975,6 +3015,20 @@ _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_
         }
 }
 
+_public_ int sd_journal_enumerate_available_unique(sd_journal *j, const void **data, size_t *size) {
+        for (;;) {
+                int r;
+
+                r = sd_journal_enumerate_unique(j, data, size);
+                if (r >= 0)
+                        return r;
+                if (!JOURNAL_ERRNO_IS_UNAVAILABLE_FIELD(r))
+                        return r;
+                /* Try with the next field. sd_journal_enumerate_unique() modifies state, so on the next try
+                 * we will access the next field. */
+        }
+}
+
 _public_ void sd_journal_restart_unique(sd_journal *j) {
         if (!j)
                 return;
index ba14d922e26493e1f9506a1fc76fc7b80ef78298..158847f3eae642b799e2ae9d8d8d5536784efb4e 100644 (file)
@@ -116,9 +116,8 @@ static void test_catalog_import_merge(void) {
         h = test_import(input, -1, 0);
         assert_se(ordered_hashmap_size(h) == 1);
 
-        ORDERED_HASHMAP_FOREACH(payload, h, j) {
+        ORDERED_HASHMAP_FOREACH(payload, h, j)
                 assert_se(streq(combined, payload));
-        }
 }
 
 static void test_catalog_import_merge_no_body(void) {
@@ -149,9 +148,8 @@ static void test_catalog_import_merge_no_body(void) {
         h = test_import(input, -1, 0);
         assert_se(ordered_hashmap_size(h) == 1);
 
-        ORDERED_HASHMAP_FOREACH(payload, h, j) {
+        ORDERED_HASHMAP_FOREACH(payload, h, j)
                 assert_se(streq(combined, payload));
-        }
 }
 
 static void test_catalog_update(const char *database) {
index 100599705859a3560b54f831b4dce2a1a38a2fff..35823a8da5e43e715e9956cba9dc5a3a82a271d0 100644 (file)
@@ -17,7 +17,7 @@ typedef int (compress_t)(const void *src, uint64_t src_size, void *dst,
 typedef int (decompress_t)(const void *src, uint64_t src_size,
                            void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max);
 
-#if HAVE_XZ || HAVE_LZ4
+#if HAVE_COMPRESSION
 
 static usec_t arg_duration;
 static size_t arg_start;
@@ -143,7 +143,7 @@ static void test_compress_decompress(const char* label, const char* type,
 #endif
 
 int main(int argc, char *argv[]) {
-#if HAVE_XZ || HAVE_LZ4
+#if HAVE_COMPRESSION
         test_setup_logging(LOG_INFO);
 
         if (argc >= 2) {
@@ -167,6 +167,9 @@ int main(int argc, char *argv[]) {
 #endif
 #if HAVE_LZ4
                 test_compress_decompress("LZ4", i, compress_blob_lz4, decompress_blob_lz4);
+#endif
+#if HAVE_ZSTD
+                test_compress_decompress("ZSTD", i, compress_blob_zstd, decompress_blob_zstd);
 #endif
         }
         return 0;
index fac2b43c4731ab45d105642efc27ec5b79bc7193..f50fb0acea82dc635bb5759137e9011a7e2efaeb 100644 (file)
@@ -29,6 +29,8 @@
 # define LZ4_OK -EPROTONOSUPPORT
 #endif
 
+#define HUGE_SIZE (4096*1024)
+
 typedef int (compress_blob_t)(const void *src, uint64_t src_size,
                               void *dst, size_t dst_alloc_size, size_t *dst_size);
 typedef int (decompress_blob_t)(const void *src, uint64_t src_size,
@@ -42,20 +44,20 @@ typedef int (decompress_sw_t)(const void *src, uint64_t src_size,
 typedef int (compress_stream_t)(int fdf, int fdt, uint64_t max_bytes);
 typedef int (decompress_stream_t)(int fdf, int fdt, uint64_t max_size);
 
-#if HAVE_XZ || HAVE_LZ4
-static void test_compress_decompress(int compression,
-                                     compress_blob_t compress,
-                                     decompress_blob_t decompress,
-                                     const char *data,
-                                     size_t data_len,
-                                     bool may_fail) {
+#if HAVE_COMPRESSION
+_unused_ static void test_compress_decompress(const char *compression,
+                                              compress_blob_t compress,
+                                              decompress_blob_t decompress,
+                                              const char *data,
+                                              size_t data_len,
+                                              bool may_fail) {
         char compressed[512];
         size_t csize, usize = 0;
         _cleanup_free_ char *decompressed = NULL;
         int r;
 
         log_info("/* testing %s %s blob compression/decompression */",
-                 object_compressed_to_string(compression), data);
+                 compression, data);
 
         r = compress(data, data_len, compressed, sizeof(compressed), &csize);
         if (r == -ENOBUFS) {
@@ -86,12 +88,12 @@ static void test_compress_decompress(int compression,
         memzero(decompressed, usize);
 }
 
-static void test_decompress_startswith(int compression,
-                                       compress_blob_t compress,
-                                       decompress_sw_t decompress_sw,
-                                       const char *data,
-                                       size_t data_len,
-                                       bool may_fail) {
+_unused_ static void test_decompress_startswith(const char *compression,
+                                                compress_blob_t compress,
+                                                decompress_sw_t decompress_sw,
+                                                const char *data,
+                                                size_t data_len,
+                                                bool may_fail) {
 
         char *compressed;
         _cleanup_free_ char *compressed1 = NULL, *compressed2 = NULL, *decompressed = NULL;
@@ -99,7 +101,7 @@ static void test_decompress_startswith(int compression,
         int r;
 
         log_info("/* testing decompress_startswith with %s on %.20s text */",
-                 object_compressed_to_string(compression), data);
+                 compression, data);
 
 #define BUFSIZE_1 512
 #define BUFSIZE_2 20000
@@ -134,9 +136,9 @@ static void test_decompress_startswith(int compression,
         assert_se(r > 0);
 }
 
-static void test_decompress_startswith_short(int compression,
-                                             compress_blob_t compress,
-                                             decompress_sw_t decompress_sw) {
+_unused_ static void test_decompress_startswith_short(const char *compression,
+                                                      compress_blob_t compress,
+                                                      decompress_sw_t decompress_sw) {
 
 #define TEXT "HUGE=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
 
@@ -144,7 +146,7 @@ static void test_decompress_startswith_short(int compression,
         size_t i, csize;
         int r;
 
-        log_info("/* %s with %s */", __func__, object_compressed_to_string(compression));
+        log_info("/* %s with %s */", __func__, compression);
 
         r = compress(TEXT, sizeof TEXT, buf, sizeof buf, &csize);
         assert_se(r == 0);
@@ -160,11 +162,11 @@ static void test_decompress_startswith_short(int compression,
         }
 }
 
-static void test_compress_stream(int compression,
-                                 const char* cat,
-                                 compress_stream_t compress,
-                                 decompress_stream_t decompress,
-                                 const char *srcfile) {
+_unused_ static void test_compress_stream(const char *compression,
+                                          const char *cat,
+                                          compress_stream_t compress,
+                                          decompress_stream_t decompress,
+                                          const char *srcfile) {
 
         _cleanup_close_ int src = -1, dst = -1, dst2 = -1;
         _cleanup_(unlink_tempfilep) char
@@ -180,8 +182,7 @@ static void test_compress_stream(int compression,
                 return;
         }
 
-        log_debug("/* testing %s compression */",
-                  object_compressed_to_string(compression));
+        log_debug("/* testing %s compression */", compression);
 
         log_debug("/* create source from %s */", srcfile);
 
@@ -231,10 +232,12 @@ static void test_lz4_decompress_partial(void) {
         int r;
         _cleanup_free_ char *huge = NULL;
 
-#define HUGE_SIZE (4096*1024)
+        log_debug("/* %s */", __func__);
+
         assert_se(huge = malloc(HUGE_SIZE));
-        memset(huge, 'x', HUGE_SIZE);
-        memcpy(huge, "HUGE=", 5);
+        memcpy(huge, "HUGE=", STRLEN("HUGE="));
+        memset(&huge[STRLEN("HUGE=")], 'x', HUGE_SIZE - STRLEN("HUGE=") - 1);
+        huge[HUGE_SIZE - 1] = '\0';
 
         r = LZ4_compress_default(huge, buf, HUGE_SIZE, buf_size);
         assert_se(r >= 0);
@@ -264,8 +267,8 @@ static void test_lz4_decompress_partial(void) {
 #endif
 
 int main(int argc, char *argv[]) {
-#if HAVE_XZ || HAVE_LZ4
-        const char text[] =
+#if HAVE_COMPRESSION
+        _unused_ const char text[] =
                 "text\0foofoofoofoo AAAA aaaaaaaaa ghost busters barbarbar FFF"
                 "foofoofoofoo AAAA aaaaaaaaa ghost busters barbarbar FFF";
 
@@ -274,70 +277,96 @@ int main(int argc, char *argv[]) {
 
         char data[512] = "random\0";
 
-        char huge[4096*1024];
-        memset(huge, 'x', sizeof(huge));
-        memcpy(huge, "HUGE=", 5);
-        char_array_0(huge);
+        _cleanup_free_ char *huge = NULL;
+
+        assert_se(huge = malloc(HUGE_SIZE));
+        memcpy(huge, "HUGE=", STRLEN("HUGE="));
+        memset(&huge[STRLEN("HUGE=")], 'x', HUGE_SIZE - STRLEN("HUGE=") - 1);
+        huge[HUGE_SIZE - 1] = '\0';
 
         test_setup_logging(LOG_DEBUG);
 
         random_bytes(data + 7, sizeof(data) - 7);
 
 #if HAVE_XZ
-        test_compress_decompress(OBJECT_COMPRESSED_XZ, compress_blob_xz, decompress_blob_xz,
+        test_compress_decompress("XZ", compress_blob_xz, decompress_blob_xz,
                                  text, sizeof(text), false);
-        test_compress_decompress(OBJECT_COMPRESSED_XZ, compress_blob_xz, decompress_blob_xz,
+        test_compress_decompress("XZ", compress_blob_xz, decompress_blob_xz,
                                  data, sizeof(data), true);
 
-        test_decompress_startswith(OBJECT_COMPRESSED_XZ,
+        test_decompress_startswith("XZ",
                                    compress_blob_xz, decompress_startswith_xz,
                                    text, sizeof(text), false);
-        test_decompress_startswith(OBJECT_COMPRESSED_XZ,
+        test_decompress_startswith("XZ",
                                    compress_blob_xz, decompress_startswith_xz,
                                    data, sizeof(data), true);
-        test_decompress_startswith(OBJECT_COMPRESSED_XZ,
+        test_decompress_startswith("XZ",
                                    compress_blob_xz, decompress_startswith_xz,
-                                   huge, sizeof(huge), true);
+                                   huge, HUGE_SIZE, true);
 
-        test_compress_stream(OBJECT_COMPRESSED_XZ, "xzcat",
+        test_compress_stream("XZ", "xzcat",
                              compress_stream_xz, decompress_stream_xz, srcfile);
 
-        test_decompress_startswith_short(OBJECT_COMPRESSED_XZ, compress_blob_xz, decompress_startswith_xz);
+        test_decompress_startswith_short("XZ", compress_blob_xz, decompress_startswith_xz);
 
 #else
         log_info("/* XZ test skipped */");
 #endif
 
 #if HAVE_LZ4
-        test_compress_decompress(OBJECT_COMPRESSED_LZ4, compress_blob_lz4, decompress_blob_lz4,
+        test_compress_decompress("LZ4", compress_blob_lz4, decompress_blob_lz4,
                                  text, sizeof(text), false);
-        test_compress_decompress(OBJECT_COMPRESSED_LZ4, compress_blob_lz4, decompress_blob_lz4,
+        test_compress_decompress("LZ4", compress_blob_lz4, decompress_blob_lz4,
                                  data, sizeof(data), true);
 
-        test_decompress_startswith(OBJECT_COMPRESSED_LZ4,
+        test_decompress_startswith("LZ4",
                                    compress_blob_lz4, decompress_startswith_lz4,
                                    text, sizeof(text), false);
-        test_decompress_startswith(OBJECT_COMPRESSED_LZ4,
+        test_decompress_startswith("LZ4",
                                    compress_blob_lz4, decompress_startswith_lz4,
                                    data, sizeof(data), true);
-        test_decompress_startswith(OBJECT_COMPRESSED_LZ4,
+        test_decompress_startswith("LZ4",
                                    compress_blob_lz4, decompress_startswith_lz4,
-                                   huge, sizeof(huge), true);
+                                   huge, HUGE_SIZE, true);
 
-        test_compress_stream(OBJECT_COMPRESSED_LZ4, "lz4cat",
+        test_compress_stream("LZ4", "lz4cat",
                              compress_stream_lz4, decompress_stream_lz4, srcfile);
 
         test_lz4_decompress_partial();
 
-        test_decompress_startswith_short(OBJECT_COMPRESSED_LZ4, compress_blob_lz4, decompress_startswith_lz4);
+        test_decompress_startswith_short("LZ4", compress_blob_lz4, decompress_startswith_lz4);
 
 #else
         log_info("/* LZ4 test skipped */");
 #endif
 
+#if HAVE_ZSTD
+        test_compress_decompress("ZSTD", compress_blob_zstd, decompress_blob_zstd,
+                                 text, sizeof(text), false);
+        test_compress_decompress("ZSTD", compress_blob_zstd, decompress_blob_zstd,
+                                 data, sizeof(data), true);
+
+        test_decompress_startswith("ZSTD",
+                                   compress_blob_zstd, decompress_startswith_zstd,
+                                   text, sizeof(text), false);
+        test_decompress_startswith("ZSTD",
+                                   compress_blob_zstd, decompress_startswith_zstd,
+                                   data, sizeof(data), true);
+        test_decompress_startswith("ZSTD",
+                                   compress_blob_zstd, decompress_startswith_zstd,
+                                   huge, HUGE_SIZE, true);
+
+        test_compress_stream("ZSTD", "zstdcat",
+                             compress_stream_zstd, decompress_stream_zstd, srcfile);
+
+        test_decompress_startswith_short("ZSTD", compress_blob_zstd, decompress_startswith_zstd);
+#else
+        log_info("/* ZSTD test skipped */");
+#endif
+
         return 0;
 #else
-        log_info("/* XZ and LZ4 tests skipped */");
+        log_info("/* XZ, LZ4 and ZSTD tests skipped */");
         return EXIT_TEST_SKIP;
 #endif
 }
index 484308e56ebe6568f5ef549b9dfeccf42a0e7714..4265735f0f167b7c616e4037ed1141bd61721854 100644 (file)
@@ -5,11 +5,23 @@
 #include <unistd.h>
 
 #include "sd-journal.h"
-
+#include "fileio.h"
 #include "macro.h"
+#include "memory-util.h"
+
+static void test_journal_print(void) {
+        assert_se(sd_journal_print(LOG_INFO, "XXX") == 0);
+        assert_se(sd_journal_print(LOG_INFO, "%s", "YYY") == 0);
+        assert_se(sd_journal_print(LOG_INFO, "X%4094sY", "ZZZ") == 0);
+        assert_se(sd_journal_print(LOG_INFO, "X%*sY", LONG_LINE_MAX - 8 - 3, "ZZZ") == 0);
+        assert_se(sd_journal_print(LOG_INFO, "X%*sY", LONG_LINE_MAX - 8 - 2, "ZZZ") == -ENOBUFS);
+}
 
-int main(int argc, char *argv[]) {
-        char huge[4096*1024];
+static void test_journal_send(void) {
+        _cleanup_free_ char *huge = NULL;
+
+#define HUGE_SIZE (4096*1024)
+        assert_se(huge = malloc(HUGE_SIZE));
 
         /* utf-8 and non-utf-8, message-less and message-ful iovecs */
         struct iovec graph1[] = {
@@ -36,9 +48,9 @@ int main(int argc, char *argv[]) {
 
         assert_se(sd_journal_perror("") == 0);
 
-        memset(huge, 'x', sizeof(huge));
-        memcpy(huge, "HUGE=", 5);
-        char_array_0(huge);
+        memcpy(huge, "HUGE=", STRLEN("HUGE="));
+        memset(&huge[STRLEN("HUGE=")], 'x', HUGE_SIZE - STRLEN("HUGE=") - 1);
+        huge[HUGE_SIZE - 1] = '\0';
 
         assert_se(sd_journal_send("MESSAGE=Huge field attached",
                                   huge,
@@ -78,7 +90,13 @@ int main(int argc, char *argv[]) {
         assert_se(sd_journal_sendv(graph2, 1) == 0);
         assert_se(sd_journal_sendv(message1, 1) == 0);
         assert_se(sd_journal_sendv(message2, 1) == 0);
+}
+
+int main(int argc, char *argv[]) {
+        test_journal_print();
+        test_journal_send();
 
+        /* Sleep a bit to make it easy for journald to collect metadata. */
         sleep(1);
 
         return 0;
index 6d97bc5ce8aa6f656c434efdc903d8a4f7bf14ff..50aab11c6a8b61182f4a3914477e7b7909aa79bb 100644 (file)
@@ -58,7 +58,7 @@ static void verify_contents(sd_journal *j, unsigned skip) {
                 assert_se(i == N_ENTRIES);
 }
 
-int main(int argc, char *argv[]) {
+static void run_test(void) {
         JournalFile *one, *two, *three;
         char t[] = "/var/tmp/journal-stream-XXXXXX";
         unsigned i;
@@ -68,12 +68,6 @@ int main(int argc, char *argv[]) {
         size_t l;
         dual_timestamp previous_ts = DUAL_TIMESTAMP_NULL;
 
-        /* journal_file_open requires a valid machine id */
-        if (access("/etc/machine-id", F_OK) != 0)
-                return log_tests_skipped("/etc/machine-id not found");
-
-        test_setup_logging(LOG_DEBUG);
-
         assert_se(mkdtemp(t));
         assert_se(chdir(t) >= 0);
         (void) chattr_path(t, FS_NOCOW_FL, FS_NOCOW_FL, NULL);
@@ -177,6 +171,22 @@ int main(int argc, char *argv[]) {
                 printf("%.*s\n", (int) l, (const char*) data);
 
         assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
+}
+
+int main(int argc, char *argv[]) {
+
+        /* journal_file_open requires a valid machine id */
+        if (access("/etc/machine-id", F_OK) != 0)
+                return log_tests_skipped("/etc/machine-id not found");
+
+        test_setup_logging(LOG_DEBUG);
+
+        /* Run this test twice. Once with old hashing and once with new hashing */
+        assert_se(setenv("SYSTEMD_JOURNAL_KEYED_HASH", "1", 1) >= 0);
+        run_test();
+
+        assert_se(setenv("SYSTEMD_JOURNAL_KEYED_HASH", "0", 1) >= 0);
+        run_test();
 
         return 0;
 }
index 7f56668af9541a5badf8bc937efaaf14631e9480..5850bb8eaa347b4a459df798f6214c5b4fe9294b 100644 (file)
@@ -157,7 +157,7 @@ static void test_empty(void) {
         (void) journal_file_close(f4);
 }
 
-#if HAVE_XZ || HAVE_LZ4
+#if HAVE_COMPRESSION
 static bool check_compressed(uint64_t compress_threshold, uint64_t data_size) {
         dual_timestamp ts;
         JournalFile *f;
@@ -251,7 +251,7 @@ int main(int argc, char *argv[]) {
 
         test_non_empty();
         test_empty();
-#if HAVE_XZ || HAVE_LZ4
+#if HAVE_COMPRESSION
         test_min_compress_size();
 #endif
 
index 261c3aaae450199a3d238af96b4bae9f2c1473ca..9ae342dfba87b3d5c1798e5d7edc29ed82693499 100644 (file)
@@ -1,14 +1,18 @@
 # SPDX-License-Identifier: LGPL-2.1+
 
-install_data('kernel-install',
-             install_mode : 'rwxr-xr-x',
-             install_dir : bindir)
-
-install_data('00-entry-directory.install',
-             '50-depmod.install',
-             '90-loaderentry.install',
-             install_mode : 'rwxr-xr-x',
-             install_dir : kernelinstalldir)
-
-meson.add_install_script('sh', '-c',
-                         mkdir_p.format(join_paths(sysconfdir, 'kernel/install.d')))
+want_kernel_install = get_option('kernel-install')
+
+if want_kernel_install
+       install_data('kernel-install',
+               install_mode : 'rwxr-xr-x',
+               install_dir : bindir)
+
+       install_data('00-entry-directory.install',
+               '50-depmod.install',
+               '90-loaderentry.install',
+               install_mode : 'rwxr-xr-x',
+               install_dir : kernelinstalldir)
+
+       meson.add_install_script('sh', '-c',
+                               mkdir_p.format(join_paths(sysconfdir, 'kernel/install.d')))
+endif
index c01c1cf6d6f50b5177ccfe65b175896075e8c020..d0610a32e23d5defcca83889ea716bfb2a4e491e 100644 (file)
@@ -15,7 +15,6 @@
 #include "udev-util.h"
 #include "virt.h"
 
-#define SYSTEMD_PEN    43793
 #define HASH_KEY       SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
 #define APPLICATION_ID SD_ID128_MAKE(a5,0a,d1,12,bf,60,45,77,a2,fb,74,1a,b1,95,5b,03)
 #define USEC_2000       ((usec_t) 946684800000000) /* 2000-01-01 00:00:00 UTC */
index b3115125d9ab1ae59043388e9355ab32763afc54..76abd6583e3a3905ed2cd3349a6040f56e96d5b2 100644 (file)
@@ -8,6 +8,8 @@
 #include "time-util.h"
 #include "unaligned.h"
 
+#define SYSTEMD_PEN    43793
+
 typedef enum DUIDType {
         DUID_TYPE_LLT       = 1,
         DUID_TYPE_EN        = 2,
index 6a803d7b05d5411ee94867815c50e80b596fe682..7e8149487a8d03cd71ae7072a669655711f752f9 100644 (file)
@@ -7,7 +7,6 @@
 
 #include <linux/if_packet.h>
 #include <net/ethernet.h>
-#include <net/if_arp.h>
 #include <stdint.h>
 
 #include "sd-dhcp-client.h"
@@ -23,6 +22,11 @@ typedef struct sd_dhcp_option {
         size_t length;
 } sd_dhcp_option;
 
+typedef struct DHCPServerData {
+        struct in_addr *addr;
+        size_t size;
+} DHCPServerData;
+
 extern const struct hash_ops dhcp_option_hash_ops;
 
 int dhcp_network_bind_raw_socket(int ifindex, union sockaddr_union *link,
index a2d0f8bd5e7b16f1e6befc1dc717367827bcf581..66222eaddbbe1c0564029a6ac36194b5644df788 100644 (file)
@@ -5,11 +5,9 @@
   Copyright © 2013 Intel Corporation. All rights reserved.
 ***/
 
-#include <stdint.h>
-#include <linux/if_packet.h>
-
 #include "sd-dhcp-client.h"
 
+#include "dhcp-internal.h"
 #include "dhcp-protocol.h"
 #include "list.h"
 #include "util.h"
@@ -52,14 +50,7 @@ struct sd_dhcp_lease {
         struct in_addr *router;
         size_t router_size;
 
-        struct in_addr *dns;
-        size_t dns_size;
-
-        struct in_addr *ntp;
-        size_t ntp_size;
-
-        struct in_addr *sip;
-        size_t sip_size;
+        DHCPServerData servers[_SD_DHCP_LEASE_SERVER_TYPE_MAX];
 
         struct sd_dhcp_route *static_route;
         size_t static_route_size, static_route_allocated;
@@ -91,6 +82,3 @@ int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, const vo
 int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease);
 
 int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const void *client_id, size_t client_id_len);
-
-int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file);
-int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file);
index 42c3ceb8b158ea73d7ba49474ddae17a82ea43f7..64c18ed570750ac96f5fb55e211296c3b25384fa 100644 (file)
@@ -19,6 +19,7 @@ typedef enum DHCPRawOption {
         DHCP_RAW_OPTION_DATA_UINT32,
         DHCP_RAW_OPTION_DATA_STRING,
         DHCP_RAW_OPTION_DATA_IPV4ADDRESS,
+        DHCP_RAW_OPTION_DATA_IPV6ADDRESS,
         _DHCP_RAW_OPTION_DATA_MAX,
         _DHCP_RAW_OPTION_DATA_INVALID,
 } DHCPRawOption;
@@ -55,10 +56,10 @@ struct sd_dhcp_server {
 
         char *timezone;
 
-        struct in_addr *ntp, *dns, *sip;
-        unsigned n_ntp, n_dns, n_sip;
+        DHCPServerData servers[_SD_DHCP_LEASE_SERVER_TYPE_MAX];
 
-        OrderedHashmap *raw_option;
+        OrderedHashmap *extra_options;
+        OrderedHashmap *vendor_options;
 
         bool emit_router;
 
@@ -67,6 +68,9 @@ struct sd_dhcp_server {
         DHCPLease invalid_lease;
 
         uint32_t max_lease_time, default_lease_time;
+
+        sd_dhcp_server_callback_t callback;
+        void *callback_userdata;
 };
 
 typedef struct DHCPRequest {
index 517e357d3df22b6c251d0dd1fc38a79588d2fb0f..baf7bb2ef4ad79714fd01f624fdf486b1dc40f1b 100644 (file)
 #include "sd-event.h"
 
 #include "list.h"
+#include "hashmap.h"
 #include "macro.h"
 #include "sparse-endian.h"
 
+typedef struct sd_dhcp6_option {
+        unsigned n_ref;
+
+        uint32_t enterprise_identifier;
+        uint16_t option;
+        void *data;
+        size_t length;
+} sd_dhcp6_option;
+
+extern const struct hash_ops dhcp6_option_hash_ops;
+
 /* Common option header */
 typedef struct DHCP6Option {
         be16_t code;
@@ -87,17 +99,20 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
 int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia);
 int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd, DHCP6Address *hint_pd_prefix);
 int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn);
+int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char **user_class);
+int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char **user_class);
+int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHashmap *vendor_options);
 int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
                        size_t *optlen, uint8_t **optvalue);
 int dhcp6_option_parse_status(DHCP6Option *option, size_t len);
-int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia);
+int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_status_code);
 int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen,
                                 struct in6_addr **addrs, size_t count,
                                 size_t *allocated);
 int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen,
                                   char ***str_arr);
 
-int dhcp6_network_bind_udp_socket(int index, struct in6_addr *address);
+int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *address);
 int dhcp6_network_send_udp_socket(int s, struct in6_addr *address,
                                   const void *packet, size_t len);
 
index f82afe6a0915258fb7fec47dc720694ee541c43d..e2efa8bbe347b12da0400688f1ec64ed284e3244 100644 (file)
 #include "fd-util.h"
 #include "socket-util.h"
 
-int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
+int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *local_address) {
         union sockaddr_union src = {
                 .in6.sin6_family = AF_INET6,
                 .in6.sin6_port = htobe16(DHCP6_PORT_CLIENT),
-                .in6.sin6_scope_id = index,
+                .in6.sin6_scope_id = ifindex,
         };
         _cleanup_close_ int s = -1;
         int r;
 
-        assert(index > 0);
+        assert(ifindex > 0);
         assert(local_address);
 
         src.in6.sin6_addr = *local_address;
index 9f5352a60d7c3f4d6e55b3fcd16ce5f685d15530..fa43587686b224d6a41ffda415380ed7fa377892 100644 (file)
@@ -9,6 +9,7 @@
 #include "sd-dhcp6-client.h"
 
 #include "alloc-util.h"
+#include "dhcp-identifier.h"
 #include "dhcp6-internal.h"
 #include "dhcp6-lease-internal.h"
 #include "dhcp6-protocol.h"
@@ -78,6 +79,39 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
         return 0;
 }
 
+int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHashmap *vendor_options) {
+        sd_dhcp6_option *options;
+        Iterator i;
+        int r;
+
+        assert(buf);
+        assert(*buf);
+        assert(buflen);
+        assert(vendor_options);
+
+        ORDERED_HASHMAP_FOREACH(options, vendor_options, i) {
+                _cleanup_free_ uint8_t *p = NULL;
+                size_t total;
+
+                total = 4 + 2 + 2 + options->length;
+
+                p = malloc(total);
+                if (!p)
+                        return -ENOMEM;
+
+                unaligned_write_be32(p, options->enterprise_identifier);
+                unaligned_write_be16(p + 4, options->option);
+                unaligned_write_be16(p + 6, options->length);
+                memcpy(p + 8, options->data, options->length);
+
+                r = dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_VENDOR_OPTS, total, p);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
 int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) {
         uint16_t len;
         uint8_t *ia_hdr;
@@ -167,6 +201,75 @@ int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) {
         return r;
 }
 
+int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char **user_class) {
+        _cleanup_free_ uint8_t *p = NULL;
+        size_t total = 0, offset = 0;
+        char **s;
+
+        assert_return(buf && *buf && buflen && user_class, -EINVAL);
+
+        STRV_FOREACH(s, user_class) {
+                size_t len = strlen(*s);
+                uint8_t *q;
+
+                if (len > 0xffff)
+                        return -ENAMETOOLONG;
+                q = realloc(p, total + len + 2);
+                if (!q)
+                        return -ENOMEM;
+
+                p = q;
+
+                unaligned_write_be16(&p[offset], len);
+                memcpy(&p[offset + 2], *s, len);
+
+                offset += 2 + len;
+                total += 2 + len;
+        }
+
+        return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_USER_CLASS, total, p);
+}
+
+int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char **vendor_class) {
+        _cleanup_free_ uint8_t *p = NULL;
+        uint32_t enterprise_identifier;
+        size_t total, offset;
+        char **s;
+
+        assert(buf);
+        assert(*buf);
+        assert(buflen);
+        assert(vendor_class);
+
+        enterprise_identifier = htobe32(SYSTEMD_PEN);
+
+        p = memdup(&enterprise_identifier, sizeof(enterprise_identifier));
+        if (!p)
+                return -ENOMEM;
+
+        total = sizeof(enterprise_identifier);
+        offset = total;
+
+        STRV_FOREACH(s, vendor_class) {
+                size_t len = strlen(*s);
+                uint8_t *q;
+
+                q = realloc(p, total + len + 2);
+                if (!q)
+                        return -ENOMEM;
+
+                p = q;
+
+                unaligned_write_be16(&p[offset], len);
+                memcpy(&p[offset + 2], *s, len);
+
+                offset += 2 + len;
+                total += 2 + len;
+        }
+
+        return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_VENDOR_CLASS, total, p);
+}
+
 int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd, DHCP6Address *hint_pd_prefix) {
         DHCP6Option *option = (DHCP6Option *)buf;
         size_t i = sizeof(*option) + sizeof(pd->ia_pd);
@@ -347,13 +450,13 @@ static int dhcp6_option_parse_pdprefix(DHCP6Option *option, DHCP6IA *ia,
         return 0;
 }
 
-int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) {
+int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_status_code) {
+        uint32_t lt_t1, lt_t2, lt_valid = 0, lt_min = UINT32_MAX;
         uint16_t iatype, optlen;
-        size_t i, len;
+        size_t iaaddr_offset;
         int r = 0, status;
+        size_t i, len;
         uint16_t opt;
-        size_t iaaddr_offset;
-        uint32_t lt_t1, lt_t2, lt_valid = 0, lt_min = UINT32_MAX;
 
         assert_return(ia, -EINVAL);
         assert_return(!ia->addresses, -EINVAL);
@@ -463,11 +566,15 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) {
                         status = dhcp6_option_parse_status(option, optlen + offsetof(DHCP6Option, data));
                         if (status < 0)
                                 return status;
+
                         if (status > 0) {
-                                log_dhcp6_client(client, "IA status %d",
-                                                 status);
+                                if (ret_status_code)
+                                        *ret_status_code = status;
 
-                                return -EINVAL;
+                                log_dhcp6_client(client, "IA status %s",
+                                                 dhcp6_message_status_to_string(status));
+
+                                return 0;
                         }
 
                         break;
@@ -511,7 +618,10 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) {
                 break;
         }
 
-        return 0;
+        if (ret_status_code)
+                *ret_status_code = 0;
+
+        return 1;
 }
 
 int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen,
@@ -597,3 +707,44 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char *
 
         return idx;
 }
+
+static sd_dhcp6_option* dhcp6_option_free(sd_dhcp6_option *i) {
+        if (!i)
+                return NULL;
+
+        free(i->data);
+        return mfree(i);
+}
+
+int sd_dhcp6_option_new(uint16_t option, const void *data, size_t length, uint32_t enterprise_identifier, sd_dhcp6_option **ret) {
+        assert_return(ret, -EINVAL);
+        assert_return(length == 0 || data, -EINVAL);
+
+        _cleanup_free_ void *q = memdup(data, length);
+        if (!q)
+                return -ENOMEM;
+
+        sd_dhcp6_option *p = new(sd_dhcp6_option, 1);
+        if (!p)
+                return -ENOMEM;
+
+        *p = (sd_dhcp6_option) {
+                .n_ref = 1,
+                .option = option,
+                .enterprise_identifier = enterprise_identifier,
+                .length = length,
+                .data = TAKE_PTR(q),
+        };
+
+        *ret = p;
+        return 0;
+}
+
+DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_option, sd_dhcp6_option, dhcp6_option_free);
+DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+                dhcp6_option_hash_ops,
+                void,
+                trivial_hash_func,
+                trivial_compare_func,
+                sd_dhcp6_option,
+                sd_dhcp6_option_unref);
index ffae4453acfd9216b14c2ccb78705acd16d5e0c3..f7a27028608b56577d5ff087f4d62d4006ae02fb 100644 (file)
@@ -82,14 +82,35 @@ enum {
         DHCP6_NTP_SUBOPTION_SRV_FQDN            = 3,
 };
 
+/*
+ * RFC 8415, RFC 5007 and RFC 7653 status codes:
+ * https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#dhcpv6-parameters-5
+ */
 enum {
-        DHCP6_STATUS_SUCCESS                    = 0,
-        DHCP6_STATUS_UNSPEC_FAIL                = 1,
-        DHCP6_STATUS_NO_ADDRS_AVAIL             = 2,
-        DHCP6_STATUS_NO_BINDING                 = 3,
-        DHCP6_STATUS_NOT_ON_LINK                = 4,
-        DHCP6_STATUS_USE_MULTICAST              = 5,
-        _DHCP6_STATUS_MAX                       = 6,
+        DHCP6_STATUS_SUCCESS                      = 0,
+        DHCP6_STATUS_UNSPEC_FAIL                  = 1,
+        DHCP6_STATUS_NO_ADDRS_AVAIL               = 2,
+        DHCP6_STATUS_NO_BINDING                   = 3,
+        DHCP6_STATUS_NOT_ON_LINK                  = 4,
+        DHCP6_STATUS_USE_MULTICAST                = 5,
+        DHCP6_STATUS_NO_PREFIX_AVAIL              = 6,
+        DHCP6_STATUS_UNKNOWN_QUERY_TYPE           = 7,
+        DHCP6_STATUS_MALFORMED_QUERY              = 8,
+        DHCP6_STATUS_NOT_CONFIGURED               = 9,
+        DHCP6_STATUS_NOT_ALLOWED                  = 10,
+        DHCP6_STATUS_QUERY_TERMINATED             = 11,
+        DHCP6_STATUS_DATA_MISSING                 = 12,
+        DHCP6_STATUS_CATCHUP_COMPLETE             = 13,
+        DHCP6_STATUS_NOT_SUPPORTED                = 14,
+        DHCP6_STATUS_TLS_CONNECTION_REFUSED       = 15,
+        DHCP6_STATUS_ADDRESS_IN_USE               = 16,
+        DHCP6_STATUS_CONFIGURATION_CONFLICT       = 17,
+        DHCP6_STATUS_MISSING_BINDING_INFORMATION  = 18,
+        DHCP6_STATUS_OUTDATED_BINDING_INFORMATION = 19,
+        DHCP6_STATUS_SERVER_SHUTTING_DOWN         = 20,
+        DHCP6_STATUS_DNS_UPDATE_NOT_SUPPORTED     = 21,
+        DHCP6_STATUS_EXCESSIVE_TIME_SKEW          = 22,
+        _DHCP6_STATUS_MAX                         = 23,
 };
 
 enum {
index dbb1e51a0e0d9567d59706d4591d551d34f4cdb6..d9690293f13e16786ea0331d9e0309d15799a154 100644 (file)
@@ -81,11 +81,11 @@ static int icmp6_bind_router_message(const struct icmp6_filter *filter,
         return TAKE_FD(s);
 }
 
-int icmp6_bind_router_solicitation(int index) {
+int icmp6_bind_router_solicitation(int ifindex) {
         struct icmp6_filter filter = {};
         struct ipv6_mreq mreq = {
                 .ipv6mr_multiaddr = IN6ADDR_ALL_NODES_MULTICAST_INIT,
-                .ipv6mr_interface = index,
+                .ipv6mr_interface = ifindex,
         };
 
         ICMP6_FILTER_SETBLOCKALL(&filter);
@@ -94,11 +94,11 @@ int icmp6_bind_router_solicitation(int index) {
         return icmp6_bind_router_message(&filter, &mreq);
 }
 
-int icmp6_bind_router_advertisement(int index) {
+int icmp6_bind_router_advertisement(int ifindex) {
         struct icmp6_filter filter = {};
         struct ipv6_mreq mreq = {
                 .ipv6mr_multiaddr = IN6ADDR_ALL_ROUTERS_MULTICAST_INIT,
-                .ipv6mr_interface = index,
+                .ipv6mr_interface = ifindex,
         };
 
         ICMP6_FILTER_SETBLOCKALL(&filter);
@@ -147,11 +147,9 @@ int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) {
 
 int icmp6_receive(int fd, void *buffer, size_t size, struct in6_addr *dst,
                   triple_timestamp *timestamp) {
-        union {
-                struct cmsghdr cmsghdr;
-                uint8_t buf[CMSG_SPACE(sizeof(int)) + /* ttl */
-                            CMSG_SPACE(sizeof(struct timeval))];
-        } control = {};
+
+        CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int)) + /* ttl */
+                         CMSG_SPACE(sizeof(struct timeval))) control;
         struct iovec iov = {};
         union sockaddr_union sa = {};
         struct msghdr msg = {
@@ -167,9 +165,9 @@ int icmp6_receive(int fd, void *buffer, size_t size, struct in6_addr *dst,
 
         iov = IOVEC_MAKE(buffer, size);
 
-        len = recvmsg(fd, &msg, MSG_DONTWAIT);
+        len = recvmsg_safe(fd, &msg, MSG_DONTWAIT);
         if (len < 0)
-                return -errno;
+                return (int) len;
 
         if ((size_t) len != size)
                 return -EINVAL;
index 725a68086bc462bf8878f7447eacf3b08bb874c8..ac68ded1fe9653d6b8485a4b990c612a3d86bb95 100644 (file)
@@ -17,8 +17,8 @@
         { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } }
 
-int icmp6_bind_router_solicitation(int index);
-int icmp6_bind_router_advertisement(int index);
+int icmp6_bind_router_solicitation(int ifindex);
+int icmp6_bind_router_advertisement(int ifindex);
 int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr);
 int icmp6_receive(int fd, void *buffer, size_t size, struct in6_addr *dst,
                   triple_timestamp *timestamp);
index 1e9fe73034b4be6994d984ecdfbc128fc521b558..02645b2bcd79dc291406d8258de2ecab1500ddb8 100644 (file)
@@ -50,6 +50,7 @@ static void lldp_neighbor_free(sd_lldp_neighbor *n) {
         free(n->port_description);
         free(n->system_name);
         free(n->system_description);
+        free(n->mud_url);
         free(n->chassis_id_as_string);
         free(n->port_id_as_string);
         free(n);
@@ -292,9 +293,20 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) {
 
                         break;
 
-                case SD_LLDP_TYPE_PRIVATE:
+                case SD_LLDP_TYPE_PRIVATE: {
                         if (length < 4)
                                 log_lldp("Found private TLV that is too short, ignoring.");
+                        else {
+                                /* RFC 8520: MUD URL */
+                                if (memcmp(p, SD_LLDP_OUI_MUD, sizeof(SD_LLDP_OUI_MUD)) == 0 &&
+                                    p[sizeof(SD_LLDP_OUI_MUD)] == SD_LLDP_OUI_SUBTYPE_MUD_USAGE_DESCRIPTION) {
+                                        r = parse_string(&n->mud_url, p + sizeof(SD_LLDP_OUI_MUD) + 1,
+                                                         length - 1 - sizeof(SD_LLDP_OUI_MUD));
+                                        if (r < 0)
+                                                return r;
+                                }
+                        }
+                }
 
                         break;
                 }
@@ -593,6 +605,17 @@ _public_ int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const ch
         return 0;
 }
 
+_public_ int sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor *n, const char **ret) {
+        assert_return(n, -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        if (!n->mud_url)
+                return -ENODATA;
+
+        *ret = n->mud_url;
+        return 0;
+}
+
 _public_ int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret) {
         assert_return(n, -EINVAL);
         assert_return(ret, -EINVAL);
index 62dbff42cad1f89d5ce792c2f559236a39c56171..74175edf545cc9aa4530c845e1ac795e64cbd0d0 100644 (file)
@@ -54,6 +54,7 @@ struct sd_lldp_neighbor {
         char *port_description;
         char *system_name;
         char *system_description;
+        char *mud_url;
 
         uint16_t port_vlan_id;
 
index 0f4ba7a35c3eb48e131723b1d0eec5ce45ce811c..459d13ad7dfe299d825d0e57e1d81755f12877a1 100644 (file)
@@ -660,27 +660,27 @@ int config_parse_bridge_port_priority(
 size_t serialize_in_addrs(FILE *f,
                           const struct in_addr *addresses,
                           size_t size,
-                          bool with_leading_space,
+                          bool *with_leading_space,
                           bool (*predicate)(const struct in_addr *addr)) {
-        size_t count;
-        size_t i;
-
         assert(f);
         assert(addresses);
 
-        count = 0;
+        size_t count = 0;
+        bool _space = false;
+        if (!with_leading_space)
+                with_leading_space = &_space;
 
-        for (i = 0; i < size; i++) {
+        for (size_t i = 0; i < size; i++) {
                 char sbuf[INET_ADDRSTRLEN];
 
                 if (predicate && !predicate(&addresses[i]))
                         continue;
-                if (with_leading_space)
+
+                if (*with_leading_space)
                         fputc(' ', f);
-                else
-                        with_leading_space = true;
                 fputs(inet_ntop(AF_INET, &addresses[i], sbuf, sizeof(sbuf)), f);
                 count++;
+                *with_leading_space = true;
         }
 
         return count;
@@ -722,20 +722,22 @@ int deserialize_in_addrs(struct in_addr **ret, const char *string) {
         return size;
 }
 
-void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses, size_t size) {
-        unsigned i;
-
+void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses, size_t size, bool *with_leading_space) {
         assert(f);
         assert(addresses);
         assert(size);
 
-        for (i = 0; i < size; i++) {
-                char buffer[INET6_ADDRSTRLEN];
+        bool _space = false;
+        if (!with_leading_space)
+                with_leading_space = &_space;
 
-                fputs(inet_ntop(AF_INET6, addresses+i, buffer, sizeof(buffer)), f);
+        for (size_t i = 0; i < size; i++) {
+                char buffer[INET6_ADDRSTRLEN];
 
-                if (i < size - 1)
+                if (*with_leading_space)
                         fputc(' ', f);
+                fputs(inet_ntop(AF_INET6, addresses+i, buffer, sizeof(buffer)), f);
+                *with_leading_space = true;
         }
 }
 
@@ -776,8 +778,6 @@ int deserialize_in6_addrs(struct in6_addr **ret, const char *string) {
 }
 
 void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, size_t size) {
-        unsigned i;
-
         assert(f);
         assert(key);
         assert(routes);
@@ -785,7 +785,7 @@ void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, siz
 
         fprintf(f, "%s=", key);
 
-        for (i = 0; i < size; i++) {
+        for (size_t i = 0; i < size; i++) {
                 char sbuf[INET_ADDRSTRLEN];
                 struct in_addr dest, gw;
                 uint8_t length;
@@ -794,8 +794,8 @@ void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, siz
                 assert_se(sd_dhcp_route_get_gateway(routes[i], &gw) >= 0);
                 assert_se(sd_dhcp_route_get_destination_prefix_length(routes[i], &length) >= 0);
 
-                fprintf(f, "%s/%" PRIu8, inet_ntop(AF_INET, &dest, sbuf, sizeof(sbuf)), length);
-                fprintf(f, ",%s%s", inet_ntop(AF_INET, &gw, sbuf, sizeof(sbuf)), (i < (size - 1)) ? " ": "");
+                fprintf(f, "%s/%" PRIu8, inet_ntop(AF_INET, &dest, sbuf, sizeof sbuf), length);
+                fprintf(f, ",%s%s", inet_ntop(AF_INET, &gw, sbuf, sizeof sbuf), i < size - 1 ? " ": "");
         }
 
         fputs("\n", f);
index 40b008ebcdd6aae6cd4d504b6cf4343f9a478cba..e4c11235b62f2f4f85ae189cb20f52f988fc80bb 100644 (file)
@@ -8,7 +8,6 @@
 #include "sd-dhcp-lease.h"
 
 #include "conf-parser.h"
-#include "def.h"
 #include "set.h"
 #include "strv.h"
 
@@ -52,15 +51,17 @@ const char *net_get_name_persistent(sd_device *device);
 size_t serialize_in_addrs(FILE *f,
                           const struct in_addr *addresses,
                           size_t size,
-                          bool with_leading_space,
+                          bool *with_leading_space,
                           bool (*predicate)(const struct in_addr *addr));
 int deserialize_in_addrs(struct in_addr **addresses, const char *string);
 void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses,
-                         size_t size);
+                         size_t size,
+                         bool *with_leading_space);
 int deserialize_in6_addrs(struct in6_addr **addresses, const char *string);
 
 /* don't include "dhcp-lease-internal.h" as it causes conflicts between netinet/ip.h and linux/ip.h */
 struct sd_dhcp_route;
+struct sd_dhcp_lease;
 
 void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, size_t size);
 int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t *ret_allocated, const char *string);
@@ -68,4 +69,5 @@ int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t
 /* It is not necessary to add deserialize_dhcp_option(). Use unhexmem() instead. */
 int serialize_dhcp_option(FILE *f, const char *key, const void *data, size_t size);
 
-#define NETWORK_DIRS ((const char* const*) CONF_PATHS_STRV("systemd/network"))
+int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file);
+int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file);
index b735915398bb54af89ddf22f1b68d8ce857cb4e7..a83ffc34237e46642d7321194efdf0a1bd1bf1dd 100644 (file)
@@ -27,6 +27,8 @@
 #include "random-util.h"
 #include "string-util.h"
 #include "strv.h"
+#include "utf8.h"
+#include "web-util.h"
 
 #define MAX_CLIENT_ID_LEN (sizeof(uint32_t) + MAX_DUID_LEN)  /* Arbitrary limit */
 #define MAX_MAC_ADDR_LEN CONST_MAX(INFINIBAND_ALEN, ETH_ALEN)
 #define RESTART_AFTER_NAK_MIN_USEC (1 * USEC_PER_SEC)
 #define RESTART_AFTER_NAK_MAX_USEC (30 * USEC_PER_MINUTE)
 
+typedef struct sd_dhcp_client_id {
+        uint8_t type;
+        union {
+                struct {
+                        /* 0: Generic (non-LL) (RFC 2132) */
+                        uint8_t data[MAX_CLIENT_ID_LEN];
+                } _packed_ gen;
+                struct {
+                        /* 1: Ethernet Link-Layer (RFC 2132) */
+                        uint8_t haddr[ETH_ALEN];
+                } _packed_ eth;
+                struct {
+                        /* 2 - 254: ARP/Link-Layer (RFC 2132) */
+                        uint8_t haddr[0];
+                } _packed_ ll;
+                struct {
+                        /* 255: Node-specific (RFC 4361) */
+                        be32_t iaid;
+                        struct duid duid;
+                } _packed_ ns;
+                struct {
+                        uint8_t data[MAX_CLIENT_ID_LEN];
+                } _packed_ raw;
+        };
+} _packed_ sd_dhcp_client_id;
+
 struct sd_dhcp_client {
         unsigned n_ref;
 
@@ -55,41 +83,20 @@ struct sd_dhcp_client {
         uint8_t mac_addr[MAX_MAC_ADDR_LEN];
         size_t mac_addr_len;
         uint16_t arp_type;
-        struct {
-                uint8_t type;
-                union {
-                        struct {
-                                /* 0: Generic (non-LL) (RFC 2132) */
-                                uint8_t data[MAX_CLIENT_ID_LEN];
-                        } _packed_ gen;
-                        struct {
-                                /* 1: Ethernet Link-Layer (RFC 2132) */
-                                uint8_t haddr[ETH_ALEN];
-                        } _packed_ eth;
-                        struct {
-                                /* 2 - 254: ARP/Link-Layer (RFC 2132) */
-                                uint8_t haddr[0];
-                        } _packed_ ll;
-                        struct {
-                                /* 255: Node-specific (RFC 4361) */
-                                be32_t iaid;
-                                struct duid duid;
-                        } _packed_ ns;
-                        struct {
-                                uint8_t data[MAX_CLIENT_ID_LEN];
-                        } _packed_ raw;
-                };
-        } _packed_ client_id;
+        sd_dhcp_client_id client_id;
         size_t client_id_len;
         char *hostname;
         char *vendor_class_identifier;
+        char *mudurl;
         char **user_class;
         uint32_t mtu;
+        uint32_t fallback_lease_lifetime;
         uint32_t xid;
         usec_t start_time;
         uint64_t attempt;
         uint64_t max_attempts;
-        OrderedHashmap *options;
+        OrderedHashmap *extra_options;
+        OrderedHashmap *vendor_options;
         usec_t request_sent;
         sd_event_source *timeout_t1;
         sd_event_source *timeout_t2;
@@ -147,6 +154,60 @@ static int client_receive_message_udp(
                 void *userdata);
 static void client_stop(sd_dhcp_client *client, int error);
 
+int sd_dhcp_client_id_to_string(const void *data, size_t len, char **ret) {
+        const sd_dhcp_client_id *client_id = data;
+        _cleanup_free_ char *t = NULL;
+        int r = 0;
+
+        assert_return(data, -EINVAL);
+        assert_return(len >= 1, -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        len -= 1;
+        if (len > MAX_CLIENT_ID_LEN)
+                return -EINVAL;
+
+        switch (client_id->type) {
+        case 0:
+                if (utf8_is_printable((char *) client_id->gen.data, len))
+                        r = asprintf(&t, "%.*s", (int) len, client_id->gen.data);
+                else
+                        r = asprintf(&t, "DATA");
+                break;
+        case 1:
+                if (len != sizeof_field(sd_dhcp_client_id, eth))
+                        return -EINVAL;
+
+                r = asprintf(&t, "%x:%x:%x:%x:%x:%x",
+                             client_id->eth.haddr[0],
+                             client_id->eth.haddr[1],
+                             client_id->eth.haddr[2],
+                             client_id->eth.haddr[3],
+                             client_id->eth.haddr[4],
+                             client_id->eth.haddr[5]);
+                break;
+        case 2 ... 254:
+                r = asprintf(&t, "ARP/LL");
+                break;
+        case 255:
+                if (len < 6)
+                        return -EINVAL;
+
+                uint32_t iaid = be32toh(client_id->ns.iaid);
+                uint16_t duid_type = be16toh(client_id->ns.duid.type);
+                if (dhcp_validate_duid_len(duid_type, len - 6, true) < 0)
+                        return -EINVAL;
+
+                r = asprintf(&t, "IAID:0x%x/DUID", iaid);
+                break;
+        }
+
+        if (r < 0)
+                return -ENOMEM;
+        *ret = TAKE_PTR(t);
+        return 0;
+}
+
 int sd_dhcp_client_set_callback(
                 sd_dhcp_client *client,
                 sd_dhcp_client_callback_t cb,
@@ -315,7 +376,7 @@ int sd_dhcp_client_set_client_id(
         /* For hardware types, log debug message about unexpected data length.
          *
          * Note that infiniband's INFINIBAND_ALEN is 20 bytes long, but only
-         * last last 8 bytes of the address are stable and suitable to put into
+         * the last 8 bytes of the address are stable and suitable to put into
          * the client-id. The caller is advised to account for that. */
         if ((type == ARPHRD_ETHER && data_len != ETH_ALEN) ||
             (type == ARPHRD_INFINIBAND && data_len != 8))
@@ -492,6 +553,18 @@ int sd_dhcp_client_set_vendor_class_identifier(
         return free_and_strdup(&client->vendor_class_identifier, vci);
 }
 
+int sd_dhcp_client_set_mud_url(
+                sd_dhcp_client *client,
+                const char *mudurl) {
+
+        assert_return(client, -EINVAL);
+        assert_return(mudurl, -EINVAL);
+        assert_return(strlen(mudurl) <= 255, -EINVAL);
+        assert_return(http_url_is_valid(mudurl), -EINVAL);
+
+        return free_and_strdup(&client->mudurl, mudurl);
+}
+
 int sd_dhcp_client_set_user_class(
                 sd_dhcp_client *client,
                 const char* const* user_class) {
@@ -540,17 +613,17 @@ int sd_dhcp_client_set_max_attempts(sd_dhcp_client *client, uint64_t max_attempt
         return 0;
 }
 
-int sd_dhcp_client_set_dhcp_option(sd_dhcp_client *client, sd_dhcp_option *v) {
+int sd_dhcp_client_add_option(sd_dhcp_client *client, sd_dhcp_option *v) {
         int r;
 
         assert_return(client, -EINVAL);
         assert_return(v, -EINVAL);
 
-        r = ordered_hashmap_ensure_allocated(&client->options, &dhcp_option_hash_ops);
+        r = ordered_hashmap_ensure_allocated(&client->extra_options, &dhcp_option_hash_ops);
         if (r < 0)
                 return r;
 
-        r = ordered_hashmap_put(client->options, UINT_TO_PTR(v->option), v);
+        r = ordered_hashmap_put(client->extra_options, UINT_TO_PTR(v->option), v);
         if (r < 0)
                 return r;
 
@@ -558,6 +631,25 @@ int sd_dhcp_client_set_dhcp_option(sd_dhcp_client *client, sd_dhcp_option *v) {
         return 0;
 }
 
+int sd_dhcp_client_add_vendor_option(sd_dhcp_client *client, sd_dhcp_option *v) {
+        int r;
+
+        assert_return(client, -EINVAL);
+        assert_return(v, -EINVAL);
+
+        r = ordered_hashmap_ensure_allocated(&client->vendor_options, &dhcp_option_hash_ops);
+        if (r < 0)
+                return -ENOMEM;
+
+        r = ordered_hashmap_put(client->vendor_options, v, v);
+        if (r < 0)
+                return r;
+
+        sd_dhcp_option_ref(v);
+
+        return 1;
+}
+
 int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) {
         assert_return(client, -EINVAL);
 
@@ -578,6 +670,15 @@ int sd_dhcp_client_set_service_type(sd_dhcp_client *client, int type) {
         return 0;
 }
 
+int sd_dhcp_client_set_fallback_lease_lifetime(sd_dhcp_client *client, uint32_t fallback_lease_lifetime) {
+        assert_return(client, -EINVAL);
+        assert_return(fallback_lease_lifetime > 0, -EINVAL);
+
+        client->fallback_lease_lifetime = fallback_lease_lifetime;
+
+        return 0;
+}
+
 static int client_notify(sd_dhcp_client *client, int event) {
         assert(client);
 
@@ -816,36 +917,12 @@ static int dhcp_client_send_raw(
                                             packet, len);
 }
 
-static int client_send_discover(sd_dhcp_client *client) {
-        _cleanup_free_ DHCPPacket *discover = NULL;
-        size_t optoffset, optlen;
+static int client_append_common_discover_request_options(sd_dhcp_client *client, DHCPPacket *packet, size_t *optoffset, size_t optlen) {
         sd_dhcp_option *j;
         Iterator i;
         int r;
 
         assert(client);
-        assert(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_SELECTING));
-
-        r = client_message_init(client, &discover, DHCP_DISCOVER,
-                                &optlen, &optoffset);
-        if (r < 0)
-                return r;
-
-        /* the client may suggest values for the network address
-           and lease time in the DHCPDISCOVER message. The client may include
-           the ’requested IP address’ option to suggest that a particular IP
-           address be assigned, and may include the ’IP address lease time’
-           option to suggest the lease time it would like.
-         */
-        /* RFC7844 section 3:
-           SHOULD NOT contain any other option. */
-        if (!client->anonymize && client->last_addr != INADDR_ANY) {
-                r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
-                                       SD_DHCP_OPTION_REQUESTED_IP_ADDRESS,
-                                       4, &client->last_addr);
-                if (r < 0)
-                        return r;
-        }
 
         if (client->hostname) {
                 /* According to RFC 4702 "clients that send the Client FQDN option in
@@ -856,18 +933,18 @@ static int client_send_discover(sd_dhcp_client *client) {
                         /* it is unclear from RFC 2131 if client should send hostname in
                            DHCPDISCOVER but dhclient does and so we do as well
                         */
-                        r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
+                        r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0,
                                                SD_DHCP_OPTION_HOST_NAME,
                                                strlen(client->hostname), client->hostname);
                 } else
-                        r = client_append_fqdn_option(&discover->dhcp, optlen, &optoffset,
+                        r = client_append_fqdn_option(&packet->dhcp, optlen, optoffset,
                                                       client->hostname);
                 if (r < 0)
                         return r;
         }
 
         if (client->vendor_class_identifier) {
-                r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
+                r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0,
                                        SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER,
                                        strlen(client->vendor_class_identifier),
                                        client->vendor_class_identifier);
@@ -875,8 +952,17 @@ static int client_send_discover(sd_dhcp_client *client) {
                         return r;
         }
 
+        if (client->mudurl) {
+                r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0,
+                                       SD_DHCP_OPTION_MUD_URL,
+                                       strlen(client->mudurl),
+                                       client->mudurl);
+                if (r < 0)
+                        return r;
+        }
+
         if (client->user_class) {
-                r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
+                r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0,
                                        SD_DHCP_OPTION_USER_CLASS,
                                        strv_length(client->user_class),
                                        client->user_class);
@@ -884,13 +970,59 @@ static int client_send_discover(sd_dhcp_client *client) {
                         return r;
         }
 
-        ORDERED_HASHMAP_FOREACH(j, client->options, i) {
-                r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
+        ORDERED_HASHMAP_FOREACH(j, client->extra_options, i) {
+                r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0,
                                        j->option, j->length, j->data);
                 if (r < 0)
                         return r;
         }
 
+        if (!ordered_hashmap_isempty(client->vendor_options)) {
+                r = dhcp_option_append(
+                                &packet->dhcp, optlen, optoffset, 0,
+                                SD_DHCP_OPTION_VENDOR_SPECIFIC,
+                                ordered_hashmap_size(client->vendor_options), client->vendor_options);
+                if (r < 0)
+                        return r;
+        }
+
+
+        return 0;
+}
+
+static int client_send_discover(sd_dhcp_client *client) {
+        _cleanup_free_ DHCPPacket *discover = NULL;
+        size_t optoffset, optlen;
+        int r;
+
+        assert(client);
+        assert(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_SELECTING));
+
+        r = client_message_init(client, &discover, DHCP_DISCOVER,
+                                &optlen, &optoffset);
+        if (r < 0)
+                return r;
+
+        /* the client may suggest values for the network address
+           and lease time in the DHCPDISCOVER message. The client may include
+           the ’requested IP address’ option to suggest that a particular IP
+           address be assigned, and may include the ’IP address lease time’
+           option to suggest the lease time it would like.
+         */
+        /* RFC7844 section 3:
+           SHOULD NOT contain any other option. */
+        if (!client->anonymize && client->last_addr != INADDR_ANY) {
+                r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
+                                       SD_DHCP_OPTION_REQUESTED_IP_ADDRESS,
+                                       4, &client->last_addr);
+                if (r < 0)
+                        return r;
+        }
+
+        r = client_append_common_discover_request_options(client, discover, &optoffset, optlen);
+        if (r < 0)
+                return r;
+
         r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
                                SD_DHCP_OPTION_END, 0, NULL);
         if (r < 0)
@@ -982,26 +1114,9 @@ static int client_send_request(sd_dhcp_client *client) {
                 return -EINVAL;
         }
 
-        if (client->hostname) {
-                if (dns_name_is_single_label(client->hostname))
-                        r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
-                                               SD_DHCP_OPTION_HOST_NAME,
-                                               strlen(client->hostname), client->hostname);
-                else
-                        r = client_append_fqdn_option(&request->dhcp, optlen, &optoffset,
-                                                      client->hostname);
-                if (r < 0)
-                        return r;
-        }
-
-        if (client->vendor_class_identifier) {
-                r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
-                                       SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER,
-                                       strlen(client->vendor_class_identifier),
-                                       client->vendor_class_identifier);
-                if (r < 0)
-                        return r;
-        }
+        r = client_append_common_discover_request_options(client, request, &optoffset, optlen);
+        if (r < 0)
+                return r;
 
         r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
                                SD_DHCP_OPTION_END, 0, NULL);
@@ -1326,7 +1441,10 @@ static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata)
         sd_dhcp_client *client = userdata;
         DHCP_CLIENT_DONT_DESTROY(client);
 
-        client->state = DHCP_STATE_RENEWING;
+        if (client->lease)
+                client->state = DHCP_STATE_RENEWING;
+        else if (client->state != DHCP_STATE_INIT)
+                client->state = DHCP_STATE_INIT_REBOOT;
         client->attempt = 0;
 
         return client_initialize_time_events(client);
@@ -1357,6 +1475,9 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer, size_
         lease->next_server = offer->siaddr;
         lease->address = offer->yiaddr;
 
+        if (lease->lifetime == 0 && client->fallback_lease_lifetime > 0)
+                lease->lifetime = client->fallback_lease_lifetime;
+
         if (lease->address == 0 ||
             lease->server_address == 0 ||
             lease->lifetime == 0) {
@@ -1837,13 +1958,13 @@ static int client_receive_message_raw(
 
         sd_dhcp_client *client = userdata;
         _cleanup_free_ DHCPPacket *packet = NULL;
-        uint8_t cmsgbuf[CMSG_SPACE(sizeof(struct tpacket_auxdata))];
+        CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct tpacket_auxdata))) control;
         struct iovec iov = {};
         struct msghdr msg = {
                 .msg_iov = &iov,
                 .msg_iovlen = 1,
-                .msg_control = cmsgbuf,
-                .msg_controllen = sizeof(cmsgbuf),
+                .msg_control = &control,
+                .msg_controllen = sizeof(control),
         };
         struct cmsghdr *cmsg;
         bool checksum = true;
@@ -1865,25 +1986,21 @@ static int client_receive_message_raw(
 
         iov = IOVEC_MAKE(packet, buflen);
 
-        len = recvmsg(fd, &msg, 0);
-        if (len < 0) {
-                if (IN_SET(errno, EAGAIN, EINTR, ENETDOWN))
-                        return 0;
-
-                return log_dhcp_client_errno(client, errno,
-                                             "Could not receive message from raw socket: %m");
-        } else if ((size_t)len < sizeof(DHCPPacket))
+        len = recvmsg_safe(fd, &msg, 0);
+        if (IN_SET(len, -EAGAIN, -EINTR, -ENETDOWN))
                 return 0;
+        if (len < 0)
+                return log_dhcp_client_errno(client, len,
+                                             "Could not receive message from raw socket: %m");
 
-        CMSG_FOREACH(cmsg, &msg)
-                if (cmsg->cmsg_level == SOL_PACKET &&
-                    cmsg->cmsg_type == PACKET_AUXDATA &&
-                    cmsg->cmsg_len == CMSG_LEN(sizeof(struct tpacket_auxdata))) {
-                        struct tpacket_auxdata *aux = (struct tpacket_auxdata*)CMSG_DATA(cmsg);
+        if ((size_t) len < sizeof(DHCPPacket))
+                return 0;
 
-                        checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY);
-                        break;
-                }
+        cmsg = cmsg_find(&msg, SOL_PACKET, PACKET_AUXDATA, CMSG_LEN(sizeof(struct tpacket_auxdata)));
+        if (cmsg) {
+                struct tpacket_auxdata *aux = (struct tpacket_auxdata*) CMSG_DATA(cmsg);
+                checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY);
+        }
 
         r = dhcp_packet_verify_headers(packet, len, checksum, client->port);
         if (r < 0)
@@ -1898,6 +2015,9 @@ int sd_dhcp_client_send_renew(sd_dhcp_client *client) {
         assert_return(client, -EINVAL);
         assert_return(client->fd >= 0, -EINVAL);
 
+        if (!client->lease)
+                return 0;
+
         client->start_delay = 0;
         client->attempt = 1;
         client->state = DHCP_STATE_RENEWING;
@@ -2072,8 +2192,10 @@ static sd_dhcp_client *dhcp_client_free(sd_dhcp_client *client) {
         free(client->req_opts);
         free(client->hostname);
         free(client->vendor_class_identifier);
+        free(client->mudurl);
         client->user_class = strv_free(client->user_class);
-        ordered_hashmap_free(client->options);
+        ordered_hashmap_free(client->extra_options);
+        ordered_hashmap_free(client->vendor_options);
         return mfree(client);
 }
 
index d072f1c57a0077d54d5532393af0fb10c0c061ff..0bc5fa321018da09e37dbdee413d9b6471506c77 100644 (file)
@@ -96,37 +96,40 @@ int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu) {
         return 0;
 }
 
-int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr) {
+int sd_dhcp_lease_get_servers(
+                sd_dhcp_lease *lease,
+                sd_dhcp_lease_server_type what,
+                const struct in_addr **addr) {
+
         assert_return(lease, -EINVAL);
+        assert_return(what >= 0, -EINVAL);
+        assert_return(what < _SD_DHCP_LEASE_SERVER_TYPE_MAX, -EINVAL);
         assert_return(addr, -EINVAL);
 
-        if (lease->dns_size <= 0)
+        if (lease->servers[what].size <= 0)
                 return -ENODATA;
 
-        *addr = lease->dns;
-        return (int) lease->dns_size;
+        *addr = lease->servers[what].addr;
+        return (int) lease->servers[what].size;
 }
 
+int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr) {
+        return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_DNS, addr);
+}
 int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, const struct in_addr **addr) {
-        assert_return(lease, -EINVAL);
-        assert_return(addr, -EINVAL);
-
-        if (lease->ntp_size <= 0)
-                return -ENODATA;
-
-        *addr = lease->ntp;
-        return (int) lease->ntp_size;
+        return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_NTP, addr);
 }
-
 int sd_dhcp_lease_get_sip(sd_dhcp_lease *lease, const struct in_addr **addr) {
-        assert_return(lease, -EINVAL);
-        assert_return(addr, -EINVAL);
-
-        if (lease->sip_size <= 0)
-                return -ENODATA;
-
-        *addr = lease->sip;
-        return (int) lease->sip_size;
+        return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_SIP, addr);
+}
+int sd_dhcp_lease_get_pop3(sd_dhcp_lease *lease, const struct in_addr **addr) {
+        return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_POP3, addr);
+}
+int sd_dhcp_lease_get_smtp(sd_dhcp_lease *lease, const struct in_addr **addr) {
+        return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_SMTP, addr);
+}
+int sd_dhcp_lease_get_lpr(sd_dhcp_lease *lease, const struct in_addr **addr) {
+        return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_LPR, addr);
 }
 
 int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname) {
@@ -276,9 +279,10 @@ static sd_dhcp_lease *dhcp_lease_free(sd_dhcp_lease *lease) {
         free(lease->timezone);
         free(lease->hostname);
         free(lease->domainname);
-        free(lease->dns);
-        free(lease->ntp);
-        free(lease->sip);
+
+        for (sd_dhcp_lease_server_type i = 0; i < _SD_DHCP_LEASE_SERVER_TYPE_MAX; i++)
+                free(lease->servers[i].addr);
+
         free(lease->static_route);
         free(lease->client_id);
         free(lease->vendor_specific);
@@ -384,7 +388,7 @@ static int lease_parse_domain(const uint8_t *option, size_t len, char **ret) {
 }
 
 static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *n_ret) {
-        assert(option);
+        assert(option || len == 0);
         assert(ret);
         assert(n_ret);
 
@@ -413,33 +417,24 @@ static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_add
 }
 
 static int lease_parse_sip_server(const uint8_t *option, size_t len, struct in_addr **ret, size_t *n_ret) {
-        assert(option);
+        assert(option || len == 0);
         assert(ret);
         assert(n_ret);
 
-        if (len <= 0) {
-                *ret = mfree(*ret);
-                *n_ret = 0;
-        } else {
-                size_t n_addresses;
-                struct in_addr *addresses;
-                int l = len - 1;
-
-                if (l % 4 != 0)
-                        return -EINVAL;
-
-                n_addresses = l / 4;
+        if (len <= 0)
+                return -EINVAL;
 
-                addresses = newdup(struct in_addr, option + 1, n_addresses);
-                if (!addresses)
-                        return -ENOMEM;
+        /* The SIP record is like the other, regular server records, but prefixed with a single "encoding"
+         * byte that is either 0 or 1. We only support it to be 1 for now. Let's drop it and parse it like
+         * the other fields */
 
-                free(*ret);
-                *ret = addresses;
-                *n_ret = n_addresses;
+        if (option[0] != 1) { /* We only support IP address encoding for now */
+                *ret = mfree(*ret);
+                *n_ret = 0;
+                return 0;
         }
 
-        return 0;
+        return lease_parse_in_addrs(option + 1, len - 1, ret, n_ret);
 }
 
 static int lease_parse_routes(
@@ -584,23 +579,41 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
                 break;
 
         case SD_DHCP_OPTION_DOMAIN_NAME_SERVER:
-                r = lease_parse_in_addrs(option, len, &lease->dns, &lease->dns_size);
+                r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_DNS].addr, &lease->servers[SD_DHCP_LEASE_DNS].size);
                 if (r < 0)
                         log_debug_errno(r, "Failed to parse DNS server, ignoring: %m");
                 break;
 
         case SD_DHCP_OPTION_NTP_SERVER:
-                r = lease_parse_in_addrs(option, len, &lease->ntp, &lease->ntp_size);
+                r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_NTP].addr, &lease->servers[SD_DHCP_LEASE_NTP].size);
                 if (r < 0)
                         log_debug_errno(r, "Failed to parse NTP server, ignoring: %m");
                 break;
 
         case SD_DHCP_OPTION_SIP_SERVER:
-                r = lease_parse_sip_server(option, len, &lease->sip, &lease->sip_size);
+                r = lease_parse_sip_server(option, len, &lease->servers[SD_DHCP_LEASE_SIP].addr, &lease->servers[SD_DHCP_LEASE_SIP].size);
                 if (r < 0)
                         log_debug_errno(r, "Failed to parse SIP server, ignoring: %m");
                 break;
 
+        case SD_DHCP_OPTION_POP3_SERVER:
+                r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_POP3].addr, &lease->servers[SD_DHCP_LEASE_POP3].size);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to parse POP3 server, ignoring: %m");
+                break;
+
+        case SD_DHCP_OPTION_SMTP_SERVER:
+                r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_SMTP].addr, &lease->servers[SD_DHCP_LEASE_SMTP].size);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to parse SMTP server, ignoring: %m");
+                break;
+
+        case SD_DHCP_OPTION_LPR_SERVER:
+                r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_LPR].addr, &lease->servers[SD_DHCP_LEASE_LPR].size);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to parse LPR server, ignoring: %m");
+                break;
+
         case SD_DHCP_OPTION_STATIC_ROUTE:
                 r = lease_parse_routes(option, len, &lease->static_route, &lease->static_route_size, &lease->static_route_allocated);
                 if (r < 0)
@@ -636,7 +649,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
         case SD_DHCP_OPTION_HOST_NAME:
                 r = lease_parse_domain(option, len, &lease->hostname);
                 if (r < 0) {
-                        log_debug_errno(r, "Failed to parse host name, ignoring: %m");
+                        log_debug_errno(r, "Failed to parse hostname, ignoring: %m");
                         return 0;
                 }
 
@@ -1037,6 +1050,9 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
                 *dns = NULL,
                 *ntp = NULL,
                 *sip = NULL,
+                *pop3 = NULL,
+                *smtp = NULL,
+                *lpr = NULL,
                 *mtu = NULL,
                 *routes = NULL,
                 *domains = NULL,
@@ -1060,12 +1076,15 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
                            "ADDRESS", &address,
                            "ROUTER", &router,
                            "NETMASK", &netmask,
-                           "SERVER_IDENTIFIER", &server_address,
+                           "SERVER_ADDRESS", &server_address,
                            "NEXT_SERVER", &next_server,
                            "BROADCAST", &broadcast,
                            "DNS", &dns,
                            "NTP", &ntp,
                            "SIP", &sip,
+                           "POP3", &pop3,
+                           "SMTP", &smtp,
+                           "LPR", &lpr,
                            "MTU", &mtu,
                            "DOMAINNAME", &lease->domainname,
                            "HOSTNAME", &lease->hostname,
@@ -1155,27 +1174,51 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
         }
 
         if (dns) {
-                r = deserialize_in_addrs(&lease->dns, dns);
+                r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_DNS].addr, dns);
                 if (r < 0)
                         log_debug_errno(r, "Failed to deserialize DNS servers %s, ignoring: %m", dns);
                 else
-                        lease->dns_size = r;
+                        lease->servers[SD_DHCP_LEASE_DNS].size = r;
         }
 
         if (ntp) {
-                r = deserialize_in_addrs(&lease->ntp, ntp);
+                r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_NTP].addr, ntp);
                 if (r < 0)
                         log_debug_errno(r, "Failed to deserialize NTP servers %s, ignoring: %m", ntp);
                 else
-                        lease->ntp_size = r;
+                        lease->servers[SD_DHCP_LEASE_NTP].size = r;
         }
 
         if (sip) {
-                r = deserialize_in_addrs(&lease->sip, sip);
+                r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_SIP].addr, sip);
                 if (r < 0)
                         log_debug_errno(r, "Failed to deserialize SIP servers %s, ignoring: %m", sip);
                 else
-                        lease->sip_size = r;
+                        lease->servers[SD_DHCP_LEASE_SIP].size = r;
+        }
+
+        if (pop3) {
+                r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_POP3].addr, pop3);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to deserialize POP3 server %s, ignoring: %m", pop3);
+                else
+                        lease->servers[SD_DHCP_LEASE_POP3].size = r;
+        }
+
+        if (smtp) {
+                r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_SMTP].addr, smtp);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to deserialize SMTP server %s, ignoring: %m", smtp);
+                else
+                        lease->servers[SD_DHCP_LEASE_SMTP].size = r;
+        }
+
+        if (lpr) {
+                r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_LPR].addr, lpr);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to deserialize LPR server %s, ignoring: %m", lpr);
+                else
+                        lease->servers[SD_DHCP_LEASE_LPR].size = r;
         }
 
         if (mtu) {
index e8b43ba9bcc50aed3307e2260adc164d3787d244..ec20b936b80ef48fded618a6e4af8dd8c39e78db 100644 (file)
@@ -3,6 +3,7 @@
   Copyright © 2013 Intel Corporation. All rights reserved.
 ***/
 
+#include <net/if_arp.h>
 #include <sys/ioctl.h>
 
 #include "sd-dhcp-server.h"
@@ -33,7 +34,13 @@ static DHCPLease *dhcp_lease_free(DHCPLease *lease) {
  * the whole pool must fit into the subnet, and may not contain the first (any) nor last (broadcast) address
  * moreover, the server's own address may be in the pool, and is in that case reserved in order not to
  * accidentally hand it out */
-int sd_dhcp_server_configure_pool(sd_dhcp_server *server, struct in_addr *address, unsigned char prefixlen, uint32_t offset, uint32_t size) {
+int sd_dhcp_server_configure_pool(
+                sd_dhcp_server *server,
+                const struct in_addr *address,
+                unsigned char prefixlen,
+                uint32_t offset,
+                uint32_t size) {
+
         struct in_addr netmask_addr;
         be32_t netmask;
         uint32_t server_off, broadcast_off, size_max;
@@ -91,6 +98,9 @@ int sd_dhcp_server_configure_pool(sd_dhcp_server *server, struct in_addr *addres
 
                 /* Drop any leases associated with the old address range */
                 hashmap_clear(server->leases_by_client_id);
+
+                if (server->callback)
+                        server->callback(server, SD_DHCP_SERVER_EVENT_LEASE_CHANGED, server->callback_userdata);
         }
 
         return 0;
@@ -137,13 +147,14 @@ static sd_dhcp_server *dhcp_server_free(sd_dhcp_server *server) {
         sd_event_unref(server->event);
 
         free(server->timezone);
-        free(server->dns);
-        free(server->ntp);
-        free(server->sip);
+
+        for (sd_dhcp_lease_server_type i = 0; i < _SD_DHCP_LEASE_SERVER_TYPE_MAX; i++)
+                free(server->servers[i].addr);
 
         hashmap_free(server->leases_by_client_id);
 
-        ordered_hashmap_free(server->raw_option);
+        ordered_hashmap_free(server->extra_options);
+        ordered_hashmap_free(server->vendor_options);
 
         free(server->bound_leases);
         return mfree(server);
@@ -263,14 +274,14 @@ static int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination,
                 .iov_base = message,
                 .iov_len = len,
         };
-        uint8_t cmsgbuf[CMSG_SPACE(sizeof(struct in_pktinfo))] = {};
+        CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct in_pktinfo))) control = {};
         struct msghdr msg = {
                 .msg_name = &dest,
                 .msg_namelen = sizeof(dest.in),
                 .msg_iov = &iov,
                 .msg_iovlen = 1,
-                .msg_control = cmsgbuf,
-                .msg_controllen = sizeof(cmsgbuf),
+                .msg_control = &control,
+                .msg_controllen = sizeof(control),
         };
         struct cmsghdr *cmsg;
         struct in_pktinfo *pktinfo;
@@ -451,10 +462,24 @@ static int server_send_offer(sd_dhcp_server *server, DHCPRequest *req,
         return 0;
 }
 
-static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req,
-                           be32_t address) {
+static int server_send_ack(
+                sd_dhcp_server *server,
+                DHCPRequest *req,
+                be32_t address) {
+
+        static const uint8_t option_map[_SD_DHCP_LEASE_SERVER_TYPE_MAX] = {
+                [SD_DHCP_LEASE_DNS] = SD_DHCP_OPTION_DOMAIN_NAME_SERVER,
+                [SD_DHCP_LEASE_NTP] = SD_DHCP_OPTION_NTP_SERVER,
+                [SD_DHCP_LEASE_SIP] = SD_DHCP_OPTION_SIP_SERVER,
+                [SD_DHCP_LEASE_POP3] = SD_DHCP_OPTION_POP3_SERVER,
+                [SD_DHCP_LEASE_SMTP] = SD_DHCP_OPTION_SMTP_SERVER,
+                [SD_DHCP_LEASE_LPR] = SD_DHCP_OPTION_LPR_SERVER,
+        };
+
         _cleanup_free_ DHCPPacket *packet = NULL;
         be32_t lease_time;
+        sd_dhcp_option *j;
+        Iterator i;
         size_t offset;
         int r;
 
@@ -483,33 +508,20 @@ static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req,
                         return r;
         }
 
-        if (server->n_dns > 0) {
-                r = dhcp_option_append(
-                                &packet->dhcp, req->max_optlen, &offset, 0,
-                                SD_DHCP_OPTION_DOMAIN_NAME_SERVER,
-                                sizeof(struct in_addr) * server->n_dns, server->dns);
-                if (r < 0)
-                        return r;
-        }
+        for (sd_dhcp_lease_server_type k = 0; k < _SD_DHCP_LEASE_SERVER_TYPE_MAX; k++) {
 
-        if (server->n_ntp > 0) {
-                r = dhcp_option_append(
-                                &packet->dhcp, req->max_optlen, &offset, 0,
-                                SD_DHCP_OPTION_NTP_SERVER,
-                                sizeof(struct in_addr) * server->n_ntp, server->ntp);
-                if (r < 0)
-                        return r;
-        }
+                if (server->servers[k].size <= 0)
+                        continue;
 
-        if (server->n_sip > 0) {
                 r = dhcp_option_append(
                                 &packet->dhcp, req->max_optlen, &offset, 0,
-                                SD_DHCP_OPTION_SIP_SERVER,
-                                sizeof(struct in_addr) * server->n_sip, server->sip);
+                                option_map[k],
+                                sizeof(struct in_addr) * server->servers[k].size, server->servers[k].addr);
                 if (r < 0)
                         return r;
         }
 
+
         if (server->timezone) {
                 r = dhcp_option_append(
                                 &packet->dhcp, req->max_optlen, &offset, 0,
@@ -519,11 +531,18 @@ static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req,
                         return r;
         }
 
-        if (!ordered_hashmap_isempty(server->raw_option)) {
+        ORDERED_HASHMAP_FOREACH(j, server->extra_options, i) {
+                r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
+                                       j->option, j->length, j->data);
+                if (r < 0)
+                        return r;
+        }
+
+        if (!ordered_hashmap_isempty(server->vendor_options)) {
                 r = dhcp_option_append(
                                 &packet->dhcp, req->max_optlen, &offset, 0,
                                 SD_DHCP_OPTION_VENDOR_SPECIFIC,
-                                ordered_hashmap_size(server->raw_option), server->raw_option);
+                                ordered_hashmap_size(server->vendor_options), server->vendor_options);
                 if (r < 0)
                         return r;
         }
@@ -883,6 +902,9 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
                                 hashmap_put(server->leases_by_client_id,
                                             &lease->client_id, lease);
 
+                                if (server->callback)
+                                        server->callback(server, SD_DHCP_SERVER_EVENT_LEASE_CHANGED, server->callback_userdata);
+
                                 return DHCP_ACK;
                         }
 
@@ -919,6 +941,9 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
                         server->bound_leases[pool_offset] = NULL;
                         hashmap_remove(server->leases_by_client_id, existing_lease);
                         dhcp_lease_free(existing_lease);
+
+                        if (server->callback)
+                                server->callback(server, SD_DHCP_SERVER_EVENT_LEASE_CHANGED, server->callback_userdata);
                 }
 
                 return 0;
@@ -930,14 +955,14 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
 static int server_receive_message(sd_event_source *s, int fd,
                                   uint32_t revents, void *userdata) {
         _cleanup_free_ DHCPMessage *message = NULL;
-        uint8_t cmsgbuf[CMSG_SPACE(sizeof(struct in_pktinfo))];
+        CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct in_pktinfo))) control;
         sd_dhcp_server *server = userdata;
         struct iovec iov = {};
         struct msghdr msg = {
                 .msg_iov = &iov,
                 .msg_iovlen = 1,
-                .msg_control = cmsgbuf,
-                .msg_controllen = sizeof(cmsgbuf),
+                .msg_control = &control,
+                .msg_controllen = sizeof(control),
         };
         struct cmsghdr *cmsg;
         ssize_t buflen, len;
@@ -955,14 +980,12 @@ static int server_receive_message(sd_event_source *s, int fd,
 
         iov = IOVEC_MAKE(message, buflen);
 
-        len = recvmsg(fd, &msg, 0);
-        if (len < 0) {
-                if (IN_SET(errno, EAGAIN, EINTR))
-                        return 0;
-
-                return -errno;
-        }
-        if ((size_t)len < sizeof(DHCPMessage))
+        len = recvmsg_safe(fd, &msg, 0);
+        if (IN_SET(len, -EAGAIN, -EINTR))
+                return 0;
+        if (len < 0)
+                return len;
+        if ((size_t) len < sizeof(DHCPMessage))
                 return 0;
 
         CMSG_FOREACH(cmsg, &msg) {
@@ -1093,82 +1116,52 @@ int sd_dhcp_server_set_default_lease_time(sd_dhcp_server *server, uint32_t t) {
         return 1;
 }
 
-int sd_dhcp_server_set_dns(sd_dhcp_server *server, const struct in_addr dns[], unsigned n) {
-        assert_return(server, -EINVAL);
-        assert_return(dns || n <= 0, -EINVAL);
-
-        if (server->n_dns == n &&
-            memcmp(server->dns, dns, sizeof(struct in_addr) * n) == 0)
-                return 0;
-
-        if (n <= 0) {
-                server->dns = mfree(server->dns);
-                server->n_dns = 0;
-        } else {
-                struct in_addr *c;
-
-                c = newdup(struct in_addr, dns, n);
-                if (!c)
-                        return -ENOMEM;
-
-                free(server->dns);
-                server->dns = c;
-                server->n_dns = n;
-        }
+int sd_dhcp_server_set_servers(
+                sd_dhcp_server *server,
+                sd_dhcp_lease_server_type what,
+                const struct in_addr addresses[],
+                size_t n_addresses) {
 
-        return 1;
-}
+        struct in_addr *c = NULL;
 
-int sd_dhcp_server_set_ntp(sd_dhcp_server *server, const struct in_addr ntp[], unsigned n) {
         assert_return(server, -EINVAL);
-        assert_return(ntp || n <= 0, -EINVAL);
+        assert_return(addresses || n_addresses == 0, -EINVAL);
+        assert_return(what >= 0, -EINVAL);
+        assert_return(what < _SD_DHCP_LEASE_SERVER_TYPE_MAX, -EINVAL);
 
-        if (server->n_ntp == n &&
-            memcmp(server->ntp, ntp, sizeof(struct in_addr) * n) == 0)
+        if (server->servers[what].size == n_addresses &&
+            memcmp(server->servers[what].addr, addresses, sizeof(struct in_addr) * n_addresses) == 0)
                 return 0;
 
-        if (n <= 0) {
-                server->ntp = mfree(server->ntp);
-                server->n_ntp = 0;
-        } else {
-                struct in_addr *c;
-
-                c = newdup(struct in_addr, ntp, n);
+        if (n_addresses > 0) {
+                c = newdup(struct in_addr, addresses, n_addresses);
                 if (!c)
                         return -ENOMEM;
-
-                free(server->ntp);
-                server->ntp = c;
-                server->n_ntp = n;
         }
 
+        free(server->servers[what].addr);
+        server->servers[what].addr = c;
+        server->servers[what].size = n_addresses;
         return 1;
 }
 
-int sd_dhcp_server_set_sip(sd_dhcp_server *server, const struct in_addr sip[], unsigned n) {
-        assert_return(server, -EINVAL);
-        assert_return(sip || n <= 0, -EINVAL);
-
-        if (server->n_sip == n &&
-            memcmp(server->sip, sip, sizeof(struct in_addr) * n) == 0)
-                return 0;
-
-        if (n <= 0) {
-                server->sip = mfree(server->sip);
-                server->n_sip = 0;
-        } else {
-                struct in_addr *c;
-
-                c = newdup(struct in_addr, sip, n);
-                if (!c)
-                        return -ENOMEM;
-
-                free(server->sip);
-                server->sip = c;
-                server->n_sip = n;
-        }
-
-        return 1;
+int sd_dhcp_server_set_dns(sd_dhcp_server *server, const struct in_addr dns[], size_t n) {
+        return sd_dhcp_server_set_servers(server, SD_DHCP_LEASE_DNS, dns, n);
+}
+int sd_dhcp_server_set_ntp(sd_dhcp_server *server, const struct in_addr ntp[], size_t n) {
+        return sd_dhcp_server_set_servers(server, SD_DHCP_LEASE_NTP, ntp, n);
+}
+int sd_dhcp_server_set_sip(sd_dhcp_server *server, const struct in_addr sip[], size_t n) {
+        return sd_dhcp_server_set_servers(server, SD_DHCP_LEASE_SIP, sip, n);
+}
+int sd_dhcp_server_set_pop3(sd_dhcp_server *server, const struct in_addr pop3[], size_t n) {
+        return sd_dhcp_server_set_servers(server, SD_DHCP_LEASE_POP3, pop3, n);
+}
+int sd_dhcp_server_set_smtp(sd_dhcp_server *server, const struct in_addr smtp[], size_t n) {
+        return sd_dhcp_server_set_servers(server, SD_DHCP_LEASE_SMTP, smtp, n);
+}
+int sd_dhcp_server_set_lpr(sd_dhcp_server *server, const struct in_addr lpr[], size_t n) {
+        return sd_dhcp_server_set_servers(server, SD_DHCP_LEASE_LPR, lpr, n);
 }
 
 int sd_dhcp_server_set_emit_router(sd_dhcp_server *server, int enabled) {
@@ -1188,11 +1181,29 @@ int sd_dhcp_server_add_option(sd_dhcp_server *server, sd_dhcp_option *v) {
         assert_return(server, -EINVAL);
         assert_return(v, -EINVAL);
 
-        r = ordered_hashmap_ensure_allocated(&server->raw_option, &dhcp_option_hash_ops);
+        r = ordered_hashmap_ensure_allocated(&server->extra_options, &dhcp_option_hash_ops);
+        if (r < 0)
+                return r;
+
+        r = ordered_hashmap_put(server->extra_options, UINT_TO_PTR(v->option), v);
+        if (r < 0)
+                return r;
+
+        sd_dhcp_option_ref(v);
+        return 0;
+}
+
+int sd_dhcp_server_add_vendor_option(sd_dhcp_server *server, sd_dhcp_option *v) {
+        int r;
+
+        assert_return(server, -EINVAL);
+        assert_return(v, -EINVAL);
+
+        r = ordered_hashmap_ensure_allocated(&server->vendor_options, &dhcp_option_hash_ops);
         if (r < 0)
                 return -ENOMEM;
 
-        r = ordered_hashmap_put(server->raw_option, v, v);
+        r = ordered_hashmap_put(server->vendor_options, v, v);
         if (r < 0)
                 return r;
 
@@ -1200,3 +1211,12 @@ int sd_dhcp_server_add_option(sd_dhcp_server *server, sd_dhcp_option *v) {
 
         return 1;
 }
+
+int sd_dhcp_server_set_callback(sd_dhcp_server *server, sd_dhcp_server_callback_t cb, void *userdata) {
+        assert_return(server, -EINVAL);
+
+        server->callback = cb;
+        server->callback_userdata = userdata;
+
+        return 0;
+}
index eac2e725cce7b4a8e66ff595f2a2301ef9237361..5cdb82bc6f30298ec3b3fba0df6f70a04407ee09 100644 (file)
@@ -18,6 +18,7 @@
 #include "dns-domain.h"
 #include "event-util.h"
 #include "fd-util.h"
+#include "hexdecoct.h"
 #include "hostname-util.h"
 #include "in-addr-util.h"
 #include "network-internal.h"
@@ -25,6 +26,7 @@
 #include "socket-util.h"
 #include "string-table.h"
 #include "util.h"
+#include "web-util.h"
 
 #define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
 
@@ -65,6 +67,9 @@ struct sd_dhcp6_client {
         size_t req_opts_allocated;
         size_t req_opts_len;
         char *fqdn;
+        char *mudurl;
+        char **user_class;
+        char **vendor_class;
         sd_event_source *receive_message;
         usec_t retransmit_time;
         uint8_t retransmit_count;
@@ -76,6 +81,8 @@ struct sd_dhcp6_client {
         size_t duid_len;
         usec_t information_request_time_usec;
         usec_t information_refresh_time_usec;
+        OrderedHashmap *extra_options;
+        OrderedHashmap *vendor_options;
 };
 
 static const uint16_t default_req_opts[] = {
@@ -104,12 +111,29 @@ const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
 
 const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
-        [DHCP6_STATUS_SUCCESS] = "Success",
-        [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure",
-        [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available",
-        [DHCP6_STATUS_NO_BINDING] = "Binding unavailable",
-        [DHCP6_STATUS_NOT_ON_LINK] = "Not on link",
-        [DHCP6_STATUS_USE_MULTICAST] = "Use multicast",
+        [DHCP6_STATUS_SUCCESS]                      = "Success",
+        [DHCP6_STATUS_UNSPEC_FAIL]                  = "Unspecified failure",
+        [DHCP6_STATUS_NO_ADDRS_AVAIL]               = "No addresses available",
+        [DHCP6_STATUS_NO_BINDING]                   = "Binding unavailable",
+        [DHCP6_STATUS_NOT_ON_LINK]                  = "Not on link",
+        [DHCP6_STATUS_USE_MULTICAST]                = "Use multicast",
+        [DHCP6_STATUS_NO_PREFIX_AVAIL]              = "No prefix available",
+        [DHCP6_STATUS_UNKNOWN_QUERY_TYPE]           = "Unknown query type",
+        [DHCP6_STATUS_MALFORMED_QUERY]              = "Malformed query",
+        [DHCP6_STATUS_NOT_CONFIGURED]               = "Not configured",
+        [DHCP6_STATUS_NOT_ALLOWED]                  = "Not allowed",
+        [DHCP6_STATUS_QUERY_TERMINATED]             = "Query terminated",
+        [DHCP6_STATUS_DATA_MISSING]                 = "Data missing",
+        [DHCP6_STATUS_CATCHUP_COMPLETE]             = "Catch up complete",
+        [DHCP6_STATUS_NOT_SUPPORTED]                = "Not supported",
+        [DHCP6_STATUS_TLS_CONNECTION_REFUSED]       = "TLS connection refused",
+        [DHCP6_STATUS_ADDRESS_IN_USE]               = "Address in use",
+        [DHCP6_STATUS_CONFIGURATION_CONFLICT]       = "Configuration conflict",
+        [DHCP6_STATUS_MISSING_BINDING_INFORMATION]  = "Missing binding information",
+        [DHCP6_STATUS_OUTDATED_BINDING_INFORMATION] = "Outdated binding information",
+        [DHCP6_STATUS_SERVER_SHUTTING_DOWN]         = "Server shutting down",
+        [DHCP6_STATUS_DNS_UPDATE_NOT_SUPPORTED]     = "DNS update not supported",
+        [DHCP6_STATUS_EXCESSIVE_TIME_SKEW]          = "Excessive time skew",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
@@ -135,7 +159,7 @@ int sd_dhcp6_client_set_callback(
 int sd_dhcp6_client_set_ifindex(sd_dhcp6_client *client, int ifindex) {
 
         assert_return(client, -EINVAL);
-        assert_return(ifindex >= -1, -EINVAL);
+        assert_return(ifindex > 0, -EINVAL);
         assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
 
         client->ifindex = ifindex;
@@ -203,6 +227,25 @@ int sd_dhcp6_client_set_prefix_delegation_hint(
         return 0;
 }
 
+int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, sd_dhcp6_option *v) {
+        int r;
+
+        assert_return(client, -EINVAL);
+        assert_return(v, -EINVAL);
+
+        r = ordered_hashmap_ensure_allocated(&client->vendor_options, &dhcp6_option_hash_ops);
+        if (r < 0)
+                return r;
+
+        r = ordered_hashmap_put(client->vendor_options, v, v);
+        if (r < 0)
+                return r;
+
+        sd_dhcp6_option_ref(v);
+
+        return 1;
+}
+
 static int client_ensure_duid(sd_dhcp6_client *client) {
         if (client->duid_len != 0)
                 return 0;
@@ -233,7 +276,8 @@ static int dhcp6_client_set_duid_internal(
                         r = dhcp_validate_duid_len(duid_type, duid_len, false);
                         if (r < 0)
                                 return log_dhcp6_client_errno(client, r, "Failed to validate length of DUID: %m");
-                        log_dhcp6_client(client, "Setting DUID of type %u with unexpected content", duid_type);
+
+                        log_dhcp6_client(client, "Using DUID of type %u of incorrect length, proceeding.", duid_type);
                 }
 
                 client->duid.type = htobe16(duid_type);
@@ -288,6 +332,48 @@ int sd_dhcp6_client_set_duid_llt(
         return dhcp6_client_set_duid_internal(client, DUID_TYPE_LLT, NULL, 0, llt_time);
 }
 
+static const char* const dhcp6_duid_type_table[_DUID_TYPE_MAX] = {
+        [DUID_TYPE_LLT]  = "DUID-LLT",
+        [DUID_TYPE_EN]   = "DUID-EN/Vendor",
+        [DUID_TYPE_LL]   = "DUID-LL",
+        [DUID_TYPE_UUID] = "UUID",
+};
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(dhcp6_duid_type, DUIDType);
+
+int sd_dhcp6_client_duid_as_string(
+                sd_dhcp6_client *client,
+                char **duid) {
+        _cleanup_free_ char *p = NULL, *s = NULL, *t = NULL;
+        const char *v;
+        int r;
+
+        assert_return(client, -EINVAL);
+        assert_return(client->duid_len > 0, -ENODATA);
+
+        v = dhcp6_duid_type_to_string(be16toh(client->duid.type));
+        if (v) {
+                s = strdup(v);
+                if (!s)
+                        return -ENOMEM;
+        } else {
+                r = asprintf(&s, "%0x", client->duid.type);
+                if (r < 0)
+                        return -ENOMEM;
+        }
+
+        t = hexmem(&client->duid.raw.data, client->duid_len);
+        if (!t)
+                return -ENOMEM;
+
+        p = strjoin(s, ":", t);
+        if (!p)
+                return -ENOMEM;
+
+        *duid = TAKE_PTR(p);
+
+        return 0;
+}
+
 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);
@@ -299,6 +385,18 @@ int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) {
         return 0;
 }
 
+int sd_dhcp6_client_get_iaid(sd_dhcp6_client *client, uint32_t *iaid) {
+        assert_return(client, -EINVAL);
+        assert_return(iaid, -EINVAL);
+
+        if (!client->iaid_set)
+                return -ENODATA;
+
+        *iaid = be32toh(client->ia_na.ia_na.id);
+
+        return 0;
+}
+
 int sd_dhcp6_client_set_fqdn(
                 sd_dhcp6_client *client,
                 const char *fqdn) {
@@ -337,18 +435,8 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option)
         assert_return(client, -EINVAL);
         assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
 
-        switch(option) {
-
-        case SD_DHCP6_OPTION_DNS_SERVERS:
-        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:
+        if (option <= 0 || option >= UINT8_MAX)
                 return -EINVAL;
-        }
 
         for (t = 0; t < client->req_opts_len; t++)
                 if (client->req_opts[t] == htobe16(option))
@@ -363,6 +451,60 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option)
         return 0;
 }
 
+int sd_dhcp6_client_set_request_mud_url(sd_dhcp6_client *client, const char *mudurl) {
+
+        assert_return(client, -EINVAL);
+        assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
+        assert_return(mudurl, -EINVAL);
+        assert_return(strlen(mudurl) <= UINT8_MAX, -EINVAL);
+        assert_return(http_url_is_valid(mudurl), -EINVAL);
+
+        return free_and_strdup(&client->mudurl, mudurl);
+}
+
+int sd_dhcp6_client_set_request_user_class(sd_dhcp6_client *client, char **user_class) {
+        _cleanup_strv_free_ char **s = NULL;
+        char **p;
+
+        assert_return(client, -EINVAL);
+        assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
+
+        assert_return(user_class, -EINVAL);
+
+        STRV_FOREACH(p, user_class)
+                if (strlen(*p) > UINT16_MAX)
+                        return -ENAMETOOLONG;
+
+        s = strv_copy(user_class);
+        if (!s)
+                return -ENOMEM;
+
+        client->user_class = TAKE_PTR(s);
+
+        return 0;
+}
+
+int sd_dhcp6_client_set_request_vendor_class(sd_dhcp6_client *client, char **vendor_class) {
+        _cleanup_strv_free_ char **s = NULL;
+        char **p;
+
+        assert_return(client, -EINVAL);
+        assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
+        assert_return(vendor_class, -EINVAL);
+
+        STRV_FOREACH(p, vendor_class)
+                if (strlen(*p) > UINT8_MAX)
+                        return -ENAMETOOLONG;
+
+        s = strv_copy(vendor_class);
+        if (!s)
+                return -ENOMEM;
+
+        client->vendor_class = TAKE_PTR(s);
+
+        return 0;
+}
+
 int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client, int *delegation) {
         assert_return(client, -EINVAL);
         assert_return(delegation, -EINVAL);
@@ -417,6 +559,24 @@ int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
         return 0;
 }
 
+int sd_dhcp6_client_add_option(sd_dhcp6_client *client, sd_dhcp6_option *v) {
+        int r;
+
+        assert_return(client, -EINVAL);
+        assert_return(v, -EINVAL);
+
+        r = ordered_hashmap_ensure_allocated(&client->extra_options, &dhcp6_option_hash_ops);
+        if (r < 0)
+                return r;
+
+        r = ordered_hashmap_put(client->extra_options, UINT_TO_PTR(v->option), v);
+        if (r < 0)
+                return r;
+
+        sd_dhcp6_option_ref(v);
+        return 0;
+}
+
 static void client_notify(sd_dhcp6_client *client, int event) {
         assert(client);
 
@@ -462,7 +622,9 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
         _cleanup_free_ DHCP6Message *message = NULL;
         struct in6_addr all_servers =
                 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
+        struct sd_dhcp6_option *j;
         size_t len, optlen = 512;
+        Iterator i;
         uint8_t *opt;
         int r;
         usec_t elapsed_usec;
@@ -484,6 +646,14 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
         case DHCP6_STATE_INFORMATION_REQUEST:
                 message->type = DHCP6_INFORMATION_REQUEST;
 
+                if (client->mudurl) {
+                        r = dhcp6_option_append(&opt, &optlen,
+                                                SD_DHCP6_OPTION_MUD_URL, strlen(client->mudurl),
+                                                client->mudurl);
+                        if (r < 0)
+                                return r;
+                }
+
                 break;
 
         case DHCP6_STATE_SOLICITATION:
@@ -507,6 +677,33 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
                                 return r;
                 }
 
+                if (client->mudurl) {
+                        r = dhcp6_option_append(&opt, &optlen,
+                                                SD_DHCP6_OPTION_MUD_URL, strlen(client->mudurl),
+                                                client->mudurl);
+                        if (r < 0)
+                                return r;
+                }
+
+                if (client->user_class) {
+                        r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class);
+                        if (r < 0)
+                                return r;
+                }
+
+                if (client->vendor_class) {
+                        r = dhcp6_option_append_vendor_class(&opt, &optlen, client->vendor_class);
+                        if (r < 0)
+                                return r;
+                }
+
+                if (!ordered_hashmap_isempty(client->vendor_options)) {
+                        r = dhcp6_option_append_vendor_option(&opt, &optlen,
+                                                       client->vendor_options);
+                        if (r < 0)
+                                return r;
+                }
+
                 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
                         r = dhcp6_option_append_pd(opt, optlen, &client->ia_pd, &client->hint_pd_prefix);
                         if (r < 0)
@@ -545,6 +742,32 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
                                 return r;
                 }
 
+                if (client->mudurl) {
+                        r = dhcp6_option_append(&opt, &optlen,
+                                                SD_DHCP6_OPTION_MUD_URL, strlen(client->mudurl),
+                                                client->mudurl);
+                        if (r < 0)
+                                return r;
+                }
+
+                if (client->user_class) {
+                        r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class);
+                        if (r < 0)
+                                return r;
+                }
+
+                if (client->vendor_class) {
+                        r = dhcp6_option_append_vendor_class(&opt, &optlen, client->vendor_class);
+                        if (r < 0)
+                                return r;
+                }
+
+                if (!ordered_hashmap_isempty(client->vendor_options)) {
+                        r = dhcp6_option_append_vendor_option(&opt, &optlen, client->vendor_options);
+                        if (r < 0)
+                                return r;
+                }
+
                 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
                         r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd, NULL);
                         if (r < 0)
@@ -571,6 +794,32 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
                                 return r;
                 }
 
+                if (client->mudurl) {
+                        r = dhcp6_option_append(&opt, &optlen,
+                                                SD_DHCP6_OPTION_MUD_URL, strlen(client->mudurl),
+                                                client->mudurl);
+                        if (r < 0)
+                                return r;
+                }
+
+                if (client->user_class) {
+                        r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class);
+                        if (r < 0)
+                                return r;
+                }
+
+                if (client->vendor_class) {
+                        r = dhcp6_option_append_vendor_class(&opt, &optlen, client->vendor_class);
+                        if (r < 0)
+                                return r;
+                }
+
+                if (!ordered_hashmap_isempty(client->vendor_options)) {
+                        r = dhcp6_option_append_vendor_option(&opt, &optlen, client->vendor_options);
+                        if (r < 0)
+                                return r;
+                }
+
                 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
                         r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd, NULL);
                         if (r < 0)
@@ -610,6 +859,12 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
         if (r < 0)
                 return r;
 
+        ORDERED_HASHMAP_FOREACH(j, client->extra_options, i) {
+                r = dhcp6_option_append(&opt, &optlen, j->option, j->length, j->data);
+                if (r < 0)
+                        return r;
+        }
+
         r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
                                           len - optlen);
         if (r < 0)
@@ -837,10 +1092,11 @@ static int client_parse_message(
                 size_t len,
                 sd_dhcp6_lease *lease) {
 
+        uint16_t ia_na_status = 0, ia_pd_status = 0;
         uint32_t lt_t1 = ~0, lt_t2 = ~0;
+        usec_t irt = IRT_DEFAULT;
         bool clientid = false;
         size_t pos = 0;
-        usec_t irt = IRT_DEFAULT;
         int r;
 
         assert(client);
@@ -854,8 +1110,8 @@ static int client_parse_message(
                 DHCP6Option *option = (DHCP6Option *) &message->options[pos];
                 uint16_t optcode, optlen;
                 be32_t iaid_lease;
+                int  status;
                 uint8_t *optval;
-                int status;
 
                 if (len < pos + offsetof(DHCP6Option, data))
                         return -ENOBUFS;
@@ -932,10 +1188,15 @@ static int client_parse_message(
                                 break;
                         }
 
-                        r = dhcp6_option_parse_ia(option, &lease->ia);
+                        r = dhcp6_option_parse_ia(option, &lease->ia, &ia_na_status);
                         if (r < 0 && r != -ENOMSG)
                                 return r;
 
+                        if (ia_na_status == DHCP6_STATUS_NO_ADDRS_AVAIL) {
+                                pos += offsetof(DHCP6Option, data) + optlen;
+                                continue;
+                        }
+
                         r = dhcp6_lease_get_iaid(lease, &iaid_lease);
                         if (r < 0)
                                 return r;
@@ -960,10 +1221,15 @@ static int client_parse_message(
                                 break;
                         }
 
-                        r = dhcp6_option_parse_ia(option, &lease->pd);
+                        r = dhcp6_option_parse_ia(option, &lease->pd, &ia_pd_status);
                         if (r < 0 && r != -ENOMSG)
                                 return r;
 
+                        if (ia_pd_status == DHCP6_STATUS_NO_PREFIX_AVAIL) {
+                                pos += offsetof(DHCP6Option, data) + optlen;
+                                continue;
+                        }
+
                         r = dhcp6_lease_get_pd_iaid(lease, &iaid_lease);
                         if (r < 0)
                                 return r;
@@ -1027,6 +1293,11 @@ static int client_parse_message(
                 pos += offsetof(DHCP6Option, data) + optlen;
         }
 
+        if (ia_na_status > 0 && ia_pd_status > 0) {
+                log_dhcp6_client(client, "No IA_PD prefix or IA_NA address received. Ignoring.");
+                return -EINVAL;
+        }
+
         if (!clientid) {
                 log_dhcp6_client(client, "%s has incomplete options",
                                  dhcp6_message_type_to_string(message->type));
@@ -1218,7 +1489,7 @@ static int client_receive_message(
                         break;
                 }
 
-                _fallthrough_; /* for Soliciation Rapid Commit option check */
+                _fallthrough_; /* for Solicitation Rapid Commit option check */
         case DHCP6_STATE_REQUEST:
         case DHCP6_STATE_RENEW:
         case DHCP6_STATE_REBIND:
@@ -1521,6 +1792,12 @@ static sd_dhcp6_client *dhcp6_client_free(sd_dhcp6_client *client) {
 
         free(client->req_opts);
         free(client->fqdn);
+        free(client->mudurl);
+
+        ordered_hashmap_free(client->extra_options);
+        strv_free(client->user_class);
+        strv_free(client->vendor_class);
+
         return mfree(client);
 }
 
index 8aebb53c873da63c6d0e0d889070c4188718cf51..4eee10ea896b7264236181e0f0e2e185001b276f 100644 (file)
@@ -213,7 +213,7 @@ int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) {
         return 0;
 }
 
-int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, struct in6_addr **addrs) {
+int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **addrs) {
         assert_return(lease, -EINVAL);
         assert_return(addrs, -EINVAL);
 
@@ -341,7 +341,7 @@ int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen)
 }
 
 int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease,
-                                 struct in6_addr **addrs) {
+                                 const struct in6_addr **addrs) {
         assert_return(lease, -EINVAL);
         assert_return(addrs, -EINVAL);
 
index aa1ece41d25cb5e7b9fdc332ee1c018aa712cb6b..4f4d9ebe87719add28630b590dcc4057f4d61349 100644 (file)
@@ -252,12 +252,14 @@ static int ipv4ll_start_internal(sd_ipv4ll *ll, bool reset_generation) {
                 return r;
         }
 
-        return 0;
+        return 1;
 }
 
 int sd_ipv4ll_start(sd_ipv4ll *ll) {
         assert_return(ll, -EINVAL);
-        assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY);
+
+        if (sd_ipv4ll_is_running(ll))
+                return 0;
 
         return ipv4ll_start_internal(ll, true);
 }
index 0d65b3f5b1f4cc353693cbcfe9222214d05354f7..6163cf169143467713c6b64213f840f20e872a59 100644 (file)
@@ -77,6 +77,12 @@ _public_ sd_event *sd_radv_get_event(sd_radv *ra) {
         return ra->event;
 }
 
+_public_ int sd_radv_is_running(sd_radv *ra) {
+        assert_return(ra, false);
+
+        return ra->state != SD_RADV_STATE_IDLE;
+}
+
 static void radv_reset(sd_radv *ra) {
         assert(ra);
 
@@ -415,7 +421,7 @@ _public_ int sd_radv_start(sd_radv *ra) {
 
 _public_ int sd_radv_set_ifindex(sd_radv *ra, int ifindex) {
         assert_return(ra, -EINVAL);
-        assert_return(ifindex >= -1, -EINVAL);
+        assert_return(ifindex > 0, -EINVAL);
 
         if (ra->state != SD_RADV_STATE_IDLE)
                 return -EBUSY;
@@ -572,6 +578,15 @@ _public_ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p, int dynamic) {
 
         cur = p;
 
+        /* If RAs have already been sent, send an RA immediately to announce the newly-added prefix */
+        if (ra->ra_sent > 0) {
+            r = radv_send(ra, NULL, ra->lifetime);
+            if (r < 0)
+                    log_radv_errno(r, "Unable to send Router Advertisement for added prefix: %m");
+            else
+                    log_radv("Sent Router Advertisement for added prefix");
+        }
+
  update:
         r = sd_event_now(ra->event, clock_boottime_or_monotonic(), &time_now);
         if (r < 0)
@@ -680,6 +695,15 @@ _public_ int sd_radv_add_route_prefix(sd_radv *ra, sd_radv_route_prefix *p, int
                 return 0;
         }
 
+        /* If RAs have already been sent, send an RA immediately to announce the newly-added route prefix */
+        if (ra->ra_sent > 0) {
+            r = radv_send(ra, NULL, ra->lifetime);
+            if (r < 0)
+                    log_radv_errno(r, "Unable to send Router Advertisement for added route prefix: %m");
+            else
+                    log_radv("Sent Router Advertisement for added route prefix");
+        }
+
  update:
         r = sd_event_now(ra->event, clock_boottime_or_monotonic(), &time_now);
         if (r < 0)
@@ -827,6 +851,18 @@ _public_ int sd_radv_prefix_set_prefix(sd_radv_prefix *p, const struct in6_addr
         return 0;
 }
 
+_public_ int sd_radv_prefix_get_prefix(sd_radv_prefix *p, struct in6_addr *ret_in6_addr,
+                                       unsigned char *ret_prefixlen) {
+        assert_return(p, -EINVAL);
+        assert_return(ret_in6_addr, -EINVAL);
+        assert_return(ret_prefixlen, -EINVAL);
+
+        *ret_in6_addr = p->opt.in6_addr;
+        *ret_prefixlen = p->opt.prefixlen;
+
+        return 0;
+}
+
 _public_ int sd_radv_prefix_set_onlink(sd_radv_prefix *p, int onlink) {
         assert_return(p, -EINVAL);
 
index 4e9b388a451aa0be06c3838d9aa34773b8e45717..8f2f4462be3513f9a0ad34c7f3c9cf2010c293b6 100644 (file)
@@ -4,10 +4,11 @@
 ***/
 
 #include <errno.h>
+#include <net/if.h>
+#include <net/if_arp.h>
 #include <stdio.h>
 #include <sys/socket.h>
 #include <unistd.h>
-#include <net/if.h>
 
 #include "sd-dhcp-client.h"
 #include "sd-event.h"
@@ -257,7 +258,7 @@ int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link, const
 }
 
 int dhcp_network_bind_raw_socket(
-                int index,
+                int ifindex,
                 union sockaddr_union *link,
                 uint32_t id,
                 const uint8_t *addr, size_t addr_len,
index 56bd690cb0a0280fb8e8803f90f0b07286e759ec..086f8b5fd96c946817f1c99ec6d5f56d1b4ef9ce 100644 (file)
@@ -1,4 +1,7 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
 #include <errno.h>
+#include <net/if_arp.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <string.h>
index ea998939bc48729adf2c7e5dfa07220142e05b64..a45d98f669077d61fe9534ceafc8c9aff03d16a6 100644 (file)
@@ -4,6 +4,7 @@
 ***/
 
 #include <errno.h>
+#include <net/if_arp.h>
 
 #include "sd-dhcp-server.h"
 #include "sd-event.h"
index 4b89b320b756cf67d06f4bb9aa5408b576db3aae..7af7d670b5917ca2879d52c17855839f733391ae 100644 (file)
@@ -30,7 +30,7 @@ static struct ether_addr mac_addr = {
 
 static sd_event_source *hangcheck;
 static int test_dhcp_fd[2];
-static int test_index = 42;
+static int test_ifindex = 42;
 static int test_client_message_num;
 static be32_t test_iaid = 0;
 static uint8_t test_duid[14] = { };
@@ -48,7 +48,7 @@ static int test_client_basic(sd_event *e) {
 
         assert_se(sd_dhcp6_client_set_ifindex(client, 15) == 0);
         assert_se(sd_dhcp6_client_set_ifindex(client, -42) == -EINVAL);
-        assert_se(sd_dhcp6_client_set_ifindex(client, -1) == 0);
+        assert_se(sd_dhcp6_client_set_ifindex(client, -1) == -EINVAL);
         assert_se(sd_dhcp6_client_set_ifindex(client, 42) >= 0);
 
         assert_se(sd_dhcp6_client_set_mac(client, (const uint8_t *) &mac_addr,
@@ -61,12 +61,12 @@ static int test_client_basic(sd_event *e) {
         assert_se(sd_dhcp6_client_set_fqdn(client, "~host") == -EINVAL);
         assert_se(sd_dhcp6_client_set_fqdn(client, "~host.domain") == -EINVAL);
 
-        assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_CLIENTID) == -EINVAL);
+        assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_CLIENTID) == 0);
         assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_DNS_SERVERS) == -EEXIST);
         assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_NTP_SERVER) == -EEXIST);
         assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_SNTP_SERVERS) == -EEXIST);
         assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_DOMAIN_LIST) == -EEXIST);
-        assert_se(sd_dhcp6_client_set_request_option(client, 10) == -EINVAL);
+        assert_se(sd_dhcp6_client_set_request_option(client, 10) == 0);
 
         assert_se(sd_dhcp6_client_set_information_request(client, 1) >= 0);
         v = 0;
@@ -247,17 +247,17 @@ static int test_option_status(sd_event *e) {
         option = (DHCP6Option *)option1;
         assert_se(sizeof(option1) == sizeof(DHCP6Option) + be16toh(option->len));
 
-        r = dhcp6_option_parse_ia(option, &ia);
-        assert_se(r == -EINVAL);
+        r = dhcp6_option_parse_ia(option, &ia, NULL);
+        assert_se(r == 0);
         assert_se(ia.addresses == NULL);
 
         option->len = htobe16(17);
-        r = dhcp6_option_parse_ia(option, &ia);
+        r = dhcp6_option_parse_ia(option, &ia, NULL);
         assert_se(r == -ENOBUFS);
         assert_se(ia.addresses == NULL);
 
         option->len = htobe16(sizeof(DHCP6Option));
-        r = dhcp6_option_parse_ia(option, &ia);
+        r = dhcp6_option_parse_ia(option, &ia, NULL);
         assert_se(r == -ENOBUFS);
         assert_se(ia.addresses == NULL);
 
@@ -265,7 +265,7 @@ static int test_option_status(sd_event *e) {
         option = (DHCP6Option *)option2;
         assert_se(sizeof(option2) == sizeof(DHCP6Option) + be16toh(option->len));
 
-        r = dhcp6_option_parse_ia(option, &ia);
+        r = dhcp6_option_parse_ia(option, &ia, NULL);
         assert_se(r >= 0);
         assert_se(ia.addresses == NULL);
 
@@ -273,7 +273,7 @@ static int test_option_status(sd_event *e) {
         option = (DHCP6Option *)option3;
         assert_se(sizeof(option3) == sizeof(DHCP6Option) + be16toh(option->len));
 
-        r = dhcp6_option_parse_ia(option, &ia);
+        r = dhcp6_option_parse_ia(option, &ia, NULL);
         assert_se(r >= 0);
         assert_se(ia.addresses != NULL);
         dhcp6_lease_free_ia(&ia);
@@ -282,8 +282,8 @@ static int test_option_status(sd_event *e) {
         option = (DHCP6Option *)option4;
         assert_se(sizeof(option4) == sizeof(DHCP6Option) + be16toh(option->len));
 
-        r = dhcp6_option_parse_ia(option, &pd);
-        assert_se(r == 0);
+        r = dhcp6_option_parse_ia(option, &pd, NULL);
+        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);
@@ -294,8 +294,8 @@ static int test_option_status(sd_event *e) {
         option = (DHCP6Option *)option5;
         assert_se(sizeof(option5) == sizeof(DHCP6Option) + be16toh(option->len));
 
-        r = dhcp6_option_parse_ia(option, &pd);
-        assert_se(r == 0);
+        r = dhcp6_option_parse_ia(option, &pd, NULL);
+        assert_se(r >= 0);
         assert_se(pd.addresses != NULL);
         dhcp6_lease_free_ia(&pd);
 
@@ -364,15 +364,15 @@ static int test_advertise_option(sd_event *e) {
         _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
         DHCP6Message *advertise = (DHCP6Message *)msg_advertise;
         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;
+        const struct in6_addr *addrs;
+        uint8_t preference = 255;
+        struct in6_addr addr;
         char **domains;
+        uint8_t *opt;
+        int r;
+        be32_t val;
 
         log_debug("/* %s */", __func__);
 
@@ -410,7 +410,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(option, &lease->ia) >= 0);
+                        assert_se(dhcp6_option_parse_ia(option, &lease->ia, NULL) >= 0);
 
                         break;
 
@@ -518,7 +518,7 @@ static void test_client_solicit_cb(sd_dhcp6_client *client, int event,
                                    void *userdata) {
         sd_event *e = userdata;
         sd_dhcp6_lease *lease;
-        struct in6_addr *addrs;
+        const struct in6_addr *addrs;
         char **domains;
 
         log_debug("/* %s */", __func__);
@@ -563,12 +563,12 @@ static int test_client_send_reply(DHCP6Message *request) {
 
 static int test_client_verify_request(DHCP6Message *request, size_t len) {
         _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
-        size_t pos = 0;
         bool found_clientid = false, found_iana = false, found_serverid = false,
                 found_elapsed_time = false, found_fqdn = false;
+        uint32_t lt_pref, lt_valid;
         struct in6_addr addr;
+        size_t pos = 0;
         be32_t val;
-        uint32_t lt_pref, lt_valid;
 
         log_debug("/* %s */", __func__);
 
@@ -606,7 +606,7 @@ static int test_client_verify_request(DHCP6Message *request, size_t len) {
                         val = htobe32(120);
                         assert_se(!memcmp(optval + 8, &val, sizeof(val)));
 
-                        assert_se(!dhcp6_option_parse_ia(option, &lease->ia));
+                        assert_se(dhcp6_option_parse_ia(option, &lease->ia, NULL) >= 0);
 
                         break;
 
@@ -744,7 +744,7 @@ static void test_client_information_cb(sd_dhcp6_client *client, int event,
                                        void *userdata) {
         sd_event *e = userdata;
         sd_dhcp6_lease *lease;
-        struct in6_addr *addrs;
+        const struct in6_addr *addrs;
         struct in6_addr address = { { { 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } } };
         char **domains;
 
@@ -877,8 +877,8 @@ int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address,
         return len;
 }
 
-int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
-        assert_se(index == test_index);
+int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *local_address) {
+        assert_se(ifindex == test_ifindex);
 
         if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_dhcp_fd) < 0)
                 return -errno;
@@ -899,7 +899,7 @@ static int test_client_solicit(sd_event *e) {
 
         assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0);
 
-        assert_se(sd_dhcp6_client_set_ifindex(client, test_index) == 0);
+        assert_se(sd_dhcp6_client_set_ifindex(client, test_ifindex) == 0);
         assert_se(sd_dhcp6_client_set_mac(client, (const uint8_t *) &mac_addr,
                                           sizeof (mac_addr),
                                           ARPHRD_ETHER) >= 0);
index 2f319bf7d9d07b94d80953008fc21e9f0b51c23b..310b658e188a0eb203041ac7f8c6d97e017feed5 100644 (file)
@@ -78,7 +78,7 @@ int arp_send_announcement(int fd, int ifindex,
         return arp_network_send_raw_socket(fd, ifindex, &ea);
 }
 
-int arp_network_bind_raw_socket(int index, be32_t address, const struct ether_addr *eth_mac) {
+int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_addr *eth_mac) {
         if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) < 0)
                 return -errno;
 
@@ -159,10 +159,10 @@ static void test_basic_request(sd_event *e) {
         assert_se(sd_ipv4ll_start(ll) == -EINVAL);
 
         assert_se(sd_ipv4ll_set_ifindex(ll, 1) == 0);
-        assert_se(sd_ipv4ll_start(ll) == 0);
+        assert_se(sd_ipv4ll_start(ll) == 1);
 
         sd_event_run(e, (uint64_t) -1);
-        assert_se(sd_ipv4ll_start(ll) == -EBUSY);
+        assert_se(sd_ipv4ll_start(ll) == 0);
 
         assert_se(sd_ipv4ll_is_running(ll));
 
index 7c6c4663f3b7836d519b0ab24dc859b67b428836..d759ec03a8ad9efbda2a28cfb66855bfc4a7372a 100644 (file)
@@ -159,8 +159,8 @@ static void test_radv(void) {
         assert_se(ra);
 
         assert_se(sd_radv_set_ifindex(NULL, 0) < 0);
-        assert_se(sd_radv_set_ifindex(ra, 0) >= 0);
-        assert_se(sd_radv_set_ifindex(ra, -1) >= 0);
+        assert_se(sd_radv_set_ifindex(ra, 0) < 0);
+        assert_se(sd_radv_set_ifindex(ra, -1) < 0);
         assert_se(sd_radv_set_ifindex(ra, -2) < 0);
         assert_se(sd_radv_set_ifindex(ra, 42) >= 0);
 
@@ -219,12 +219,12 @@ static void test_radv(void) {
         assert_se(!ra);
 }
 
-int icmp6_bind_router_solicitation(int index) {
+int icmp6_bind_router_solicitation(int ifindex) {
         return -ENOSYS;
 }
 
-int icmp6_bind_router_advertisement(int index) {
-        assert_se(index == 42);
+int icmp6_bind_router_advertisement(int ifindex) {
+        assert_se(ifindex == 42);
 
         return test_fd[1];
 }
index c8ee1ec31d24b7436b640dadb61aa59578082270..3f46c8d0c58bb97436cb343dca897f57c690e5cd 100644 (file)
@@ -174,8 +174,8 @@ static int test_rs_hangcheck(sd_event_source *s, uint64_t usec,
         return 0;
 }
 
-int icmp6_bind_router_solicitation(int index) {
-        assert_se(index == 42);
+int icmp6_bind_router_solicitation(int ifindex) {
+        assert_se(ifindex == 42);
 
         if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) < 0)
                 return -errno;
@@ -183,8 +183,7 @@ int icmp6_bind_router_solicitation(int index) {
         return test_fd[0];
 }
 
-int icmp6_bind_router_advertisement(int index) {
-
+int icmp6_bind_router_advertisement(int ifindex) {
         return -ENOSYS;
 }
 
@@ -218,7 +217,7 @@ static int send_ra(uint8_t flags) {
         advertisement[5] = flags;
 
         assert_se(write(test_fd[1], advertisement, sizeof(advertisement)) ==
-               sizeof(advertisement));
+                  sizeof(advertisement));
 
         if (verbose)
                 printf("  sent RA with flag 0x%02x\n", flags);
@@ -292,11 +291,12 @@ static void test_rs(void) {
         assert_se(sd_ndisc_set_callback(nd, test_callback, e) >= 0);
 
         assert_se(sd_event_add_time(e, &test_hangcheck, clock_boottime_or_monotonic(),
-                                 time_now + 2 *USEC_PER_SEC, 0,
-                                 test_rs_hangcheck, NULL) >= 0);
+                                    time_now + 30 * USEC_PER_SEC, 0,
+                                    test_rs_hangcheck, NULL) >= 0);
 
         assert_se(sd_ndisc_stop(nd) >= 0);
         assert_se(sd_ndisc_start(nd) >= 0);
+        assert_se(sd_ndisc_start(nd) >= 0);
         assert_se(sd_ndisc_stop(nd) >= 0);
 
         assert_se(sd_ndisc_start(nd) >= 0);
@@ -393,8 +393,8 @@ static void test_timeout(void) {
         assert_se(sd_ndisc_set_mac(nd, &mac_addr) >= 0);
 
         assert_se(sd_event_add_time(e, &test_hangcheck, clock_boottime_or_monotonic(),
-                                 time_now + 2U * USEC_PER_SEC, 0,
-                                 test_rs_hangcheck, NULL) >= 0);
+                                    time_now + 30 * USEC_PER_SEC, 0,
+                                    test_rs_hangcheck, NULL) >= 0);
 
         assert_se(sd_ndisc_start(nd) >= 0);
 
index 8b6ebbcf8bf6ca42369ef576d68a86a3295d134e..1e654b49ea3c1b07b10b87327bd993efa65a1917 100644 (file)
@@ -697,3 +697,27 @@ global:
         sd_event_source_send_child_signal;
         sd_journal_open_namespace;
 } LIBSYSTEMD_243;
+
+LIBSYSTEMD_246 {
+global:
+        sd_bus_interface_name_is_valid;
+        sd_bus_service_name_is_valid;
+        sd_bus_member_name_is_valid;
+        sd_bus_object_path_is_valid;
+
+        sd_bus_call_methodv;
+        sd_bus_call_method_asyncv;
+        sd_bus_emit_signalv;
+        sd_bus_reply_method_errnofv;
+        sd_bus_reply_method_errorfv;
+        sd_bus_reply_method_returnv;
+        sd_bus_set_propertyv;
+
+        sd_path_lookup;
+        sd_path_lookup_strv;
+
+        sd_notify_barrier;
+
+        sd_journal_enumerate_available_data;
+        sd_journal_enumerate_available_unique;
+} LIBSYSTEMD_245;
index 174f1228af29fdc35cdbd2c14322de7543fb105f..dc9a2fdc3a56f3f6c37682a31f47f817d24bc98c 100644 (file)
@@ -120,6 +120,8 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
         SD_BUS_ERROR_MAP(BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN,    EBADSLT),
         SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_PIN_NEEDED,             ENOANO),
         SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED, ERFKILL),
+        SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED,   EMEDIUMTYPE),
+        SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_ACTION_TIMEOUT,         ENOSTR),
         SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_PIN_LOCKED,             EOWNERDEAD),
         SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_BAD_PIN,                ENOLCK),
         SD_BUS_ERROR_MAP(BUS_ERROR_TOKEN_BAD_PIN_FEW_TRIES_LEFT, ETOOMANYREFS),
@@ -134,6 +136,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
         SD_BUS_ERROR_MAP(BUS_ERROR_HOME_NOT_LOCKED,              ENOEXEC),
         SD_BUS_ERROR_MAP(BUS_ERROR_TOO_MANY_OPERATIONS,          ENOBUFS),
         SD_BUS_ERROR_MAP(BUS_ERROR_AUTHENTICATION_LIMIT_HIT,     ETOOMANYREFS),
+        SD_BUS_ERROR_MAP(BUS_ERROR_HOME_CANT_AUTHENTICATE,       EKEYREVOKED),
 
         SD_BUS_ERROR_MAP_END
 };
index e5f92b9ec261e3c669cf502348ffc6b2625a9967..ae805438804e5257430bac99fe523716df17d5d1 100644 (file)
@@ -29,6 +29,7 @@
 #define BUS_ERROR_DISK_FULL "org.freedesktop.systemd1.DiskFull"
 #define BUS_ERROR_NOTHING_TO_CLEAN "org.freedesktop.systemd1.NothingToClean"
 #define BUS_ERROR_UNIT_BUSY "org.freedesktop.systemd1.UnitBusy"
+#define BUS_ERROR_UNIT_INACTIVE "org.freedesktop.systemd1.UnitInactive"
 
 #define BUS_ERROR_NO_SUCH_MACHINE "org.freedesktop.machine1.NoSuchMachine"
 #define BUS_ERROR_NO_SUCH_IMAGE "org.freedesktop.machine1.NoSuchImage"
 #define BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN "org.freedesktop.home1.BadPasswordAndNoToken"
 #define BUS_ERROR_TOKEN_PIN_NEEDED "org.freedesktop.home1.TokenPinNeeded"
 #define BUS_ERROR_TOKEN_PROTECTED_AUTHENTICATION_PATH_NEEDED "org.freedesktop.home1.TokenProtectedAuthenticationPathNeeded"
+#define BUS_ERROR_TOKEN_USER_PRESENCE_NEEDED "org.freedesktop.home1.TokenUserPresenceNeeded"
+#define BUS_ERROR_TOKEN_ACTION_TIMEOUT "org.freedesktop.home1.TokenActionTimeout"
 #define BUS_ERROR_TOKEN_PIN_LOCKED "org.freedesktop.home1.TokenPinLocked"
 #define BUS_ERROR_TOKEN_BAD_PIN "org.freedesktop.home1.BadPin"
 #define BUS_ERROR_TOKEN_BAD_PIN_FEW_TRIES_LEFT "org.freedesktop.home1.BadPinFewTriesLeft"
 #define BUS_ERROR_NO_DISK_SPACE "org.freedesktop.home1.NoDiskSpace"
 #define BUS_ERROR_TOO_MANY_OPERATIONS "org.freedesktop.home1.TooManyOperations"
 #define BUS_ERROR_AUTHENTICATION_LIMIT_HIT "org.freedesktop.home1.AuthenticationLimitHit"
+#define BUS_ERROR_HOME_CANT_AUTHENTICATE "org.freedesktop.home1.HomeCantAuthenticate"
 
 BUS_ERROR_MAP_ELF_USE(bus_common_errors);
index 4ec061644d53b42cb15cfd08c46da33c72c710a9..a5672a831ff8401bc75d3b73282e1fb66d655a0e 100644 (file)
 #include "bus-util.h"
 #include "string-util.h"
 
-_public_ int sd_bus_emit_signal(
+_public_ int sd_bus_emit_signalv(
                 sd_bus *bus,
                 const char *path,
                 const char *interface,
                 const char *member,
-                const char *types, ...) {
+                const char *types, va_list ap) {
 
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
         int r;
@@ -32,11 +32,7 @@ _public_ int sd_bus_emit_signal(
                 return r;
 
         if (!isempty(types)) {
-                va_list ap;
-
-                va_start(ap, types);
                 r = sd_bus_message_appendv(m, types, ap);
-                va_end(ap);
                 if (r < 0)
                         return r;
         }
@@ -44,7 +40,24 @@ _public_ int sd_bus_emit_signal(
         return sd_bus_send(bus, m, NULL);
 }
 
-_public_ int sd_bus_call_method_async(
+_public_ int sd_bus_emit_signal(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *member,
+                const char *types, ...) {
+
+        va_list ap;
+        int r;
+
+        va_start(ap, types);
+        r = sd_bus_emit_signalv(bus, path, interface, member, types, ap);
+        va_end(ap);
+
+        return r;
+}
+
+_public_ int sd_bus_call_method_asyncv(
                 sd_bus *bus,
                 sd_bus_slot **slot,
                 const char *destination,
@@ -53,7 +66,7 @@ _public_ int sd_bus_call_method_async(
                 const char *member,
                 sd_bus_message_handler_t callback,
                 void *userdata,
-                const char *types, ...) {
+                const char *types, va_list ap) {
 
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
         int r;
@@ -70,11 +83,7 @@ _public_ int sd_bus_call_method_async(
                 return r;
 
         if (!isempty(types)) {
-                va_list ap;
-
-                va_start(ap, types);
                 r = sd_bus_message_appendv(m, types, ap);
-                va_end(ap);
                 if (r < 0)
                         return r;
         }
@@ -82,7 +91,28 @@ _public_ int sd_bus_call_method_async(
         return sd_bus_call_async(bus, slot, m, callback, userdata, 0);
 }
 
-_public_ int sd_bus_call_method(
+_public_ int sd_bus_call_method_async(
+                sd_bus *bus,
+                sd_bus_slot **slot,
+                const char *destination,
+                const char *path,
+                const char *interface,
+                const char *member,
+                sd_bus_message_handler_t callback,
+                void *userdata,
+                const char *types, ...) {
+
+        va_list ap;
+        int r;
+
+        va_start(ap, types);
+        r = sd_bus_call_method_asyncv(bus, slot, destination, path, interface, member, callback, userdata, types, ap);
+        va_end(ap);
+
+        return r;
+}
+
+_public_ int sd_bus_call_methodv(
                 sd_bus *bus,
                 const char *destination,
                 const char *path,
@@ -90,12 +120,13 @@ _public_ int sd_bus_call_method(
                 const char *member,
                 sd_bus_error *error,
                 sd_bus_message **reply,
-                const char *types, ...) {
+                const char *types, va_list ap) {
 
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
         int r;
 
         bus_assert_return(bus, -EINVAL, error);
+        bus_assert_return(bus = bus_resolve(bus), -ENOPKG, error);
         bus_assert_return(!bus_pid_changed(bus), -ECHILD, error);
 
         if (!BUS_IS_OPEN(bus->state)) {
@@ -108,11 +139,7 @@ _public_ int sd_bus_call_method(
                 goto fail;
 
         if (!isempty(types)) {
-                va_list ap;
-
-                va_start(ap, types);
                 r = sd_bus_message_appendv(m, types, ap);
-                va_end(ap);
                 if (r < 0)
                         goto fail;
         }
@@ -123,10 +150,30 @@ fail:
         return sd_bus_error_set_errno(error, r);
 }
 
-_public_ int sd_bus_reply_method_return(
-                sd_bus_message *call,
+_public_ int sd_bus_call_method(
+                sd_bus *bus,
+                const char *destination,
+                const char *path,
+                const char *interface,
+                const char *member,
+                sd_bus_error *error,
+                sd_bus_message **reply,
                 const char *types, ...) {
 
+        va_list ap;
+        int r;
+
+        va_start(ap, types);
+        r = sd_bus_call_methodv(bus, destination, path, interface, member, error, reply, types, ap);
+        va_end(ap);
+
+        return r;
+}
+
+_public_ int sd_bus_reply_method_returnv(
+                sd_bus_message *call,
+                const char *types, va_list ap) {
+
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
         int r;
 
@@ -147,11 +194,7 @@ _public_ int sd_bus_reply_method_return(
                 return r;
 
         if (!isempty(types)) {
-                va_list ap;
-
-                va_start(ap, types);
                 r = sd_bus_message_appendv(m, types, ap);
-                va_end(ap);
                 if (r < 0)
                         return r;
         }
@@ -159,6 +202,20 @@ _public_ int sd_bus_reply_method_return(
         return sd_bus_send(call->bus, m, NULL);
 }
 
+_public_ int sd_bus_reply_method_return(
+                sd_bus_message *call,
+                const char *types, ...) {
+
+        va_list ap;
+        int r;
+
+        va_start(ap, types);
+        r = sd_bus_reply_method_returnv(call, types, ap);
+        va_end(ap);
+
+        return r;
+}
+
 _public_ int sd_bus_reply_method_error(
                 sd_bus_message *call,
                 const sd_bus_error *e) {
@@ -186,14 +243,13 @@ _public_ int sd_bus_reply_method_error(
         return sd_bus_send(call->bus, m, NULL);
 }
 
-_public_ int sd_bus_reply_method_errorf(
+_public_ int sd_bus_reply_method_errorfv(
                 sd_bus_message *call,
                 const char *name,
                 const char *format,
-                ...) {
+                va_list ap) {
 
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
-        va_list ap;
 
         assert_return(call, -EINVAL);
         assert_return(call->sealed, -EPERM);
@@ -207,13 +263,27 @@ _public_ int sd_bus_reply_method_errorf(
         if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
                 return 0;
 
-        va_start(ap, format);
         bus_error_setfv(&error, name, format, ap);
-        va_end(ap);
 
         return sd_bus_reply_method_error(call, &error);
 }
 
+_public_ int sd_bus_reply_method_errorf(
+                sd_bus_message *call,
+                const char *name,
+                const char *format,
+                ...) {
+
+        va_list ap;
+        int r;
+
+        va_start(ap, format);
+        r = sd_bus_reply_method_errorfv(call, name, format, ap);
+        va_end(ap);
+
+        return r;
+}
+
 _public_ int sd_bus_reply_method_errno(
                 sd_bus_message *call,
                 int error,
@@ -241,14 +311,13 @@ _public_ int sd_bus_reply_method_errno(
         return sd_bus_reply_method_error(call, &berror);
 }
 
-_public_ int sd_bus_reply_method_errnof(
+_public_ int sd_bus_reply_method_errnofv(
                 sd_bus_message *call,
                 int error,
                 const char *format,
-                ...) {
+                va_list ap) {
 
         _cleanup_(sd_bus_error_free) sd_bus_error berror = SD_BUS_ERROR_NULL;
-        va_list ap;
 
         assert_return(call, -EINVAL);
         assert_return(call->sealed, -EPERM);
@@ -262,13 +331,27 @@ _public_ int sd_bus_reply_method_errnof(
         if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
                 return 0;
 
-        va_start(ap, format);
         sd_bus_error_set_errnofv(&berror, error, format, ap);
-        va_end(ap);
 
         return sd_bus_reply_method_error(call, &berror);
 }
 
+_public_ int sd_bus_reply_method_errnof(
+                sd_bus_message *call,
+                int error,
+                const char *format,
+                ...) {
+
+        va_list ap;
+        int r;
+
+        va_start(ap, format);
+        r = sd_bus_reply_method_errnofv(call, error, format, ap);
+        va_end(ap);
+
+        return r;
+}
+
 _public_ int sd_bus_get_property(
                 sd_bus *bus,
                 const char *destination,
@@ -283,6 +366,7 @@ _public_ int sd_bus_get_property(
         int r;
 
         bus_assert_return(bus, -EINVAL, error);
+        bus_assert_return(bus = bus_resolve(bus), -ENOPKG, error);
         bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error);
         bus_assert_return(member_name_is_valid(member), -EINVAL, error);
         bus_assert_return(reply, -EINVAL, error);
@@ -294,7 +378,10 @@ _public_ int sd_bus_get_property(
                 goto fail;
         }
 
-        r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &rep, "ss", strempty(interface), member);
+        r = sd_bus_call_method(bus, destination, path,
+                               "org.freedesktop.DBus.Properties", "Get",
+                               error, &rep,
+                               "ss", strempty(interface), member);
         if (r < 0)
                 return r;
 
@@ -324,6 +411,7 @@ _public_ int sd_bus_get_property_trivial(
         int r;
 
         bus_assert_return(bus, -EINVAL, error);
+        bus_assert_return(bus = bus_resolve(bus), -ENOPKG, error);
         bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error);
         bus_assert_return(member_name_is_valid(member), -EINVAL, error);
         bus_assert_return(bus_type_is_trivial(type), -EINVAL, error);
@@ -368,6 +456,7 @@ _public_ int sd_bus_get_property_string(
         int r;
 
         bus_assert_return(bus, -EINVAL, error);
+        bus_assert_return(bus = bus_resolve(bus), -ENOPKG, error);
         bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error);
         bus_assert_return(member_name_is_valid(member), -EINVAL, error);
         bus_assert_return(ret, -EINVAL, error);
@@ -416,6 +505,7 @@ _public_ int sd_bus_get_property_strv(
         int r;
 
         bus_assert_return(bus, -EINVAL, error);
+        bus_assert_return(bus = bus_resolve(bus), -ENOPKG, error);
         bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error);
         bus_assert_return(member_name_is_valid(member), -EINVAL, error);
         bus_assert_return(ret, -EINVAL, error);
@@ -444,20 +534,20 @@ fail:
         return sd_bus_error_set_errno(error, r);
 }
 
-_public_ int sd_bus_set_property(
+_public_ int sd_bus_set_propertyv(
                 sd_bus *bus,
                 const char *destination,
                 const char *path,
                 const char *interface,
                 const char *member,
                 sd_bus_error *error,
-                const char *type, ...) {
+                const char *type, va_list ap) {
 
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
-        va_list ap;
         int r;
 
         bus_assert_return(bus, -EINVAL, error);
+        bus_assert_return(bus = bus_resolve(bus), -ENOPKG, error);
         bus_assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL, error);
         bus_assert_return(member_name_is_valid(member), -EINVAL, error);
         bus_assert_return(signature_is_single(type, false), -EINVAL, error);
@@ -480,9 +570,7 @@ _public_ int sd_bus_set_property(
         if (r < 0)
                 goto fail;
 
-        va_start(ap, type);
         r = sd_bus_message_appendv(m, type, ap);
-        va_end(ap);
         if (r < 0)
                 goto fail;
 
@@ -496,6 +584,25 @@ fail:
         return sd_bus_error_set_errno(error, r);
 }
 
+_public_ int sd_bus_set_property(
+                sd_bus *bus,
+                const char *destination,
+                const char *path,
+                const char *interface,
+                const char *member,
+                sd_bus_error *error,
+                const char *type, ...) {
+
+        va_list ap;
+        int r;
+
+        va_start(ap, type);
+        r = sd_bus_set_propertyv(bus, destination, path, interface, member, error, type, ap);
+        va_end(ap);
+
+        return r;
+}
+
 _public_ int sd_bus_query_sender_creds(sd_bus_message *call, uint64_t mask, sd_bus_creds **creds) {
         sd_bus_creds *c;
 
index 908b9e75b25429a572de963e90d09c5cc8b504fc..2740be9226453d849d5b227bd3e09150e060ea1f 100644 (file)
@@ -650,16 +650,15 @@ _public_ int sd_bus_creds_get_description(sd_bus_creds *c, const char **ret) {
 }
 
 static int has_cap(sd_bus_creds *c, size_t offset, int capability) {
-        unsigned long lc;
         size_t sz;
 
         assert(c);
         assert(capability >= 0);
         assert(c->capability);
 
-        lc = cap_last_cap();
+        unsigned lc = cap_last_cap();
 
-        if ((unsigned long) capability > lc)
+        if ((unsigned) capability > lc)
                 return 0;
 
         /* If the last cap is 63, then there are 64 caps defined, and we need 2 entries á 32bit hence. *
index caab5e5ebe65c873649021dbccd0addb55ae86f2..94107c297f4c042d93c0faf3371452e552041d70 100644 (file)
@@ -56,7 +56,7 @@ _public_ int sd_bus_message_dump(sd_bus_message *m, FILE *f, uint64_t flags) {
 
         if (flags & SD_BUS_MESSAGE_DUMP_WITH_HEADER) {
                 fprintf(f,
-                        "%s%s%s Type=%s%s%s  Endian=%c  Flags=%u  Version=%u  Priority=%"PRIi64,
+                        "%s%s%s Type=%s%s%s  Endian=%c  Flags=%u  Version=%u",
                         m->header->type == SD_BUS_MESSAGE_METHOD_ERROR ? ansi_highlight_red() :
                         m->header->type == SD_BUS_MESSAGE_METHOD_RETURN ? ansi_highlight_green() :
                         m->header->type != SD_BUS_MESSAGE_SIGNAL ? ansi_highlight() : "",
@@ -69,8 +69,7 @@ _public_ int sd_bus_message_dump(sd_bus_message *m, FILE *f, uint64_t flags) {
 
                         m->header->endian,
                         m->header->flags,
-                        m->header->version,
-                        m->priority);
+                        m->header->version);
 
                 /* Display synthetic message serial number in a more readable
                  * format than (uint32_t) -1 */
index d5f8c6db647d50ce181582d3b3d7a3ae660ab432..5c3e955c20c83abce597b0582575792f15b832e0 100644 (file)
@@ -314,13 +314,9 @@ char *bus_address_escape(const char *v) {
 int bus_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error) {
         assert(m);
 
-        if (r < 0) {
+        if (sd_bus_error_is_set(error) || r < 0) {
                 if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL)
                         sd_bus_reply_method_errno(m, r, error);
-
-        } else if (sd_bus_error_is_set(error)) {
-                if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL)
-                        sd_bus_reply_method_error(m, error);
         } else
                 return r;
 
index 352a419e25d76516d762e7a98d6e97acf1239af3..ef2c3dbc4fde6f034fc841bb0f308fb4843df5ee 100644 (file)
@@ -43,7 +43,7 @@ struct match_callback {
 
         unsigned last_iteration;
 
-        /* Don't dispatch this slot with with messages that arrived in any iteration before or at the this
+        /* Don't dispatch this slot with messages that arrived in any iteration before or at the this
          * one. We use this to ensure that matches don't apply "retroactively" and thus can confuse the
          * caller: matches will only match incoming messages from the moment on the match was installed. */
         uint64_t after;
@@ -354,6 +354,7 @@ bool interface_name_is_valid(const char *p) _pure_;
 bool service_name_is_valid(const char *p) _pure_;
 bool member_name_is_valid(const char *p) _pure_;
 bool object_path_is_valid(const char *p) _pure_;
+
 char *object_path_startswith(const char *a, const char *b) _pure_;
 
 bool namespace_complex_pattern(const char *pattern, const char *value) _pure_;
index e8934489b581f1b07a35609cbedeb35fb628aea6..734abcf3fdedac8b15304f62102fd828adee7ad0 100644 (file)
 #include "memory-util.h"
 #include "string-util.h"
 
+#define BUS_INTROSPECT_DOCTYPE                                       \
+        "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n" \
+        "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
+
+#define BUS_INTROSPECT_INTERFACE_PEER                                \
+        " <interface name=\"org.freedesktop.DBus.Peer\">\n"             \
+        "  <method name=\"Ping\"/>\n"                                   \
+        "  <method name=\"GetMachineId\">\n"                            \
+        "   <arg type=\"s\" name=\"machine_uuid\" direction=\"out\"/>\n" \
+        "  </method>\n"                                                 \
+        " </interface>\n"
+
+#define BUS_INTROSPECT_INTERFACE_INTROSPECTABLE                      \
+        " <interface name=\"org.freedesktop.DBus.Introspectable\">\n"   \
+        "  <method name=\"Introspect\">\n"                              \
+        "   <arg name=\"data\" type=\"s\" direction=\"out\"/>\n"        \
+        "  </method>\n"                                                 \
+        " </interface>\n"
+
+#define BUS_INTROSPECT_INTERFACE_PROPERTIES                          \
+        " <interface name=\"org.freedesktop.DBus.Properties\">\n"       \
+        "  <method name=\"Get\">\n"                                     \
+        "   <arg name=\"interface\" direction=\"in\" type=\"s\"/>\n"    \
+        "   <arg name=\"property\" direction=\"in\" type=\"s\"/>\n"     \
+        "   <arg name=\"value\" direction=\"out\" type=\"v\"/>\n"       \
+        "  </method>\n"                                                 \
+        "  <method name=\"GetAll\">\n"                                  \
+        "   <arg name=\"interface\" direction=\"in\" type=\"s\"/>\n"    \
+        "   <arg name=\"properties\" direction=\"out\" type=\"a{sv}\"/>\n" \
+        "  </method>\n"                                                 \
+        "  <method name=\"Set\">\n"                                     \
+        "   <arg name=\"interface\" direction=\"in\" type=\"s\"/>\n"    \
+        "   <arg name=\"property\" direction=\"in\" type=\"s\"/>\n"     \
+        "   <arg name=\"value\" direction=\"in\" type=\"v\"/>\n"        \
+        "  </method>\n"                                                 \
+        "  <signal name=\"PropertiesChanged\">\n"                       \
+        "   <arg type=\"s\" name=\"interface\"/>\n"                     \
+        "   <arg type=\"a{sv}\" name=\"changed_properties\"/>\n"        \
+        "   <arg type=\"as\" name=\"invalidated_properties\"/>\n"       \
+        "  </signal>\n"                                                 \
+        " </interface>\n"
+
+#define BUS_INTROSPECT_INTERFACE_OBJECT_MANAGER                      \
+        " <interface name=\"org.freedesktop.DBus.ObjectManager\">\n"    \
+        "  <method name=\"GetManagedObjects\">\n"                       \
+        "   <arg type=\"a{oa{sa{sv}}}\" name=\"object_paths_interfaces_and_properties\" direction=\"out\"/>\n" \
+        "  </method>\n"                                                 \
+        "  <signal name=\"InterfacesAdded\">\n"                         \
+        "   <arg type=\"o\" name=\"object_path\"/>\n"                   \
+        "   <arg type=\"a{sa{sv}}\" name=\"interfaces_and_properties\"/>\n" \
+        "  </signal>\n"                                                 \
+        "  <signal name=\"InterfacesRemoved\">\n"                       \
+        "   <arg type=\"o\" name=\"object_path\"/>\n"                   \
+        "   <arg type=\"as\" name=\"interfaces\"/>\n"                   \
+        "  </signal>\n"                                                 \
+        " </interface>\n"
+
 int introspect_begin(struct introspect *i, bool trusted) {
         assert(i);
 
-        zero(*i);
-        i->trusted = trusted;
+        *i = (struct introspect) {
+                .trusted = trusted,
+        };
 
         i->f = open_memstream_unlocked(&i->introspection, &i->size);
         if (!i->f)
@@ -39,12 +97,27 @@ int introspect_write_default_interfaces(struct introspect *i, bool object_manage
         return 0;
 }
 
+static int set_interface_name(struct introspect *intro, const char *interface_name) {
+        if (streq_ptr(intro->interface_name, interface_name))
+                return 0;
+
+        if (intro->interface_name)
+                fputs(" </interface>\n", intro->f);
+
+        if (interface_name)
+                fprintf(intro->f, " <interface name=\"%s\">\n", interface_name);
+
+        return free_and_strdup(&intro->interface_name, interface_name);
+}
+
 int introspect_write_child_nodes(struct introspect *i, Set *s, const char *prefix) {
         char *node;
 
         assert(i);
         assert(prefix);
 
+        assert_se(set_interface_name(i, NULL) >= 0);
+
         while ((node = set_steal_first(s))) {
                 const char *e;
 
@@ -114,13 +187,23 @@ static int introspect_write_arguments(struct introspect *i, const char *signatur
         }
 }
 
-int introspect_write_interface(struct introspect *i, const sd_bus_vtable *v) {
+int introspect_write_interface(
+                struct introspect *i,
+                const char *interface_name,
+                const sd_bus_vtable *v) {
+
         const sd_bus_vtable *vtable = v;
         const char *names = "";
+        int r;
 
         assert(i);
+        assert(interface_name);
         assert(v);
 
+        r = set_interface_name(i, interface_name);
+        if (r < 0)
+                return r;
+
         for (; v->type != _SD_BUS_VTABLE_END; v = bus_vtable_next(vtable, v)) {
 
                 /* Ignore methods, signals and properties that are
@@ -177,6 +260,8 @@ int introspect_finish(struct introspect *i, char **ret) {
 
         assert(i);
 
+        assert_se(set_interface_name(i, NULL) >= 0);
+
         fputs("</node>\n", i->f);
 
         r = fflush_and_check(i->f);
@@ -195,5 +280,6 @@ void introspect_free(struct introspect *i) {
         /* Normally introspect_finish() does all the work, this is just a backup for error paths */
 
         safe_fclose(i->f);
+        free(i->interface_name);
         free(i->introspection);
 }
index ccbb543d0c9d491133fae5327885ba4a2d950365..19d39923e5d7922c8a1cd0bfaf75e641df192ece 100644 (file)
@@ -9,6 +9,7 @@
 
 struct introspect {
         FILE *f;
+        char *interface_name;
         char *introspection;
         size_t size;
         bool trusted;
@@ -17,6 +18,9 @@ struct introspect {
 int introspect_begin(struct introspect *i, bool trusted);
 int introspect_write_default_interfaces(struct introspect *i, bool object_manager);
 int introspect_write_child_nodes(struct introspect *i, Set *s, const char *prefix);
-int introspect_write_interface(struct introspect *i, const sd_bus_vtable *v);
+int introspect_write_interface(
+                struct introspect *i,
+                const char *interface_name,
+                const sd_bus_vtable *v);
 int introspect_finish(struct introspect *i, char **ret);
 void introspect_free(struct introspect *i);
index 8b00a49ed2c8de274bea82b60b8718e67f647774..55e35cd902ee960e20bb13271aa69ce0e0df89c5 100644 (file)
@@ -46,7 +46,7 @@ static void message_free_part(sd_bus_message *m, struct bus_body_part *part) {
         assert(part);
 
         if (part->memfd >= 0) {
-                /* erase if requested, but ony if the memfd is not sealed yet, i.e. is writable */
+                /* erase if requested, but only if the memfd is not sealed yet, i.e. is writable */
                 if (m->sensitive && !m->sealed)
                         explicit_bzero_safe(part->data, part->size);
 
@@ -451,7 +451,7 @@ int bus_message_from_header(
         if (!IN_SET(h->version, 1, 2))
                 return -EBADMSG;
 
-        if (h->type <= _SD_BUS_MESSAGE_TYPE_INVALID || h->type >= _SD_BUS_MESSAGE_TYPE_MAX)
+        if (h->type == _SD_BUS_MESSAGE_TYPE_INVALID)
                 return -EBADMSG;
 
         if (!IN_SET(h->endian, BUS_LITTLE_ENDIAN, BUS_BIG_ENDIAN))
@@ -585,14 +585,14 @@ _public_ int sd_bus_message_new(
                 sd_bus_message **m,
                 uint8_t type) {
 
-        sd_bus_message *t;
-
         assert_return(bus, -ENOTCONN);
+        assert_return(bus = bus_resolve(bus), -ENOPKG);
         assert_return(bus->state != BUS_UNSET, -ENOTCONN);
         assert_return(m, -EINVAL);
-        assert_return(type > _SD_BUS_MESSAGE_TYPE_INVALID && type < _SD_BUS_MESSAGE_TYPE_MAX, -EINVAL);
+        /* Creation of messages with _SD_BUS_MESSAGE_TYPE_INVALID is allowed. */
+        assert_return(type < _SD_BUS_MESSAGE_TYPE_MAX, -EINVAL);
 
-        t = malloc0(ALIGN(sizeof(sd_bus_message)) + sizeof(struct bus_header));
+        sd_bus_message *t = malloc0(ALIGN(sizeof(sd_bus_message)) + sizeof(struct bus_header));
         if (!t)
                 return -ENOMEM;
 
@@ -623,6 +623,7 @@ _public_ int sd_bus_message_new_signal(
         int r;
 
         assert_return(bus, -ENOTCONN);
+        assert_return(bus = bus_resolve(bus), -ENOPKG);
         assert_return(bus->state != BUS_UNSET, -ENOTCONN);
         assert_return(object_path_is_valid(path), -EINVAL);
         assert_return(interface_name_is_valid(interface), -EINVAL);
@@ -663,6 +664,7 @@ _public_ int sd_bus_message_new_method_call(
         int r;
 
         assert_return(bus, -ENOTCONN);
+        assert_return(bus = bus_resolve(bus), -ENOPKG);
         assert_return(bus->state != BUS_UNSET, -ENOTCONN);
         assert_return(!destination || service_name_is_valid(destination), -EINVAL);
         assert_return(object_path_is_valid(path), -EINVAL);
@@ -3020,7 +3022,7 @@ int bus_body_part_map(struct bus_body_part *part) {
                 return 0;
         }
 
-        shift = part->memfd_offset - ((part->memfd_offset / page_size()) * page_size());
+        shift = PAGE_OFFSET(part->memfd_offset);
         psz = PAGE_ALIGN(part->size + shift);
 
         if (part->memfd >= 0)
@@ -5497,9 +5499,6 @@ int bus_message_parse_fields(sd_bus_message *m) {
                 if (m->reply_cookie == 0 || !m->error.name)
                         return -EBADMSG;
                 break;
-
-        default:
-                assert_not_reached("Bad message type");
         }
 
         /* Refuse non-local messages that claim they are local */
@@ -5931,18 +5930,31 @@ int bus_message_remarshal(sd_bus *bus, sd_bus_message **m) {
 }
 
 _public_ int sd_bus_message_get_priority(sd_bus_message *m, int64_t *priority) {
+        static bool warned = false;
+
         assert_return(m, -EINVAL);
         assert_return(priority, -EINVAL);
 
-        *priority = m->priority;
+        if (!warned) {
+                log_debug("sd_bus_message_get_priority() is deprecated and always returns 0.");
+                warned = true;
+        }
+
+        *priority = 0;
         return 0;
 }
 
 _public_ int sd_bus_message_set_priority(sd_bus_message *m, int64_t priority) {
+        static bool warned = false;
+
         assert_return(m, -EINVAL);
         assert_return(!m->sealed, -EPERM);
 
-        m->priority = priority;
+        if (!warned) {
+                log_debug("sd_bus_message_set_priority() is deprecated and does nothing.");
+                warned = true;
+        }
+
         return 0;
 }
 
index a88a531e15b82d8b5050f16c7cf61e8cd3b8bbcc..5d869213ab80f0a0d70c9f73ae83b0901628cd57 100644 (file)
@@ -76,7 +76,6 @@ struct sd_bus_message {
         usec_t monotonic;
         usec_t realtime;
         uint64_t seqnum;
-        int64_t priority;
         uint64_t verify_destination_id;
 
         bool sealed:1;
index 04d6adddc5d8ed30268e8847c892233a266dbbd0..6abac8822c6eed5792529f5ca7cb09b67af58f7d 100644 (file)
@@ -56,7 +56,7 @@ static int node_vtable_get_userdata(
 static void *vtable_method_convert_userdata(const sd_bus_vtable *p, void *u) {
         assert(p);
 
-        if (!u)
+        if (!u || FLAGS_SET(p->flags, SD_BUS_VTABLE_ABSOLUTE_OFFSET))
                 return SIZE_TO_PTR(p->x.method.offset); /* don't add offset on NULL, to make ubsan happy */
 
         return (uint8_t*) u + p->x.method.offset;
@@ -65,7 +65,7 @@ static void *vtable_method_convert_userdata(const sd_bus_vtable *p, void *u) {
 static void *vtable_property_convert_userdata(const sd_bus_vtable *p, void *u) {
         assert(p);
 
-        if (!u)
+        if (!u || FLAGS_SET(p->flags, SD_BUS_VTABLE_ABSOLUTE_OFFSET))
                 return SIZE_TO_PTR(p->x.property.offset); /* as above */
 
         return (uint8_t*) u + p->x.property.offset;
@@ -780,7 +780,7 @@ static int vtable_append_all_properties(
                 if (v->flags & SD_BUS_VTABLE_HIDDEN)
                         continue;
 
-                /* Let's not include properties marked as "explicit" in any message that contians a generic
+                /* Let's not include properties marked as "explicit" in any message that contains a generic
                  * dump of properties, but only in those generated as a response to an explicit request. */
                 if (v->flags & SD_BUS_VTABLE_PROPERTY_EXPLICIT)
                         continue;
@@ -827,10 +827,10 @@ static int property_get_all_callbacks_run(
         if (r < 0)
                 return r;
 
-        found_interface = !iface ||
-                streq(iface, "org.freedesktop.DBus.Properties") ||
-                streq(iface, "org.freedesktop.DBus.Peer") ||
-                streq(iface, "org.freedesktop.DBus.Introspectable");
+        found_interface = !iface || STR_IN_SET(iface,
+                                               "org.freedesktop.DBus.Properties",
+                                               "org.freedesktop.DBus.Peer",
+                                               "org.freedesktop.DBus.Introspectable");
 
         LIST_FOREACH(vtables, c, first) {
                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
@@ -939,7 +939,6 @@ int introspect_path(
                 sd_bus_error *error) {
 
         _cleanup_set_free_free_ Set *s = NULL;
-        const char *previous_interface = NULL;
         _cleanup_(introspect_free) struct introspect intro = {};
         struct node_vtable *c;
         bool empty;
@@ -984,23 +983,11 @@ int introspect_path(
                 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
                         continue;
 
-                if (!streq_ptr(previous_interface, c->interface)) {
-                        if (previous_interface)
-                                fputs(" </interface>\n", intro.f);
-
-                        fprintf(intro.f, " <interface name=\"%s\">\n", c->interface);
-                }
-
-                r = introspect_write_interface(&intro, c->vtable);
+                r = introspect_write_interface(&intro, c->interface, c->vtable);
                 if (r < 0)
                         return r;
-
-                previous_interface = c->interface;
         }
 
-        if (previous_interface)
-                fputs(" </interface>\n", intro.f);
-
         if (empty) {
                 /* Nothing?, let's see if we exist at all, and if not
                  * refuse to do anything */
index d01fd8e6a3fd71f3c91b3d141e8d3dc94a1b11b1..7e1cd3c31e33d288d268596529e62af81f41ee7d 100644 (file)
@@ -103,60 +103,3 @@ enum {
         BUS_START_REPLY_SUCCESS = 1,
         BUS_START_REPLY_ALREADY_RUNNING = 2,
 };
-
-#define BUS_INTROSPECT_DOCTYPE                                       \
-        "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n" \
-        "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
-
-#define BUS_INTROSPECT_INTERFACE_PEER                                \
-        " <interface name=\"org.freedesktop.DBus.Peer\">\n"             \
-        "  <method name=\"Ping\"/>\n"                                   \
-        "  <method name=\"GetMachineId\">\n"                            \
-        "   <arg type=\"s\" name=\"machine_uuid\" direction=\"out\"/>\n" \
-        "  </method>\n"                                                 \
-        " </interface>\n"
-
-#define BUS_INTROSPECT_INTERFACE_INTROSPECTABLE                      \
-        " <interface name=\"org.freedesktop.DBus.Introspectable\">\n"   \
-        "  <method name=\"Introspect\">\n"                              \
-        "   <arg name=\"data\" type=\"s\" direction=\"out\"/>\n"        \
-        "  </method>\n"                                                 \
-        " </interface>\n"
-
-#define BUS_INTROSPECT_INTERFACE_PROPERTIES                          \
-        " <interface name=\"org.freedesktop.DBus.Properties\">\n"       \
-        "  <method name=\"Get\">\n"                                     \
-        "   <arg name=\"interface\" direction=\"in\" type=\"s\"/>\n"    \
-        "   <arg name=\"property\" direction=\"in\" type=\"s\"/>\n"     \
-        "   <arg name=\"value\" direction=\"out\" type=\"v\"/>\n"       \
-        "  </method>\n"                                                 \
-        "  <method name=\"GetAll\">\n"                                  \
-        "   <arg name=\"interface\" direction=\"in\" type=\"s\"/>\n"    \
-        "   <arg name=\"properties\" direction=\"out\" type=\"a{sv}\"/>\n" \
-        "  </method>\n"                                                 \
-        "  <method name=\"Set\">\n"                                     \
-        "   <arg name=\"interface\" direction=\"in\" type=\"s\"/>\n"    \
-        "   <arg name=\"property\" direction=\"in\" type=\"s\"/>\n"     \
-        "   <arg name=\"value\" direction=\"in\" type=\"v\"/>\n"        \
-        "  </method>\n"                                                 \
-        "  <signal name=\"PropertiesChanged\">\n"                       \
-        "   <arg type=\"s\" name=\"interface\"/>\n"                     \
-        "   <arg type=\"a{sv}\" name=\"changed_properties\"/>\n"        \
-        "   <arg type=\"as\" name=\"invalidated_properties\"/>\n"       \
-        "  </signal>\n"                                                 \
-        " </interface>\n"
-
-#define BUS_INTROSPECT_INTERFACE_OBJECT_MANAGER                      \
-        " <interface name=\"org.freedesktop.DBus.ObjectManager\">\n"    \
-        "  <method name=\"GetManagedObjects\">\n"                       \
-        "   <arg type=\"a{oa{sa{sv}}}\" name=\"object_paths_interfaces_and_properties\" direction=\"out\"/>\n" \
-        "  </method>\n"                                                 \
-        "  <signal name=\"InterfacesAdded\">\n"                         \
-        "   <arg type=\"o\" name=\"object_path\"/>\n"                   \
-        "   <arg type=\"a{sa{sv}}\" name=\"interfaces_and_properties\"/>\n" \
-        "  </signal>\n"                                                 \
-        "  <signal name=\"InterfacesRemoved\">\n"                       \
-        "   <arg type=\"o\" name=\"object_path\"/>\n"                   \
-        "   <arg type=\"as\" name=\"interfaces\"/>\n"                   \
-        "  </signal>\n"                                                 \
-        " </interface>\n"
index 0adfc97be3805a50bfc951133d83b59a40ecdc82..fc7e8e844abda5ed0864b3632894137b220d5710 100644 (file)
@@ -136,11 +136,10 @@ static int bus_socket_write_auth(sd_bus *b) {
         if (b->prefer_writev)
                 k = writev(b->output_fd, b->auth_iovec + b->auth_index, ELEMENTSOF(b->auth_iovec) - b->auth_index);
         else {
-                struct msghdr mh;
-                zero(mh);
-
-                mh.msg_iov = b->auth_iovec + b->auth_index;
-                mh.msg_iovlen = ELEMENTSOF(b->auth_iovec) - b->auth_index;
+                struct msghdr mh = {
+                        .msg_iov = b->auth_iovec + b->auth_index,
+                        .msg_iovlen = ELEMENTSOF(b->auth_iovec) - b->auth_index,
+                };
 
                 k = sendmsg(b->output_fd, &mh, MSG_DONTWAIT|MSG_NOSIGNAL);
                 if (k < 0 && errno == ENOTSOCK) {
@@ -519,10 +518,7 @@ static int bus_socket_read_auth(sd_bus *b) {
         ssize_t k;
         int r;
         void *p;
-        union {
-                struct cmsghdr cmsghdr;
-                uint8_t buf[CMSG_SPACE(sizeof(int) * BUS_FDS_MAX)];
-        } control;
+        CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int) * BUS_FDS_MAX)) control;
         bool handle_cmsg = false;
 
         assert(b);
@@ -551,11 +547,12 @@ static int bus_socket_read_auth(sd_bus *b) {
         if (b->prefer_readv)
                 k = readv(b->input_fd, &iov, 1);
         else {
-                zero(mh);
-                mh.msg_iov = &iov;
-                mh.msg_iovlen = 1;
-                mh.msg_control = &control;
-                mh.msg_controllen = sizeof(control);
+                mh = (struct msghdr) {
+                        .msg_iov = &iov,
+                        .msg_iovlen = 1,
+                        .msg_control = &control,
+                        .msg_controllen = sizeof(control),
+                };
 
                 k = recvmsg_safe(b->input_fd, &mh, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
                 if (k == -ENOTSOCK) {
@@ -1169,10 +1166,7 @@ int bus_socket_read_message(sd_bus *bus) {
         size_t need;
         int r;
         void *b;
-        union {
-                struct cmsghdr cmsghdr;
-                uint8_t buf[CMSG_SPACE(sizeof(int) * BUS_FDS_MAX)];
-        } control;
+        CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int) * BUS_FDS_MAX)) control;
         bool handle_cmsg = false;
 
         assert(bus);
@@ -1196,11 +1190,12 @@ int bus_socket_read_message(sd_bus *bus) {
         if (bus->prefer_readv)
                 k = readv(bus->input_fd, &iov, 1);
         else {
-                zero(mh);
-                mh.msg_iov = &iov;
-                mh.msg_iovlen = 1;
-                mh.msg_control = &control;
-                mh.msg_controllen = sizeof(control);
+                mh = (struct msghdr) {
+                        .msg_iov = &iov,
+                        .msg_iovlen = 1,
+                        .msg_control = &control,
+                        .msg_controllen = sizeof(control),
+                };
 
                 k = recvmsg_safe(bus->input_fd, &mh, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
                 if (k == -ENOTSOCK) {
@@ -1267,21 +1262,15 @@ int bus_socket_read_message(sd_bus *bus) {
 }
 
 int bus_socket_process_opening(sd_bus *b) {
-        int error = 0;
+        int error = 0, events, r;
         socklen_t slen = sizeof(error);
-        struct pollfd p = {
-                .fd = b->output_fd,
-                .events = POLLOUT,
-        };
-        int r;
 
         assert(b->state == BUS_OPENING);
 
-        r = poll(&p, 1, 0);
-        if (r < 0)
-                return -errno;
-
-        if (!(p.revents & (POLLOUT|POLLERR|POLLHUP)))
+        events = fd_wait_for_event(b->output_fd, POLLOUT, 0);
+        if (events < 0)
+                return events;
+        if (!(events & (POLLOUT|POLLERR|POLLHUP)))
                 return 0;
 
         r = getsockopt(b->output_fd, SOL_SOCKET, SO_ERROR, &error, &slen);
@@ -1289,7 +1278,7 @@ int bus_socket_process_opening(sd_bus *b) {
                 b->last_connect_error = errno;
         else if (error != 0)
                 b->last_connect_error = error;
-        else if (p.revents & (POLLERR|POLLHUP))
+        else if (events & (POLLERR|POLLHUP))
                 b->last_connect_error = ECONNREFUSED;
         else
                 return bus_socket_start_auth(b);
index f6098549254e1a45868b6e62021c21e8135aa68d..13fd52ffd5ed0ad714126166cce5985307ede492 100644 (file)
@@ -6,6 +6,7 @@
 #include "bus-internal.h"
 #include "bus-track.h"
 #include "bus-util.h"
+#include "string-util.h"
 
 struct track_item {
         unsigned n_ref;
index 18564a53834a1d71ade295c0ef901dabc3d65cfb..585f8424b3f85c9ae4cfcdb785284b7bf25b6029 100644 (file)
@@ -4,6 +4,7 @@
 
 #include "sd-bus.h"
 
+#include "bus-internal.h"
 #include "bus-type.h"
 
 bool bus_type_is_valid(char c) {
@@ -135,3 +136,27 @@ int bus_type_get_size(char c) {
 
         return -EINVAL;
 }
+
+_public_ int sd_bus_interface_name_is_valid(const char *p) {
+        assert_return(p, -EINVAL);
+
+        return interface_name_is_valid(p);
+}
+
+_public_ int sd_bus_service_name_is_valid(const char *p) {
+        assert_return(p, -EINVAL);
+
+        return service_name_is_valid(p);
+}
+
+_public_ int sd_bus_member_name_is_valid(const char *p) {
+        assert_return(p, -EINVAL);
+
+        return member_name_is_valid(p);
+}
+
+_public_ int sd_bus_object_path_is_valid(const char *p) {
+        assert_return(p, -EINVAL);
+
+        return object_path_is_valid(p);
+}
index 7ad03680f48d80d44adcf720412bbfa0e0fde887..9de5e454a6b1f41c585b6cbda8c3e3809d030aab 100644 (file)
@@ -284,7 +284,7 @@ _public_ int sd_bus_set_fd(sd_bus *bus, int input_fd, int output_fd) {
         return 0;
 }
 
-_public_ int sd_bus_set_exec(sd_bus *bus, const char *path, char *const argv[]) {
+_public_ int sd_bus_set_exec(sd_bus *bus, const char *path, char *const *argv) {
         _cleanup_strv_free_ char **a = NULL;
         int r;
 
@@ -504,7 +504,6 @@ static int synthesize_connected_signal(sd_bus *bus) {
 }
 
 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",
@@ -980,9 +979,8 @@ static int parse_container_unix_address(sd_bus *b, const char **p, char **guid)
                         return -EINVAL;
 
                 free_and_replace(b->machine, machine);
-        } else {
+        } else
                 b->machine = mfree(b->machine);
-        }
 
         if (pid) {
                 r = parse_pid(pid, &b->nspid);
@@ -1268,13 +1266,16 @@ _public_ int sd_bus_open(sd_bus **ret) {
 
 int bus_set_address_system(sd_bus *b) {
         const char *e;
+        int r;
+
         assert(b);
 
         e = secure_getenv("DBUS_SYSTEM_BUS_ADDRESS");
-        if (e)
-                return sd_bus_set_address(b, e);
 
-        return sd_bus_set_address(b, DEFAULT_SYSTEM_BUS_ADDRESS);
+        r = sd_bus_set_address(b, e ?: DEFAULT_SYSTEM_BUS_ADDRESS);
+        if (r >= 0)
+                b->is_system = true;
+        return r;
 }
 
 _public_ int sd_bus_open_system_with_description(sd_bus **ret, const char *description) {
@@ -1298,7 +1299,6 @@ _public_ int sd_bus_open_system_with_description(sd_bus **ret, const char *descr
                 return r;
 
         b->bus_client = true;
-        b->is_system = true;
 
         /* Let's do per-method access control on the system bus. We
          * need the caller's UID and capability set for that. */
@@ -1319,29 +1319,34 @@ _public_ int sd_bus_open_system(sd_bus **ret) {
 }
 
 int bus_set_address_user(sd_bus *b) {
-        const char *e;
-        _cleanup_free_ char *ee = NULL, *s = NULL;
+        const char *a;
+        _cleanup_free_ char *_a = NULL;
+        int r;
 
         assert(b);
 
-        e = secure_getenv("DBUS_SESSION_BUS_ADDRESS");
-        if (e)
-                return sd_bus_set_address(b, e);
-
-        e = secure_getenv("XDG_RUNTIME_DIR");
-        if (!e)
-                return -ENOENT;
+        a = secure_getenv("DBUS_SESSION_BUS_ADDRESS");
+        if (!a) {
+                const char *e;
+                _cleanup_free_ char *ee = NULL;
 
-        ee = bus_address_escape(e);
-        if (!ee)
-                return -ENOMEM;
+                e = secure_getenv("XDG_RUNTIME_DIR");
+                if (!e)
+                        return -ENOENT;
 
-        if (asprintf(&s, DEFAULT_USER_BUS_ADDRESS_FMT, ee) < 0)
-                return -ENOMEM;
+                ee = bus_address_escape(e);
+                if (!ee)
+                        return -ENOMEM;
 
-        b->address = TAKE_PTR(s);
+                if (asprintf(&_a, DEFAULT_USER_BUS_ADDRESS_FMT, ee) < 0)
+                        return -ENOMEM;
+                a = _a;
+        }
 
-        return 0;
+        r = sd_bus_set_address(b, a);
+        if (r >= 0)
+                b->is_user = true;
+        return r;
 }
 
 _public_ int sd_bus_open_user_with_description(sd_bus **ret, const char *description) {
@@ -1365,7 +1370,6 @@ _public_ int sd_bus_open_user_with_description(sd_bus **ret, const char *descrip
                 return r;
 
         b->bus_client = true;
-        b->is_user = true;
 
         /* We don't do any per-method access control on the user bus. */
         b->trusted = true;
@@ -1837,7 +1841,7 @@ static int dispatch_wqueue(sd_bus *bus) {
         return ret;
 }
 
-static int bus_read_message(sd_bus *bus, bool hint_priority, int64_t priority) {
+static int bus_read_message(sd_bus *bus) {
         assert(bus);
 
         return bus_socket_read_message(bus);
@@ -1864,17 +1868,13 @@ static void rqueue_drop_one(sd_bus *bus, size_t i) {
         bus->rqueue_size--;
 }
 
-static int dispatch_rqueue(sd_bus *bus, bool hint_priority, int64_t priority, sd_bus_message **m) {
+static int dispatch_rqueue(sd_bus *bus, sd_bus_message **m) {
         int r, ret = 0;
 
         assert(bus);
         assert(m);
         assert(IN_SET(bus->state, BUS_RUNNING, BUS_HELLO));
 
-        /* Note that the priority logic is only available on kdbus,
-         * where the rqueue is unused. We check the rqueue here
-         * anyway, because it's simple... */
-
         for (;;) {
                 if (bus->rqueue_size > 0) {
                         /* Dispatch a queued message */
@@ -1884,7 +1884,7 @@ static int dispatch_rqueue(sd_bus *bus, bool hint_priority, int64_t priority, sd
                 }
 
                 /* Try to read a new message */
-                r = bus_read_message(bus, hint_priority, priority);
+                r = bus_read_message(bus);
                 if (r < 0)
                         return r;
                 if (r == 0) {
@@ -1902,9 +1902,10 @@ _public_ int sd_bus_send(sd_bus *bus, sd_bus_message *_m, uint64_t *cookie) {
 
         assert_return(m, -EINVAL);
 
-        if (!bus)
-                bus = m->bus;
-
+        if (bus)
+                assert_return(bus = bus_resolve(bus), -ENOPKG);
+        else
+                assert_return(bus = m->bus, -ENOTCONN);
         assert_return(!bus_pid_changed(bus), -ECHILD);
 
         if (!BUS_IS_OPEN(bus->state))
@@ -1986,9 +1987,10 @@ _public_ int sd_bus_send_to(sd_bus *bus, sd_bus_message *m, const char *destinat
 
         assert_return(m, -EINVAL);
 
-        if (!bus)
-                bus = m->bus;
-
+        if (bus)
+                assert_return(bus = bus_resolve(bus), -ENOPKG);
+        else
+                assert_return(bus = m->bus, -ENOTCONN);
         assert_return(!bus_pid_changed(bus), -ECHILD);
 
         if (!BUS_IS_OPEN(bus->state))
@@ -2051,9 +2053,10 @@ _public_ int sd_bus_call_async(
         assert_return(m->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
         assert_return(!m->sealed || (!!callback == !(m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)), -EINVAL);
 
-        if (!bus)
-                bus = m->bus;
-
+        if (bus)
+                assert_return(bus = bus_resolve(bus), -ENOPKG);
+        else
+                assert_return(bus = m->bus, -ENOTCONN);
         assert_return(!bus_pid_changed(bus), -ECHILD);
 
         if (!BUS_IS_OPEN(bus->state))
@@ -2157,9 +2160,10 @@ _public_ int sd_bus_call(
         bus_assert_return(!(m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED), -EINVAL, error);
         bus_assert_return(!bus_error_is_dirty(error), -EINVAL, error);
 
-        if (!bus)
-                bus = m->bus;
-
+        if (bus)
+                assert_return(bus = bus_resolve(bus), -ENOPKG);
+        else
+                assert_return(bus = m->bus, -ENOTCONN);
         bus_assert_return(!bus_pid_changed(bus), -ECHILD, error);
 
         if (!BUS_IS_OPEN(bus->state)) {
@@ -2237,7 +2241,7 @@ _public_ int sd_bus_call(
                         i++;
                 }
 
-                r = bus_read_message(bus, false, 0);
+                r = bus_read_message(bus);
                 if (r < 0) {
                         if (ERRNO_IS_DISCONNECT(r)) {
                                 bus_enter_closing(bus);
@@ -2286,7 +2290,6 @@ 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);
@@ -2777,7 +2780,7 @@ static int dispatch_track(sd_bus *bus) {
         return 1;
 }
 
-static int process_running(sd_bus *bus, bool hint_priority, int64_t priority, sd_bus_message **ret) {
+static int process_running(sd_bus *bus, sd_bus_message **ret) {
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
         int r;
 
@@ -2796,7 +2799,7 @@ static int process_running(sd_bus *bus, bool hint_priority, int64_t priority, sd
         if (r != 0)
                 goto null_message;
 
-        r = dispatch_rqueue(bus, hint_priority, priority, &m);
+        r = dispatch_rqueue(bus, &m);
         if (r < 0)
                 return r;
         if (!m)
@@ -2982,7 +2985,7 @@ finish:
         return r;
 }
 
-static int bus_process_internal(sd_bus *bus, bool hint_priority, int64_t priority, sd_bus_message **ret) {
+static int bus_process_internal(sd_bus *bus, sd_bus_message **ret) {
         int r;
 
         /* Returns 0 when we didn't do anything. This should cause the
@@ -3022,7 +3025,7 @@ static int bus_process_internal(sd_bus *bus, bool hint_priority, int64_t priorit
 
         case BUS_RUNNING:
         case BUS_HELLO:
-                r = process_running(bus, hint_priority, priority, ret);
+                r = process_running(bus, ret);
                 if (r >= 0)
                         return r;
 
@@ -3049,11 +3052,11 @@ static int bus_process_internal(sd_bus *bus, bool hint_priority, int64_t priorit
 }
 
 _public_ int sd_bus_process(sd_bus *bus, sd_bus_message **ret) {
-        return bus_process_internal(bus, false, 0, ret);
+        return bus_process_internal(bus, ret);
 }
 
 _public_ int sd_bus_process_priority(sd_bus *bus, int64_t priority, sd_bus_message **ret) {
-        return bus_process_internal(bus, true, priority, ret);
+        return bus_process_internal(bus, ret);
 }
 
 static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec) {
@@ -3118,8 +3121,15 @@ static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec) {
         r = ppoll(p, n, m == USEC_INFINITY ? NULL : timespec_store(&ts, m), NULL);
         if (r < 0)
                 return -errno;
+        if (r == 0)
+                return 0;
+
+        if (p[0].revents & POLLNVAL)
+                return -EBADF;
+        if (n >= 2 && (p[1].revents & POLLNVAL))
+                return -EBADF;
 
-        return r > 0 ? 1 : 0;
+        return 1;
 }
 
 _public_ int sd_bus_wait(sd_bus *bus, uint64_t timeout_usec) {
@@ -3677,31 +3687,31 @@ _public_ int sd_bus_detach_event(sd_bus *bus) {
 }
 
 _public_ sd_event* sd_bus_get_event(sd_bus *bus) {
-        assert_return(bus, NULL);
+        assert_return(bus = bus_resolve(bus), NULL);
 
         return bus->event;
 }
 
 _public_ sd_bus_message* sd_bus_get_current_message(sd_bus *bus) {
-        assert_return(bus, NULL);
+        assert_return(bus = bus_resolve(bus), NULL);
 
         return bus->current_message;
 }
 
 _public_ sd_bus_slot* sd_bus_get_current_slot(sd_bus *bus) {
-        assert_return(bus, NULL);
+        assert_return(bus = bus_resolve(bus), NULL);
 
         return bus->current_slot;
 }
 
 _public_ sd_bus_message_handler_t sd_bus_get_current_handler(sd_bus *bus) {
-        assert_return(bus, NULL);
+        assert_return(bus = bus_resolve(bus), NULL);
 
         return bus->current_handler;
 }
 
 _public_ void* sd_bus_get_current_userdata(sd_bus *bus) {
-        assert_return(bus, NULL);
+        assert_return(bus = bus_resolve(bus), NULL);
 
         return bus->current_userdata;
 }
@@ -4018,7 +4028,6 @@ _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);
index 9c8d1434b139565c8888e77832d2f6a58ae8bdcb..cbc31589241c14b1517c6ae0b9017987e65d0025 100644 (file)
@@ -14,11 +14,11 @@ static void test_manual_introspection(const sd_bus_vtable vtable[]) {
 
         assert_se(introspect_begin(&intro, false) >= 0);
 
-        fprintf(intro.f, " <interface name=\"org.foo\">\n");
-        assert_se(introspect_write_interface(&intro, vtable) >= 0);
-        fputs(" </interface>\n", intro.f);
-
+        assert_se(introspect_write_interface(&intro, "org.foo", vtable) >= 0);
+        /* write again to check if output looks OK for a different interface */
+        assert_se(introspect_write_interface(&intro, "org.foo.bar", vtable) >= 0);
         assert_se(introspect_finish(&intro, &s) == 0);
+
         fputs(s, stdout);
         fputs("\n", stdout);
 }
index 4cd71cb2d3a022e6534fce90e6f0a89b589d5dfa..bbe7cd76d50366681d2e62da557fb2ae89635ff5 100644 (file)
@@ -4,6 +4,7 @@
 #include <limits.h>
 #include <mqueue.h>
 #include <netinet/in.h>
+#include <poll.h>
 #include <stdarg.h>
 #include <stddef.h>
 #include <stdio.h>
@@ -23,6 +24,7 @@
 #include "process-util.h"
 #include "socket-util.h"
 #include "strv.h"
+#include "time-util.h"
 #include "util.h"
 
 #define SNDBUF_SIZE (8*1024*1024)
@@ -551,6 +553,28 @@ finish:
         return r;
 }
 
+_public_ int sd_notify_barrier(int unset_environment, uint64_t timeout) {
+        _cleanup_close_pair_ int pipe_fd[2] = { -1, -1 };
+        int r;
+
+        if (pipe2(pipe_fd, O_CLOEXEC) < 0)
+                return -errno;
+
+        r = sd_pid_notify_with_fds(0, unset_environment, "BARRIER=1", &pipe_fd[1], 1);
+        if (r <= 0)
+                return r;
+
+        pipe_fd[1] = safe_close(pipe_fd[1]);
+
+        r = fd_wait_for_event(pipe_fd[0], 0 /* POLLHUP is implicit */, timeout);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return -ETIMEDOUT;
+
+        return 1;
+}
+
 _public_ int sd_pid_notify(pid_t pid, int unset_environment, const char *state) {
         return sd_pid_notify_with_fds(pid, unset_environment, state, NULL, 0);
 }
index a1932f41f9b7f811085dc1e2ecff36b8d028d426..95dfc2f07782f9f6c700e71a0082c45776a8c333 100644 (file)
@@ -71,14 +71,14 @@ static sd_device_enumerator *device_enumerator_free(sd_device_enumerator *enumer
                 sd_device_unref(enumerator->devices[i]);
 
         free(enumerator->devices);
-        set_free_free(enumerator->match_subsystem);
-        set_free_free(enumerator->nomatch_subsystem);
-        hashmap_free_free_free(enumerator->match_sysattr);
-        hashmap_free_free_free(enumerator->nomatch_sysattr);
-        hashmap_free_free_free(enumerator->match_property);
-        set_free_free(enumerator->match_sysname);
-        set_free_free(enumerator->match_tag);
-        set_free_free(enumerator->match_parent);
+        set_free(enumerator->match_subsystem);
+        set_free(enumerator->nomatch_subsystem);
+        hashmap_free(enumerator->match_sysattr);
+        hashmap_free(enumerator->nomatch_sysattr);
+        hashmap_free(enumerator->match_property);
+        set_free(enumerator->match_sysname);
+        set_free(enumerator->match_tag);
+        set_free(enumerator->match_parent);
 
         return mfree(enumerator);
 }
@@ -97,89 +97,49 @@ _public_ int sd_device_enumerator_add_match_subsystem(sd_device_enumerator *enum
         else
                 set = &enumerator->nomatch_subsystem;
 
-        r = set_ensure_allocated(set, NULL);
-        if (r < 0)
-                return r;
-
-        r = set_put_strdup(*set, subsystem);
-        if (r < 0)
+        r = set_put_strdup(set, subsystem);
+        if (r <= 0)
                 return r;
 
         enumerator->scan_uptodate = false;
 
-        return 0;
+        return 1;
 }
 
-_public_ int sd_device_enumerator_add_match_sysattr(sd_device_enumerator *enumerator, const char *_sysattr, const char *_value, int match) {
-        _cleanup_free_ char *sysattr = NULL, *value = NULL;
+_public_ int sd_device_enumerator_add_match_sysattr(sd_device_enumerator *enumerator, const char *sysattr, const char *value, int match) {
         Hashmap **hashmap;
         int r;
 
         assert_return(enumerator, -EINVAL);
-        assert_return(_sysattr, -EINVAL);
+        assert_return(sysattr, -EINVAL);
 
         if (match)
                 hashmap = &enumerator->match_sysattr;
         else
                 hashmap = &enumerator->nomatch_sysattr;
 
-        r = hashmap_ensure_allocated(hashmap, NULL);
-        if (r < 0)
-                return r;
-
-        sysattr = strdup(_sysattr);
-        if (!sysattr)
-                return -ENOMEM;
-
-        if (_value) {
-                value = strdup(_value);
-                if (!value)
-                        return -ENOMEM;
-        }
-
-        r = hashmap_put(*hashmap, sysattr, value);
-        if (r < 0)
+        r = hashmap_put_strdup(hashmap, sysattr, value);
+        if (r <= 0)
                 return r;
 
-        sysattr = NULL;
-        value = NULL;
-
         enumerator->scan_uptodate = false;
 
-        return 0;
+        return 1;
 }
 
-_public_ int sd_device_enumerator_add_match_property(sd_device_enumerator *enumerator, const char *_property, const char *_value) {
-        _cleanup_free_ char *property = NULL, *value = NULL;
+_public_ int sd_device_enumerator_add_match_property(sd_device_enumerator *enumerator, const char *property, const char *value) {
         int r;
 
         assert_return(enumerator, -EINVAL);
-        assert_return(_property, -EINVAL);
-
-        r = hashmap_ensure_allocated(&enumerator->match_property, NULL);
-        if (r < 0)
-                return r;
-
-        property = strdup(_property);
-        if (!property)
-                return -ENOMEM;
-
-        if (_value) {
-                value = strdup(_value);
-                if (!value)
-                        return -ENOMEM;
-        }
+        assert_return(property, -EINVAL);
 
-        r = hashmap_put(enumerator->match_property, property, value);
-        if (r < 0)
+        r = hashmap_put_strdup(&enumerator->match_property, property, value);
+        if (r <= 0)
                 return r;
 
-        property = NULL;
-        value = NULL;
-
         enumerator->scan_uptodate = false;
 
-        return 0;
+        return 1;
 }
 
 _public_ int sd_device_enumerator_add_match_sysname(sd_device_enumerator *enumerator, const char *sysname) {
@@ -188,17 +148,13 @@ _public_ int sd_device_enumerator_add_match_sysname(sd_device_enumerator *enumer
         assert_return(enumerator, -EINVAL);
         assert_return(sysname, -EINVAL);
 
-        r = set_ensure_allocated(&enumerator->match_sysname, NULL);
-        if (r < 0)
-                return r;
-
-        r = set_put_strdup(enumerator->match_sysname, sysname);
-        if (r < 0)
+        r = set_put_strdup(&enumerator->match_sysname, sysname);
+        if (r <= 0)
                 return r;
 
         enumerator->scan_uptodate = false;
 
-        return 0;
+        return 1;
 }
 
 _public_ int sd_device_enumerator_add_match_tag(sd_device_enumerator *enumerator, const char *tag) {
@@ -207,52 +163,41 @@ _public_ int sd_device_enumerator_add_match_tag(sd_device_enumerator *enumerator
         assert_return(enumerator, -EINVAL);
         assert_return(tag, -EINVAL);
 
-        r = set_ensure_allocated(&enumerator->match_tag, NULL);
-        if (r < 0)
-                return r;
-
-        r = set_put_strdup(enumerator->match_tag, tag);
-        if (r < 0)
+        r = set_put_strdup(&enumerator->match_tag, tag);
+        if (r <= 0)
                 return r;
 
         enumerator->scan_uptodate = false;
 
-        return 0;
-}
-
-static void device_enumerator_clear_match_parent(sd_device_enumerator *enumerator) {
-        if (!enumerator)
-                return;
-
-        set_clear_free(enumerator->match_parent);
+        return 1;
 }
 
 int device_enumerator_add_match_parent_incremental(sd_device_enumerator *enumerator, sd_device *parent) {
         const char *path;
         int r;
 
-        assert_return(enumerator, -EINVAL);
-        assert_return(parent, -EINVAL);
+        assert(enumerator);
+        assert(parent);
 
         r = sd_device_get_syspath(parent, &path);
         if (r < 0)
                 return r;
 
-        r = set_ensure_allocated(&enumerator->match_parent, NULL);
-        if (r < 0)
-                return r;
-
-        r = set_put_strdup(enumerator->match_parent, path);
-        if (r < 0)
+        r = set_put_strdup(&enumerator->match_parent, path);
+        if (r <= 0)
                 return r;
 
         enumerator->scan_uptodate = false;
 
-        return 0;
+        return 1;
 }
 
 _public_ int sd_device_enumerator_add_match_parent(sd_device_enumerator *enumerator, sd_device *parent) {
-        device_enumerator_clear_match_parent(enumerator);
+        assert_return(enumerator, -EINVAL);
+        assert_return(parent, -EINVAL);
+
+        set_clear(enumerator->match_parent);
+
         return device_enumerator_add_match_parent_incremental(enumerator, parent);
 }
 
@@ -263,7 +208,7 @@ _public_ int sd_device_enumerator_allow_uninitialized(sd_device_enumerator *enum
 
         enumerator->scan_uptodate = false;
 
-        return 0;
+        return 1;
 }
 
 int device_enumerator_add_match_is_initialized(sd_device_enumerator *enumerator) {
@@ -273,7 +218,7 @@ int device_enumerator_add_match_is_initialized(sd_device_enumerator *enumerator)
 
         enumerator->scan_uptodate = false;
 
-        return 0;
+        return 1;
 }
 
 static int device_compare(sd_device * const *_a, sd_device * const *_b) {
index 023fe0fcd702a36375f3c3631aab63fbceff2ba9..1fe5c1a6bf291b1c4dd2e09815dd8ab68b7832f9 100644 (file)
@@ -94,8 +94,8 @@ int device_read_uevent_file(sd_device *device);
 int device_set_syspath(sd_device *device, const char *_syspath, bool verify);
 int device_set_ifindex(sd_device *device, const char *ifindex);
 int device_set_devmode(sd_device *device, const char *devmode);
-int device_set_devname(sd_device *device, const char *_devname);
-int device_set_devtype(sd_device *device, const char *_devtype);
+int device_set_devname(sd_device *device, const char *devname);
+int device_set_devtype(sd_device *device, const char *devtype);
 int device_set_devnum(sd_device *device, const char *major, const char *minor);
 int device_set_subsystem(sd_device *device, const char *_subsystem);
 int device_set_driver(sd_device *device, const char *_driver);
index 42753abe0834aa3dbe8744c0694461de09dbe1f3..bed45da8e4e1d6ecaa7ad7e2aeea3d91c8f3a7fa 100644 (file)
@@ -3,6 +3,8 @@
 #include <errno.h>
 #include <linux/filter.h>
 #include <linux/netlink.h>
+#include <linux/sockios.h>
+#include <sys/ioctl.h>
 #include <unistd.h>
 
 #include "sd-device.h"
 #include "device-monitor-private.h"
 #include "device-private.h"
 #include "device-util.h"
+#include "errno-util.h"
 #include "fd-util.h"
 #include "format-util.h"
 #include "hashmap.h"
 #include "io-util.h"
+#include "missing_socket.h"
 #include "mountpoint-util.h"
 #include "set.h"
 #include "socket-util.h"
@@ -163,12 +167,54 @@ int device_monitor_new_full(sd_device_monitor **ret, MonitorNetlinkGroup group,
 
         if (fd >= 0) {
                 r = monitor_set_nl_address(m);
-                if (r < 0)
-                        return log_debug_errno(r, "sd-device-monitor: Failed to set netlink address: %m");
+                if (r < 0) {
+                        log_debug_errno(r, "sd-device-monitor: Failed to set netlink address: %m");
+                        goto fail;
+                }
+        }
+
+        if (DEBUG_LOGGING) {
+                _cleanup_close_ int netns = -1;
+
+                /* So here's the thing: only AF_NETLINK sockets from the main network namespace will get
+                 * hardware events. Let's check if ours is from there, and if not generate a debug message,
+                 * since we cannot possibly work correctly otherwise. This is just a safety check to make
+                 * things easier to debug. */
+
+                netns = ioctl(m->sock, SIOCGSKNS);
+                if (netns < 0)
+                        log_debug_errno(errno, "sd-device-monitor: Unable to get network namespace of udev netlink socket, unable to determine if we are in host netns: %m");
+                else {
+                        struct stat a, b;
+
+                        if (fstat(netns, &a) < 0) {
+                                r = log_debug_errno(errno, "sd-device-monitor: Failed to stat netns of udev netlink socket: %m");
+                                goto fail;
+                        }
+
+                        if (stat("/proc/1/ns/net", &b) < 0) {
+                                if (ERRNO_IS_PRIVILEGE(errno))
+                                        /* If we can't access PID1's netns info due to permissions, it's fine, this is a
+                                         * safety check only after all. */
+                                        log_debug_errno(errno, "sd-device-monitor: No permission to stat PID1's netns, unable to determine if we are in host netns: %m");
+                                else
+                                        log_debug_errno(errno, "sd-device-monitor: Failed to stat PID1's netns: %m");
+
+                        } else if (a.st_dev != b.st_dev || a.st_ino != b.st_ino)
+                                log_debug("sd-device-monitor: Netlink socket we listen on is not from host netns, we won't see device events.");
+                }
         }
 
         *ret = TAKE_PTR(m);
         return 0;
+
+fail:
+        /* Let's unset the socket fd in the monitor object before we destroy it so that the fd passed in is
+         * not closed on failure. */
+        if (fd >= 0)
+                m->sock = -1;
+
+        return r;
 }
 
 _public_ int sd_device_monitor_new(sd_device_monitor **ret) {
@@ -361,13 +407,13 @@ int device_monitor_receive_device(sd_device_monitor *m, sd_device **ret) {
                 .iov_base = &buf,
                 .iov_len = sizeof(buf)
         };
-        char cred_msg[CMSG_SPACE(sizeof(struct ucred))];
+        CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control;
         union sockaddr_union snl;
         struct msghdr smsg = {
                 .msg_iov = &iov,
                 .msg_iovlen = 1,
-                .msg_control = cred_msg,
-                .msg_controllen = sizeof(cred_msg),
+                .msg_control = &control,
+                .msg_controllen = sizeof(control),
                 .msg_name = &snl,
                 .msg_namelen = sizeof(snl),
         };
@@ -512,10 +558,9 @@ int device_monitor_send_device(
         r = device_get_properties_nulstr(device, (const uint8_t **) &buf, &blen);
         if (r < 0)
                 return log_device_debug_errno(device, r, "sd-device-monitor: Failed to get device properties: %m");
-        if (blen < 32) {
-                log_device_debug(device, "sd-device-monitor: Length of device property nulstr is too small to contain valid device information");
-                return -EINVAL;
-        }
+        if (blen < 32)
+                log_device_debug_errno(device, SYNTHETIC_ERRNO(EINVAL),
+                                       "sd-device-monitor: Length of device property nulstr is too small to contain valid device information");
 
         /* fill in versioned header */
         r = sd_device_get_subsystem(device, &val);
@@ -713,30 +758,13 @@ _public_ int sd_device_monitor_filter_add_match_subsystem_devtype(sd_device_moni
 }
 
 _public_ int sd_device_monitor_filter_add_match_tag(sd_device_monitor *m, const char *tag) {
-        _cleanup_free_ char *t = NULL;
-        int r;
-
         assert_return(m, -EINVAL);
         assert_return(tag, -EINVAL);
 
-        t = strdup(tag);
-        if (!t)
-                return -ENOMEM;
-
-        r = set_ensure_allocated(&m->tag_filter, &string_hash_ops);
-        if (r < 0)
-                return r;
-
-        r = set_put(m->tag_filter, t);
-        if (r == -EEXIST)
-                return 0;
-        if (r < 0)
-                return r;
-
-        TAKE_PTR(t);
-        m->filter_uptodate = false;
-
-        return 0;
+        int r = set_put_strdup(&m->tag_filter, tag);
+        if (r > 0)
+                m->filter_uptodate = false;
+        return r;
 }
 
 _public_ int sd_device_monitor_filter_remove(sd_device_monitor *m) {
index 16f8627bdbc147a511f58d38b980f8180d0d442d..1e61732dfe6d15caf9c5be196d21518153b8f1f3 100644 (file)
@@ -363,10 +363,9 @@ static int device_append(sd_device *device, char *key, const char **_major, cons
         assert(_minor);
 
         value = strchr(key, '=');
-        if (!value) {
-                log_device_debug(device, "sd-device: Not a key-value pair: '%s'", key);
-                return -EINVAL;
-        }
+        if (!value)
+                return log_device_debug_errno(device, SYNTHETIC_ERRNO(EINVAL),
+                                              "sd-device: Not a key-value pair: '%s'", key);
 
         *value = '\0';
 
@@ -400,10 +399,9 @@ void device_seal(sd_device *device) {
 static int device_verify(sd_device *device) {
         assert(device);
 
-        if (!device->devpath || !device->subsystem || device->action < 0 || device->seqnum == 0) {
-                log_device_debug(device, "sd-device: Device created from strv or nulstr lacks devpath, subsystem, action or seqnum.");
-                return -EINVAL;
-        }
+        if (!device->devpath || !device->subsystem || device->action < 0 || device->seqnum == 0)
+                return log_device_debug_errno(device, SYNTHETIC_ERRNO(EINVAL),
+                                              "sd-device: Device created from strv or nulstr lacks devpath, subsystem, action or seqnum.");
 
         device->sealed = true;
 
@@ -464,10 +462,10 @@ int device_new_from_nulstr(sd_device **ret, uint8_t *nulstr, size_t len) {
 
                 key = (char*)&nulstr[i];
                 end = memchr(key, '\0', len - i);
-                if (!end) {
-                        log_device_debug(device, "sd-device: Failed to parse nulstr");
-                        return -EINVAL;
-                }
+                if (!end)
+                        return log_device_debug_errno(device, SYNTHETIC_ERRNO(EINVAL),
+                                                      "sd-device: Failed to parse nulstr");
+
                 i += end - key + 1;
 
                 r = device_append(device, key, &major, &minor);
index a25682d89360ff19ef447df35f7c1e05a155b4cb..1a1795d974fb53c7e6f18510b1c6c9fa36ad5ed3 100644 (file)
@@ -37,7 +37,7 @@
                 sd_device *_d = (device);                               \
                 int _level = (level), _error = (error);                 \
                                                                         \
-                if (_d && _unlikely_(log_get_max_level() >= _level))    \
+                if (_d && _unlikely_(log_get_max_level() >= LOG_PRI(_level))) \
                         (void) sd_device_get_sysname(_d, &_sysname);    \
                 log_object_internal(_level, _error, PROJECT_FILE, __LINE__, __func__, \
                                     _sysname ? "DEVICE=" : NULL, _sysname, \
index 1f2451f8e1b4e4d851571a19d858aedc9d1e9196..3bba17aff853b0684da9798b313e77d13a747015 100644 (file)
@@ -68,9 +68,9 @@ static sd_device *device_free(sd_device *device) {
         ordered_hashmap_free_free_free(device->properties);
         ordered_hashmap_free_free_free(device->properties_db);
         hashmap_free_free_free(device->sysattr_values);
-        set_free_free(device->sysattrs);
-        set_free_free(device->tags);
-        set_free_free(device->devlinks);
+        set_free(device->sysattrs);
+        set_free(device->tags);
+        set_free(device->devlinks);
 
         return mfree(device);
 }
@@ -320,24 +320,22 @@ _public_ int sd_device_new_from_subsystem_sysname(sd_device **ret, const char *s
         return -ENODEV;
 }
 
-int device_set_devtype(sd_device *device, const char *_devtype) {
-        _cleanup_free_ char *devtype = NULL;
+int device_set_devtype(sd_device *device, const char *devtype) {
+        _cleanup_free_ char *t = NULL;
         int r;
 
         assert(device);
-        assert(_devtype);
+        assert(devtype);
 
-        devtype = strdup(_devtype);
-        if (!devtype)
+        t = strdup(devtype);
+        if (!t)
                 return -ENOMEM;
 
-        r = device_add_property_internal(device, "DEVTYPE", devtype);
+        r = device_add_property_internal(device, "DEVTYPE", t);
         if (r < 0)
                 return r;
 
-        free_and_replace(device->devtype, devtype);
-
-        return 0;
+        return free_and_replace(device->devtype, t);
 }
 
 int device_set_ifindex(sd_device *device, const char *name) {
@@ -359,30 +357,25 @@ int device_set_ifindex(sd_device *device, const char *name) {
         return 0;
 }
 
-int device_set_devname(sd_device *device, const char *_devname) {
-        _cleanup_free_ char *devname = NULL;
+int device_set_devname(sd_device *device, const char *devname) {
+        _cleanup_free_ char *t = NULL;
         int r;
 
         assert(device);
-        assert(_devname);
+        assert(devname);
 
-        if (_devname[0] != '/') {
-                r = asprintf(&devname, "/dev/%s", _devname);
-                if (r < 0)
-                        return -ENOMEM;
-        } else {
-                devname = strdup(_devname);
-                if (!devname)
-                        return -ENOMEM;
-        }
+        if (devname[0] != '/')
+                t = strjoin("/dev/", devname);
+        else
+                t = strdup(devname);
+        if (!t)
+                return -ENOMEM;
 
-        r = device_add_property_internal(device, "DEVNAME", devname);
+        r = device_add_property_internal(device, "DEVNAME", t);
         if (r < 0)
                 return r;
 
-        free_and_replace(device->devname, devname);
-
-        return 0;
+        return free_and_replace(device->devname, t);
 }
 
 int device_set_devmode(sd_device *device, const char *_devmode) {
@@ -1078,11 +1071,7 @@ int device_add_tag(sd_device *device, const char *tag) {
         if (!is_valid_tag(tag))
                 return -EINVAL;
 
-        r = set_ensure_allocated(&device->tags, &string_hash_ops);
-        if (r < 0)
-                return r;
-
-        r = set_put_strdup(device->tags, tag);
+        r = set_put_strdup(&device->tags, tag);
         if (r < 0)
                 return r;
 
@@ -1098,11 +1087,7 @@ int device_add_devlink(sd_device *device, const char *devlink) {
         assert(device);
         assert(devlink);
 
-        r = set_ensure_allocated(&device->devlinks, &string_hash_ops);
-        if (r < 0)
-                return r;
-
-        r = set_put_strdup(device->devlinks, devlink);
+        r = set_put_strdup(&device->devlinks, devlink);
         if (r < 0)
                 return r;
 
@@ -1258,17 +1243,15 @@ int device_get_id_filename(sd_device *device, const char **ret) {
                         if (!subsystem)
                                 return -EINVAL;
 
-                        if (streq(subsystem, "drivers")) {
+
+                        if (streq(subsystem, "drivers"))
                                 /* the 'drivers' pseudo-subsystem is special, and needs the real subsystem
                                  * encoded as well */
-                                r = asprintf(&id, "+drivers:%s:%s", device->driver_subsystem, sysname);
-                                if (r < 0)
-                                        return -ENOMEM;
-                        } else {
-                                r = asprintf(&id, "+%s:%s", subsystem, sysname);
-                                if (r < 0)
-                                        return -ENOMEM;
-                        }
+                                id = strjoin("+drivers:", device->driver_subsystem, ":", sysname);
+                        else
+                                id = strjoin("+", subsystem, ":", sysname);
+                        if (!id)
+                                return -ENOMEM;
                 }
 
                 device->id_filename = TAKE_PTR(id);
@@ -1572,38 +1555,68 @@ _public_ const char *sd_device_get_property_next(sd_device *device, const char *
         return key;
 }
 
-static int device_sysattrs_read_all(sd_device *device) {
+static int device_sysattrs_read_all_internal(sd_device *device, const char *subdir) {
+        _cleanup_free_ char *path_dir = NULL;
         _cleanup_closedir_ DIR *dir = NULL;
-        const char *syspath;
         struct dirent *dent;
+        const char *syspath;
         int r;
 
-        assert(device);
-
-        if (device->sysattrs_read)
-                return 0;
-
         r = sd_device_get_syspath(device, &syspath);
         if (r < 0)
                 return r;
 
-        dir = opendir(syspath);
+        if (subdir) {
+                _cleanup_free_ char *p = NULL;
+
+                p = path_join(syspath, subdir, "uevent");
+                if (!p)
+                        return -ENOMEM;
+
+                if (access(p, F_OK) >= 0)
+                        /* this is a child device, skipping */
+                        return 0;
+                if (errno != ENOENT) {
+                        log_device_debug_errno(device, errno, "sd-device: Failed to stat %s, ignoring subdir: %m", p);
+                        return 0;
+                }
+
+                path_dir = path_join(syspath, subdir);
+                if (!path_dir)
+                        return -ENOMEM;
+        }
+
+        dir = opendir(path_dir ?: syspath);
         if (!dir)
                 return -errno;
 
-        r = set_ensure_allocated(&device->sysattrs, &string_hash_ops);
-        if (r < 0)
-                return r;
-
         FOREACH_DIRENT_ALL(dent, dir, return -errno) {
-                _cleanup_free_ char *path = NULL;
+                _cleanup_free_ char *path = NULL, *p = NULL;
                 struct stat statbuf;
 
-                /* only handle symlinks and regular files */
-                if (!IN_SET(dent->d_type, DT_LNK, DT_REG))
+                if (dot_or_dot_dot(dent->d_name))
                         continue;
 
-                path = path_join(syspath, dent->d_name);
+                /* only handle symlinks, regular files, and directories */
+                if (!IN_SET(dent->d_type, DT_LNK, DT_REG, DT_DIR))
+                        continue;
+
+                if (subdir) {
+                        p = path_join(subdir, dent->d_name);
+                        if (!p)
+                                return -ENOMEM;
+                }
+
+                if (dent->d_type == DT_DIR) {
+                        /* read subdirectory */
+                        r = device_sysattrs_read_all_internal(device, p ?: dent->d_name);
+                        if (r < 0)
+                                return r;
+
+                        continue;
+                }
+
+                path = path_join(syspath, p ?: dent->d_name);
                 if (!path)
                         return -ENOMEM;
 
@@ -1613,11 +1626,26 @@ static int device_sysattrs_read_all(sd_device *device) {
                 if (!(statbuf.st_mode & S_IRUSR))
                         continue;
 
-                r = set_put_strdup(device->sysattrs, dent->d_name);
+                r = set_put_strdup(&device->sysattrs, p ?: dent->d_name);
                 if (r < 0)
                         return r;
         }
 
+        return 0;
+}
+
+static int device_sysattrs_read_all(sd_device *device) {
+        int r;
+
+        assert(device);
+
+        if (device->sysattrs_read)
+                return 0;
+
+        r = device_sysattrs_read_all_internal(device, NULL);
+        if (r < 0)
+                return r;
+
         device->sysattrs_read = true;
 
         return 0;
index fb9db47105c06c01b5c5b396a9e064d6bfc2bca8..860eb048ff5be1975a3fdfb140fbaa25587dacb7 100644 (file)
@@ -1450,10 +1450,6 @@ _public_ int sd_event_add_post(
         assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
         assert_return(!event_pid_changed(e), -ECHILD);
 
-        r = set_ensure_allocated(&e->post_sources, NULL);
-        if (r < 0)
-                return r;
-
         s = source_new(e, !ret, SOURCE_POST);
         if (!s)
                 return -ENOMEM;
@@ -1462,9 +1458,10 @@ _public_ int sd_event_add_post(
         s->userdata = userdata;
         s->enabled = SD_EVENT_ON;
 
-        r = set_put(e->post_sources, s);
+        r = set_ensure_put(&e->post_sources, NULL, s);
         if (r < 0)
                 return r;
+        assert(r > 0);
 
         if (ret)
                 *ret = s;
index 3e96c98cd95a2b9a5afa807ebb248c6f9a140cde..746c895b6120dff18740123dca15e096f9aa3a3c 100644 (file)
@@ -746,7 +746,7 @@ static int seat_get_can(const char *seat, const char *variable) {
 }
 
 _public_ int sd_seat_can_multi_session(const char *seat) {
-        return seat_get_can(seat, "CAN_MULTI_SESSION");
+        return true;
 }
 
 _public_ int sd_seat_can_tty(const char *seat) {
index 49ed24727868db29acb1bbd045366f38070a52f7..c0c77e04714bc1c54d8a29f8e27445554845ad59 100644 (file)
@@ -142,8 +142,11 @@ static void test_login(void) {
 
                         log_info("sd_session_get_seat(\"%s\") → \"%s\"", session, seat);
 
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
                         r = sd_seat_can_multi_session(seat);
-                        assert_se(r >= 0);
+#pragma GCC diagnostic pop
+                        assert_se(r == 1);
                         log_info("sd_session_can_multi_seat(\"%s\") → %s", seat, yes_no(r));
 
                         r = sd_seat_can_tty(seat);
index 2648b338f5daa3afacb25b0c5ededd55cdc37dad..d6bf31efc7a196f3708fcbb2db1ea6c8f9700e65 100644 (file)
@@ -342,6 +342,74 @@ int sd_netlink_message_append_u64(sd_netlink_message *m, unsigned short type, ui
         return 0;
 }
 
+int sd_netlink_message_append_s8(sd_netlink_message *m, unsigned short type, int8_t data) {
+        int r;
+
+        assert_return(m, -EINVAL);
+        assert_return(!m->sealed, -EPERM);
+
+        r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_S8);
+        if (r < 0)
+                return r;
+
+        r = add_rtattr(m, type, &data, sizeof(int8_t));
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+int sd_netlink_message_append_s16(sd_netlink_message *m, unsigned short type, int16_t data) {
+        int r;
+
+        assert_return(m, -EINVAL);
+        assert_return(!m->sealed, -EPERM);
+
+        r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_S16);
+        if (r < 0)
+                return r;
+
+        r = add_rtattr(m, type, &data, sizeof(int16_t));
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+int sd_netlink_message_append_s32(sd_netlink_message *m, unsigned short type, int32_t data) {
+        int r;
+
+        assert_return(m, -EINVAL);
+        assert_return(!m->sealed, -EPERM);
+
+        r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_S32);
+        if (r < 0)
+                return r;
+
+        r = add_rtattr(m, type, &data, sizeof(int32_t));
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+int sd_netlink_message_append_s64(sd_netlink_message *m, unsigned short type, int64_t data) {
+        int r;
+
+        assert_return(m, -EINVAL);
+        assert_return(!m->sealed, -EPERM);
+
+        r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_S64);
+        if (r < 0)
+                return r;
+
+        r = add_rtattr(m, type, &data, sizeof(int64_t));
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
 int sd_netlink_message_append_data(sd_netlink_message *m, unsigned short type, const void *data, size_t len) {
         int r;
 
index 9401c43d05309d50733edb12dacede3295fd0393..bcd82fe164699868d2cf62253999b7bed9996820 100644 (file)
@@ -238,34 +238,29 @@ int socket_write_message(sd_netlink *nl, sd_netlink_message *m) {
         return k;
 }
 
-static int socket_recv_message(int fd, struct iovec *iov, uint32_t *_group, bool peek) {
+static int socket_recv_message(int fd, struct iovec *iov, uint32_t *ret_mcast_group, bool peek) {
         union sockaddr_union sender;
-        uint8_t cmsg_buffer[CMSG_SPACE(sizeof(struct nl_pktinfo))];
+        CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct nl_pktinfo))) control;
         struct msghdr msg = {
                 .msg_iov = iov,
                 .msg_iovlen = 1,
                 .msg_name = &sender,
                 .msg_namelen = sizeof(sender),
-                .msg_control = cmsg_buffer,
-                .msg_controllen = sizeof(cmsg_buffer),
+                .msg_control = &control,
+                .msg_controllen = sizeof(control),
         };
-        struct cmsghdr *cmsg;
-        uint32_t group = 0;
         ssize_t n;
 
         assert(fd >= 0);
         assert(iov);
 
-        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");
-                else if (errno == EAGAIN)
-                        log_debug("rtnl: no data in socket");
-
-                return IN_SET(errno, EAGAIN, EINTR) ? 0 : -errno;
-        }
+        n = recvmsg_safe(fd, &msg, MSG_TRUNC | (peek ? MSG_PEEK : 0));
+        if (n == -ENOBUFS)
+                return log_debug_errno(n, "rtnl: kernel receive buffer overrun");
+        if (IN_SET(n, -EAGAIN, -EINTR))
+                return 0;
+        if (n < 0)
+                return (int) n;
 
         if (sender.nl.nl_pid != 0) {
                 /* not from the kernel, ignore */
@@ -273,28 +268,24 @@ static int socket_recv_message(int fd, struct iovec *iov, uint32_t *_group, bool
 
                 if (peek) {
                         /* drop the message */
-                        n = recvmsg(fd, &msg, 0);
+                        n = recvmsg_safe(fd, &msg, 0);
                         if (n < 0)
-                                return IN_SET(errno, EAGAIN, EINTR) ? 0 : -errno;
+                                return (int) n;
                 }
 
                 return 0;
         }
 
-        CMSG_FOREACH(cmsg, &msg) {
-                if (cmsg->cmsg_level == SOL_NETLINK &&
-                    cmsg->cmsg_type == NETLINK_PKTINFO &&
-                    cmsg->cmsg_len == CMSG_LEN(sizeof(struct nl_pktinfo))) {
-                        struct nl_pktinfo *pktinfo = (void *)CMSG_DATA(cmsg);
+        if (ret_mcast_group) {
+                struct nl_pktinfo *pi;
 
-                        /* multi-cast group */
-                        group = pktinfo->group;
-                }
+                pi = CMSG_FIND_DATA(&msg, SOL_NETLINK, NETLINK_PKTINFO, struct nl_pktinfo);
+                if (pi)
+                        *ret_mcast_group = pi->group;
+                else
+                        *ret_mcast_group = 0;
         }
 
-        if (_group)
-                *_group = group;
-
         return (int) n;
 }
 
index e35127a4cd2ed97526cc3d1b5898dc722afa62dd..060458a534c0a161a053c58256105e1886e3f88e 100644 (file)
@@ -93,9 +93,20 @@ static const NLType rtnl_link_info_data_ipvlan_types[] = {
         [IFLA_IPVLAN_FLAGS]  = { .type = NETLINK_TYPE_U16 },
 };
 
+static const NLType rtnl_macvlan_macaddr_types[] = {
+        [IFLA_MACVLAN_MACADDR] = { .type = NETLINK_TYPE_ETHER_ADDR },
+};
+
+static const NLTypeSystem rtnl_macvlan_macaddr_type_system = {
+        .count = ELEMENTSOF(rtnl_macvlan_macaddr_types),
+        .types = rtnl_macvlan_macaddr_types,
+};
+
 static const NLType rtnl_link_info_data_macvlan_types[] = {
         [IFLA_MACVLAN_MODE]  = { .type = NETLINK_TYPE_U32 },
         [IFLA_MACVLAN_FLAGS] = { .type = NETLINK_TYPE_U16 },
+        [IFLA_MACVLAN_MACADDR_MODE] = { .type = NETLINK_TYPE_U32 },
+        [IFLA_MACVLAN_MACADDR_DATA] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_macvlan_macaddr_type_system },
 };
 
 static const NLType rtnl_link_info_data_bridge_types[] = {
@@ -316,6 +327,7 @@ static const NLType rtnl_link_info_data_can_types[] = {
         [IFLA_CAN_BITTIMING]            = { .size = sizeof(struct can_bittiming) },
         [IFLA_CAN_RESTART_MS]           = { .type = NETLINK_TYPE_U32 },
         [IFLA_CAN_CTRLMODE]             = { .size = sizeof(struct can_ctrlmode) },
+        [IFLA_CAN_TERMINATION]          = { .type = NETLINK_TYPE_U16 },
 };
 
 static const NLType rtnl_link_info_data_macsec_types[] = {
@@ -536,15 +548,50 @@ static const NLTypeSystem rtnl_prop_list_type_system = {
         .types = rtnl_prop_list_types,
 };
 
+static const NLType rtnl_vf_vlan_list_types[] = {
+        [IFLA_VF_VLAN_INFO]  = { .size = sizeof(struct ifla_vf_vlan_info) },
+};
+
+static const NLTypeSystem rtnl_vf_vlan_type_system = {
+        .count = ELEMENTSOF(rtnl_vf_vlan_list_types),
+        .types = rtnl_vf_vlan_list_types,
+};
+
+static const NLType rtnl_vf_vlan_info_types[] = {
+        [IFLA_VF_MAC]           = { .size = sizeof(struct ifla_vf_mac) },
+        [IFLA_VF_VLAN]          = { .size = sizeof(struct ifla_vf_vlan) },
+        [IFLA_VF_VLAN_LIST]     = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_vf_vlan_type_system},
+        [IFLA_VF_TX_RATE]       = { .size = sizeof(struct ifla_vf_tx_rate) },
+        [IFLA_VF_SPOOFCHK]      = { .size = sizeof(struct ifla_vf_spoofchk) },
+        [IFLA_VF_RATE]          = { .size = sizeof(struct ifla_vf_rate) },
+        [IFLA_VF_LINK_STATE]    = { .size = sizeof(struct ifla_vf_link_state) },
+        [IFLA_VF_RSS_QUERY_EN]  = { .size = sizeof(struct ifla_vf_rss_query_en) },
+        [IFLA_VF_TRUST]         = { .size = sizeof(struct ifla_vf_trust) },
+        [IFLA_VF_IB_NODE_GUID]  = { .size = sizeof(struct ifla_vf_guid) },
+        [IFLA_VF_IB_PORT_GUID]  = { .size = sizeof(struct ifla_vf_guid) },
+};
+
+static const NLTypeSystem rtnl_vf_vlan_info_type_system = {
+        .count = ELEMENTSOF(rtnl_vf_vlan_info_types),
+        .types = rtnl_vf_vlan_info_types,
+};
+
+static const NLType rtnl_link_io_srv_types[] = {
+        [IFLA_VF_INFO] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_vf_vlan_info_type_system },
+};
+
+static const NLTypeSystem rtnl_io_srv_type_system = {
+        .count = ELEMENTSOF(rtnl_link_io_srv_types),
+        .types = rtnl_link_io_srv_types,
+};
+
 static const NLType rtnl_link_types[] = {
         [IFLA_ADDRESS]          = { .type = NETLINK_TYPE_ETHER_ADDR },
         [IFLA_BROADCAST]        = { .type = NETLINK_TYPE_ETHER_ADDR },
         [IFLA_IFNAME]           = { .type = NETLINK_TYPE_STRING, .size = IFNAMSIZ - 1 },
         [IFLA_MTU]              = { .type = NETLINK_TYPE_U32 },
         [IFLA_LINK]             = { .type = NETLINK_TYPE_U32 },
-/*
-        [IFLA_QDISC],
-*/
+        [IFLA_QDISC]            = { .type = NETLINK_TYPE_STRING },
         [IFLA_STATS]            = { .size = sizeof(struct rtnl_link_stats) },
 /*
         [IFLA_COST],
@@ -565,10 +612,8 @@ static const NLType rtnl_link_types[] = {
         [IFLA_LINKINFO]         = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_info_type_system },
         [IFLA_NET_NS_PID]       = { .type = NETLINK_TYPE_U32 },
         [IFLA_IFALIAS]          = { .type = NETLINK_TYPE_STRING, .size = IFALIASZ - 1 },
-/*
-        [IFLA_NUM_VF],
-        [IFLA_VFINFO_LIST]      = {. type = NETLINK_TYPE_NESTED, },
-*/
+        [IFLA_NUM_VF]           = { .type = NETLINK_TYPE_U32 },
+        [IFLA_VFINFO_LIST]      = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_io_srv_type_system },
         [IFLA_STATS64]          = { .size = sizeof(struct rtnl_link_stats64) },
 /*
         [IFLA_VF_PORTS]         = { .type = NETLINK_TYPE_NESTED },
@@ -745,6 +790,12 @@ static const NLTypeSystem rtnl_nexthop_type_system = {
        .types = rtnl_nexthop_types,
 };
 
+static const NLType rtnl_tca_option_data_cake_types[] = {
+        [TCA_CAKE_BASE_RATE64] = { .type = NETLINK_TYPE_U64 },
+        [TCA_CAKE_OVERHEAD]    = { .type = NETLINK_TYPE_S32 },
+        [TCA_CAKE_MPU]         = { .type = NETLINK_TYPE_U32 },
+};
+
 static const NLType rtnl_tca_option_data_codel_types[] = {
         [TCA_CODEL_TARGET]        = { .type = NETLINK_TYPE_U32 },
         [TCA_CODEL_LIMIT]         = { .type = NETLINK_TYPE_U32 },
@@ -753,6 +804,36 @@ static const NLType rtnl_tca_option_data_codel_types[] = {
         [TCA_CODEL_CE_THRESHOLD]  = { .type = NETLINK_TYPE_U32 },
 };
 
+static const NLType rtnl_tca_option_data_drr_types[] = {
+        [TCA_DRR_QUANTUM] = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLType rtnl_tca_option_data_ets_quanta_types[] = {
+        [TCA_ETS_QUANTA_BAND] = { .type = NETLINK_TYPE_U32, },
+};
+
+static const NLTypeSystem rtnl_tca_option_data_ets_quanta_type_system = {
+        .count = ELEMENTSOF(rtnl_tca_option_data_ets_quanta_types),
+        .types = rtnl_tca_option_data_ets_quanta_types,
+};
+
+static const NLType rtnl_tca_option_data_ets_prio_types[] = {
+        [TCA_ETS_PRIOMAP_BAND] = { .type = NETLINK_TYPE_U8, },
+};
+
+static const NLTypeSystem rtnl_tca_option_data_ets_prio_type_system = {
+        .count = ELEMENTSOF(rtnl_tca_option_data_ets_prio_types),
+        .types = rtnl_tca_option_data_ets_prio_types,
+};
+
+static const NLType rtnl_tca_option_data_ets_types[] = {
+        [TCA_ETS_NBANDS]      = { .type = NETLINK_TYPE_U8 },
+        [TCA_ETS_NSTRICT]     = { .type = NETLINK_TYPE_U8 },
+        [TCA_ETS_QUANTA]      = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_option_data_ets_quanta_type_system },
+        [TCA_ETS_PRIOMAP]     = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_option_data_ets_prio_type_system },
+        [TCA_ETS_QUANTA_BAND] = { .type = NETLINK_TYPE_U32 },
+};
+
 static const NLType rtnl_tca_option_data_fq_types[] = {
         [TCA_FQ_PLIMIT]             = { .type = NETLINK_TYPE_U32 },
         [TCA_FQ_FLOW_PLIMIT]        = { .type = NETLINK_TYPE_U32 },
@@ -780,6 +861,36 @@ static const NLType rtnl_tca_option_data_fq_codel_types[] = {
         [TCA_FQ_CODEL_MEMORY_LIMIT]    = { .type = NETLINK_TYPE_U32 },
 };
 
+static const NLType rtnl_tca_option_data_gred_types[] = {
+        [TCA_GRED_DPS] = { .size = sizeof(struct tc_gred_sopt) },
+};
+
+static const NLType rtnl_tca_option_data_hhf_types[] = {
+        [TCA_HHF_BACKLOG_LIMIT] = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLType rtnl_tca_option_data_htb_types[] = {
+        [TCA_HTB_PARMS]  = { .size = sizeof(struct tc_htb_opt) },
+        [TCA_HTB_INIT]   = { .size = sizeof(struct tc_htb_glob) },
+        [TCA_HTB_CTAB]   = { .size = TC_RTAB_SIZE },
+        [TCA_HTB_RTAB]   = { .size = TC_RTAB_SIZE },
+        [TCA_HTB_RATE64] = { .type = NETLINK_TYPE_U64 },
+        [TCA_HTB_CEIL64] = { .type = NETLINK_TYPE_U64 },
+};
+
+static const NLType rtnl_tca_option_data_pie_types[] = {
+        [TCA_PIE_LIMIT]   = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLType rtnl_tca_option_data_qfq_types[] = {
+        [TCA_QFQ_WEIGHT] = { .type = NETLINK_TYPE_U32 },
+        [TCA_QFQ_LMAX]   = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLType rtnl_tca_option_data_sfb_types[] = {
+        [TCA_SFB_PARMS] = { .size = sizeof(struct tc_sfb_qopt) },
+};
+
 static const NLType rtnl_tca_option_data_tbf_types[] = {
         [TCA_TBF_PARMS]   = { .size = sizeof(struct tc_tbf_qopt) },
         [TCA_TBF_RTAB]    = { .size = TC_RTAB_SIZE },
@@ -791,21 +902,48 @@ static const NLType rtnl_tca_option_data_tbf_types[] = {
 };
 
 static const char* const nl_union_tca_option_data_table[] = {
+        [NL_UNION_TCA_OPTION_DATA_CAKE] = "cake",
         [NL_UNION_TCA_OPTION_DATA_CODEL] = "codel",
+        [NL_UNION_TCA_OPTION_DATA_DRR] = "drr",
+        [NL_UNION_TCA_OPTION_DATA_ETS] = "ets",
         [NL_UNION_TCA_OPTION_DATA_FQ] = "fq",
         [NL_UNION_TCA_OPTION_DATA_FQ_CODEL] = "fq_codel",
+        [NL_UNION_TCA_OPTION_DATA_GRED] = "gred",
+        [NL_UNION_TCA_OPTION_DATA_HHF] = "hhf",
+        [NL_UNION_TCA_OPTION_DATA_HTB] = "htb",
+        [NL_UNION_TCA_OPTION_DATA_PIE] = "pie",
+        [NL_UNION_TCA_OPTION_DATA_QFQ] = "qfq",
+        [NL_UNION_TCA_OPTION_DATA_SFB] = "sfb",
         [NL_UNION_TCA_OPTION_DATA_TBF] = "tbf",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(nl_union_tca_option_data, NLUnionTCAOptionData);
 
 static const NLTypeSystem rtnl_tca_option_data_type_systems[] = {
+        [NL_UNION_TCA_OPTION_DATA_CAKE] =        { .count = ELEMENTSOF(rtnl_tca_option_data_cake_types),
+                                                   .types = rtnl_tca_option_data_cake_types },
         [NL_UNION_TCA_OPTION_DATA_CODEL] =       { .count = ELEMENTSOF(rtnl_tca_option_data_codel_types),
                                                    .types = rtnl_tca_option_data_codel_types },
+        [NL_UNION_TCA_OPTION_DATA_DRR] =         { .count = ELEMENTSOF(rtnl_tca_option_data_drr_types),
+                                                   .types = rtnl_tca_option_data_drr_types },
+        [NL_UNION_TCA_OPTION_DATA_ETS] =         { .count = ELEMENTSOF(rtnl_tca_option_data_ets_types),
+                                                   .types = rtnl_tca_option_data_ets_types },
         [NL_UNION_TCA_OPTION_DATA_FQ] =          { .count = ELEMENTSOF(rtnl_tca_option_data_fq_types),
                                                    .types = rtnl_tca_option_data_fq_types },
         [NL_UNION_TCA_OPTION_DATA_FQ_CODEL] =    { .count = ELEMENTSOF(rtnl_tca_option_data_fq_codel_types),
                                                    .types = rtnl_tca_option_data_fq_codel_types },
+        [NL_UNION_TCA_OPTION_DATA_GRED] =        { .count = ELEMENTSOF(rtnl_tca_option_data_gred_types),
+                                                   .types = rtnl_tca_option_data_gred_types },
+        [NL_UNION_TCA_OPTION_DATA_HHF] =         { .count = ELEMENTSOF(rtnl_tca_option_data_hhf_types),
+                                                   .types = rtnl_tca_option_data_hhf_types },
+        [NL_UNION_TCA_OPTION_DATA_HTB] =         { .count = ELEMENTSOF(rtnl_tca_option_data_htb_types),
+                                                   .types = rtnl_tca_option_data_htb_types },
+        [NL_UNION_TCA_OPTION_DATA_PIE] =         { .count = ELEMENTSOF(rtnl_tca_option_data_pie_types),
+                                                   .types = rtnl_tca_option_data_pie_types },
+        [NL_UNION_TCA_OPTION_DATA_QFQ] =         { .count = ELEMENTSOF(rtnl_tca_option_data_qfq_types),
+                                                   .types = rtnl_tca_option_data_qfq_types },
+        [NL_UNION_TCA_OPTION_DATA_SFB] =         { .count = ELEMENTSOF(rtnl_tca_option_data_sfb_types),
+                                                   .types = rtnl_tca_option_data_sfb_types },
         [NL_UNION_TCA_OPTION_DATA_TBF] =         { .count = ELEMENTSOF(rtnl_tca_option_data_tbf_types),
                                                    .types = rtnl_tca_option_data_tbf_types },
 };
@@ -818,16 +956,16 @@ static const NLTypeSystemUnion rtnl_tca_option_data_type_system_union = {
         .match = TCA_KIND,
 };
 
-static const NLType rtnl_qdisc_types[] = {
+static const NLType rtnl_tca_types[] = {
         [TCA_KIND]           = { .type = NETLINK_TYPE_STRING },
         [TCA_OPTIONS]        = { .type = NETLINK_TYPE_UNION, .type_system_union = &rtnl_tca_option_data_type_system_union },
         [TCA_INGRESS_BLOCK]  = { .type = NETLINK_TYPE_U32 },
         [TCA_EGRESS_BLOCK]   = { .type = NETLINK_TYPE_U32 },
 };
 
-static const NLTypeSystem rtnl_qdisc_type_system = {
-        .count = ELEMENTSOF(rtnl_qdisc_types),
-        .types = rtnl_qdisc_types,
+static const NLTypeSystem rtnl_tca_type_system = {
+        .count = ELEMENTSOF(rtnl_tca_types),
+        .types = rtnl_tca_types,
 };
 
 static const NLType error_types[] = {
@@ -868,9 +1006,12 @@ static const NLType rtnl_types[] = {
         [RTM_NEWNEXTHOP]   = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_nexthop_type_system, .size = sizeof(struct nhmsg) },
         [RTM_DELNEXTHOP]   = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_nexthop_type_system, .size = sizeof(struct nhmsg) },
         [RTM_GETNEXTHOP]   = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_nexthop_type_system, .size = sizeof(struct nhmsg) },
-        [RTM_NEWQDISC]     = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_qdisc_type_system, .size = sizeof(struct tcmsg) },
-        [RTM_DELQDISC]     = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_qdisc_type_system, .size = sizeof(struct tcmsg) },
-        [RTM_GETQDISC]     = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_qdisc_type_system, .size = sizeof(struct tcmsg) },
+        [RTM_NEWQDISC]     = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
+        [RTM_DELQDISC]     = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
+        [RTM_GETQDISC]     = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
+        [RTM_NEWTCLASS]    = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
+        [RTM_DELTCLASS]    = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
+        [RTM_GETTCLASS]    = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_tca_type_system, .size = sizeof(struct tcmsg) },
 };
 
 const NLTypeSystem rtnl_type_system_root = {
index b2fa8c96e64e770d0d1d1be878f5ce3a0b7d0456..058747a2e9f6cef089a7f6c75ab88e0faf65f09b 100644 (file)
@@ -9,6 +9,10 @@ enum {
         NETLINK_TYPE_U16,                       /* NLA_U16 */
         NETLINK_TYPE_U32,                       /* NLA_U32 */
         NETLINK_TYPE_U64,                       /* NLA_U64 */
+        NETLINK_TYPE_S8,                        /* NLA_S8 */
+        NETLINK_TYPE_S16,                       /* NLA_S16 */
+        NETLINK_TYPE_S32,                       /* NLA_S32 */
+        NETLINK_TYPE_S64,                       /* NLA_S64 */
         NETLINK_TYPE_STRING,                    /* NLA_STRING */
         NETLINK_TYPE_FLAG,                      /* NLA_FLAG */
         NETLINK_TYPE_IN_ADDR,
@@ -92,9 +96,18 @@ const char *nl_union_link_info_data_to_string(NLUnionLinkInfoData p) _const_;
 NLUnionLinkInfoData nl_union_link_info_data_from_string(const char *p) _pure_;
 
 typedef enum NLUnionTCAOptionData {
+        NL_UNION_TCA_OPTION_DATA_CAKE,
         NL_UNION_TCA_OPTION_DATA_CODEL,
+        NL_UNION_TCA_OPTION_DATA_DRR,
+        NL_UNION_TCA_OPTION_DATA_ETS,
         NL_UNION_TCA_OPTION_DATA_FQ,
         NL_UNION_TCA_OPTION_DATA_FQ_CODEL,
+        NL_UNION_TCA_OPTION_DATA_GRED,
+        NL_UNION_TCA_OPTION_DATA_HHF,
+        NL_UNION_TCA_OPTION_DATA_HTB,
+        NL_UNION_TCA_OPTION_DATA_PIE,
+        NL_UNION_TCA_OPTION_DATA_QFQ,
+        NL_UNION_TCA_OPTION_DATA_SFB,
         NL_UNION_TCA_OPTION_DATA_TBF,
         _NL_UNION_TCA_OPTION_DATA_MAX,
         _NL_UNION_TCA_OPTION_DATA_INVALID = -1,
index 7387cffaa3a06adff5a2d866e8f4aee08194a983..f045e794b6e7d8de2cf108f545dcc3da77a5afa8 100644 (file)
@@ -2,6 +2,7 @@
 
 #include "sd-netlink.h"
 
+#include "format-util.h"
 #include "memory-util.h"
 #include "netlink-internal.h"
 #include "netlink-util.h"
@@ -9,6 +10,8 @@
 
 int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
+        _cleanup_strv_free_ char **alternative_names = NULL;
+        char old_name[IF_NAMESIZE + 1] = {};
         int r;
 
         assert(rtnl);
@@ -18,10 +21,18 @@ int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name) {
         if (!ifname_valid(name))
                 return -EINVAL;
 
-        if (!*rtnl) {
-                r = sd_netlink_open(rtnl);
+        r = rtnl_get_link_alternative_names(rtnl, ifindex, &alternative_names);
+        if (r < 0)
+                log_debug_errno(r, "Failed to get alternative names on network interface %i, ignoring: %m",
+                                ifindex);
+
+        if (strv_contains(alternative_names, name)) {
+                r = rtnl_delete_link_alternative_names(rtnl, ifindex, STRV_MAKE(name));
                 if (r < 0)
-                        return r;
+                        return log_debug_errno(r, "Failed to remove '%s' from alternative names on network interface %i: %m",
+                                               name, ifindex);
+
+                format_ifname(ifindex, old_name);
         }
 
         r = sd_rtnl_message_new_link(*rtnl, &message, RTM_SETLINK, ifindex);
@@ -36,6 +47,13 @@ int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name) {
         if (r < 0)
                 return r;
 
+        if (!isempty(old_name)) {
+                r = rtnl_set_link_alternative_names(rtnl, ifindex, STRV_MAKE(old_name));
+                if (r < 0)
+                        log_debug_errno(r, "Failed to set '%s' as an alternative name on network interface %i, ignoring: %m",
+                                        old_name, ifindex);
+        }
+
         return 0;
 }
 
@@ -85,12 +103,45 @@ int rtnl_set_link_properties(sd_netlink **rtnl, int ifindex, const char *alias,
         return 0;
 }
 
-int rtnl_set_link_alternative_names(sd_netlink **rtnl, int ifindex, char * const *alternative_names) {
+int rtnl_get_link_alternative_names(sd_netlink **rtnl, int ifindex, char ***ret) {
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL;
+        _cleanup_strv_free_ char **names = NULL;
+        int r;
+
+        assert(rtnl);
+        assert(ifindex > 0);
+        assert(ret);
+
+        if (!*rtnl) {
+                r = sd_netlink_open(rtnl);
+                if (r < 0)
+                        return r;
+        }
+
+        r = sd_rtnl_message_new_link(*rtnl, &message, RTM_GETLINK, ifindex);
+        if (r < 0)
+                return r;
+
+        r = sd_netlink_call(*rtnl, message, 0, &reply);
+        if (r < 0)
+                return r;
+
+        r = sd_netlink_message_read_strv(reply, IFLA_PROP_LIST, IFLA_ALT_IFNAME, &names);
+        if (r < 0 && r != -ENODATA)
+                return r;
+
+        *ret = TAKE_PTR(names);
+
+        return 0;
+}
+
+static int rtnl_update_link_alternative_names(sd_netlink **rtnl, uint16_t nlmsg_type, int ifindex, char * const *alternative_names) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
         int r;
 
         assert(rtnl);
         assert(ifindex > 0);
+        assert(IN_SET(nlmsg_type, RTM_NEWLINKPROP, RTM_DELLINKPROP));
 
         if (strv_isempty(alternative_names))
                 return 0;
@@ -101,7 +152,7 @@ int rtnl_set_link_alternative_names(sd_netlink **rtnl, int ifindex, char * const
                         return r;
         }
 
-        r = sd_rtnl_message_new_link(*rtnl, &message, RTM_NEWLINKPROP, ifindex);
+        r = sd_rtnl_message_new_link(*rtnl, &message, nlmsg_type, ifindex);
         if (r < 0)
                 return r;
 
@@ -124,6 +175,14 @@ int rtnl_set_link_alternative_names(sd_netlink **rtnl, int ifindex, char * const
         return 0;
 }
 
+int rtnl_set_link_alternative_names(sd_netlink **rtnl, int ifindex, char * const *alternative_names) {
+        return rtnl_update_link_alternative_names(rtnl, RTM_NEWLINKPROP, ifindex, alternative_names);
+}
+
+int rtnl_delete_link_alternative_names(sd_netlink **rtnl, int ifindex, char * const *alternative_names) {
+        return rtnl_update_link_alternative_names(rtnl, RTM_DELLINKPROP, ifindex, alternative_names);
+}
+
 int rtnl_set_link_alternative_names_by_ifname(sd_netlink **rtnl, const char *ifname, char * const *alternative_names) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
         int r;
@@ -236,10 +295,10 @@ int rtnl_message_new_synthetic_error(sd_netlink *rtnl, int error, uint32_t seria
         if (r < 0)
                 return r;
 
+        rtnl_message_seal(*ret);
         (*ret)->hdr->nlmsg_seq = serial;
 
         err = NLMSG_DATA((*ret)->hdr);
-
         err->error = error;
 
         return 0;
index d2d8334b2136bc509a0cb0d9d9559f8801dd6e1b..04e6a98e6b5ca32da1ebc16304fdcebb815fef27 100644 (file)
@@ -47,10 +47,16 @@ static inline bool rtnl_message_type_is_qdisc(uint16_t type) {
         return IN_SET(type, RTM_NEWQDISC, RTM_DELQDISC, RTM_GETQDISC);
 }
 
+static inline bool rtnl_message_type_is_tclass(uint16_t type) {
+        return IN_SET(type, RTM_NEWTCLASS, RTM_DELTCLASS, RTM_GETTCLASS);
+}
+
 int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name);
 int rtnl_set_link_properties(sd_netlink **rtnl, int ifindex, const char *alias, const struct ether_addr *mac, uint32_t mtu);
+int rtnl_get_link_alternative_names(sd_netlink **rtnl, int ifindex, char ***ret);
 int rtnl_set_link_alternative_names(sd_netlink **rtnl, int ifindex, char * const *alternative_names);
 int rtnl_set_link_alternative_names_by_ifname(sd_netlink **rtnl, const char *ifname, char * const *alternative_names);
+int rtnl_delete_link_alternative_names(sd_netlink **rtnl, int ifindex, char * const *alternative_names);
 int rtnl_resolve_link_alternative_name(sd_netlink **rtnl, const char *name);
 int rtnl_get_link_iftype(sd_netlink **rtnl, int ifindex, unsigned short *ret);
 
index 182a666746055ff6210e02ba1bcc63fe2f0c13cc..7689bf66211712bcf1aafe6e08fb029935cd9b4e 100644 (file)
@@ -1077,3 +1077,46 @@ int sd_rtnl_message_set_qdisc_handle(sd_netlink_message *m, uint32_t handle) {
 
         return 0;
 }
+
+int sd_rtnl_message_new_tclass(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int tcm_family, int tcm_ifindex) {
+        struct tcmsg *tcm;
+        int r;
+
+        assert_return(rtnl_message_type_is_tclass(nlmsg_type), -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        r = message_new(rtnl, ret, nlmsg_type);
+        if (r < 0)
+                return r;
+
+        if (nlmsg_type == RTM_NEWTCLASS)
+                (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL;
+
+        tcm = NLMSG_DATA((*ret)->hdr);
+        tcm->tcm_family = tcm_family;
+        tcm->tcm_ifindex = tcm_ifindex;
+
+        return 0;
+}
+
+int sd_rtnl_message_set_tclass_parent(sd_netlink_message *m, uint32_t parent) {
+        struct tcmsg *tcm;
+
+        assert_return(rtnl_message_type_is_tclass(m->hdr->nlmsg_type), -EINVAL);
+
+        tcm = NLMSG_DATA(m->hdr);
+        tcm->tcm_parent = parent;
+
+        return 0;
+}
+
+int sd_rtnl_message_set_tclass_handle(sd_netlink_message *m, uint32_t handle) {
+        struct tcmsg *tcm;
+
+        assert_return(rtnl_message_type_is_tclass(m->hdr->nlmsg_type), -EINVAL);
+
+        tcm = NLMSG_DATA(m->hdr);
+        tcm->tcm_handle = handle;
+
+        return 0;
+}
index 5b7081089e4a92dbb6c753bdf55cfdea30899402..91670ee324efe2ed906a19e40e611b0f7a712699 100644 (file)
@@ -7,6 +7,7 @@
 #include "alloc-util.h"
 #include "fd-util.h"
 #include "hashmap.h"
+#include "io-util.h"
 #include "macro.h"
 #include "netlink-internal.h"
 #include "netlink-slot.h"
@@ -462,8 +463,6 @@ static usec_t calc_elapse(uint64_t usec) {
 }
 
 static int rtnl_poll(sd_netlink *rtnl, bool need_more, uint64_t timeout_usec) {
-        struct pollfd p[1] = {};
-        struct timespec ts;
         usec_t m = USEC_INFINITY;
         int r, e;
 
@@ -492,17 +491,16 @@ static int rtnl_poll(sd_netlink *rtnl, bool need_more, uint64_t timeout_usec) {
                 }
         }
 
-        if (timeout_usec != (uint64_t) -1 && (m == (uint64_t) -1 || timeout_usec < m))
+        if (timeout_usec != (uint64_t) -1 && (m == USEC_INFINITY || timeout_usec < m))
                 m = timeout_usec;
 
-        p[0].fd = rtnl->fd;
-        p[0].events = e;
-
-        r = ppoll(p, 1, m == (uint64_t) -1 ? NULL : timespec_store(&ts, m), NULL);
+        r = fd_wait_for_event(rtnl->fd, e, m);
         if (r < 0)
-                return -errno;
+                return r;
+        if (r == 0)
+                return 0;
 
-        return r > 0 ? 1 : 0;
+        return 1;
 }
 
 int sd_netlink_wait(sd_netlink *nl, uint64_t timeout_usec) {
index b9b618e58501c7b03a4836f20b2f68f1988537d2..ce6ae846c5041e828cd2ef0a4cb85af53e755a65 100644 (file)
@@ -168,6 +168,14 @@ _public_ int sd_network_link_get_address_state(int ifindex, char **state) {
         return network_link_get_string(ifindex, "ADDRESS_STATE", state);
 }
 
+_public_ int sd_network_link_get_dhcp6_client_iaid_string(int ifindex, char **iaid) {
+        return network_link_get_string(ifindex, "DHCP6_CLIENT_IAID", iaid);
+}
+
+_public_ int sd_network_link_get_dhcp6_client_duid_string(int ifindex, char **duid) {
+        return network_link_get_string(ifindex, "DHCP6_CLIENT_DUID", duid);
+}
+
 _public_ int sd_network_link_get_required_for_online(int ifindex) {
         _cleanup_free_ char *s = NULL;
         int r;
@@ -224,14 +232,6 @@ _public_ int sd_network_link_get_dnssec_negative_trust_anchors(int ifindex, char
         return network_link_get_strv(ifindex, "DNSSEC_NTA", nta);
 }
 
-_public_ int sd_network_link_get_timezone(int ifindex, char **ret) {
-        return network_link_get_string(ifindex, "TIMEZONE", ret);
-}
-
-_public_ int sd_network_link_get_dhcp4_address(int ifindex, char **ret) {
-        return network_link_get_string(ifindex, "DHCP4_ADDRESS", ret);
-}
-
 _public_ int sd_network_link_get_dns(int ifindex, char ***ret) {
         return network_link_get_strv(ifindex, "DNS", ret);
 }
@@ -240,6 +240,10 @@ _public_ int sd_network_link_get_ntp(int ifindex, char ***ret) {
         return network_link_get_strv(ifindex, "NTP", ret);
 }
 
+_public_ int sd_network_link_get_sip(int ifindex, char ***ret) {
+        return network_link_get_strv(ifindex, "SIP", ret);
+}
+
 _public_ int sd_network_link_get_search_domains(int ifindex, char ***ret) {
         return network_link_get_strv(ifindex, "DOMAINS", ret);
 }
@@ -248,10 +252,6 @@ _public_ int sd_network_link_get_route_domains(int ifindex, char ***ret) {
         return network_link_get_strv(ifindex, "ROUTE_DOMAINS", ret);
 }
 
-_public_ int sd_network_link_get_sip_servers(int ifindex, char ***ret) {
-        return network_link_get_strv(ifindex, "SIP", ret);
-}
-
 _public_ int sd_network_link_get_dns_default_route(int ifindex) {
         char path[STRLEN("/run/systemd/netif/links/") + DECIMAL_STR_MAX(ifindex) + 1];
         _cleanup_free_ char *s = NULL;
index 95d6551e5cf5064b3ba47c023bd5232c54e72e8a..736795d1d797b8c49c4b94c67c5e34d92816b51f 100644 (file)
@@ -7,6 +7,7 @@
 #include "fd-util.h"
 #include "fileio.h"
 #include "fs-util.h"
+#include "path-lookup.h"
 #include "path-util.h"
 #include "string-util.h"
 #include "strv.h"
@@ -318,64 +319,130 @@ static int get_path(uint64_t type, char **buffer, const char **ret) {
 
         case SD_PATH_USER_DESKTOP:
                 return from_user_dir("XDG_DESKTOP_DIR", buffer, ret);
+
+        case SD_PATH_SYSTEMD_UTIL:
+                *ret = ROOTPREFIX "/lib/systemd";
+                return 0;
+
+        case SD_PATH_SYSTEMD_SYSTEM_UNIT:
+                *ret = SYSTEM_DATA_UNIT_PATH;
+                return 0;
+
+        case SD_PATH_SYSTEMD_SYSTEM_PRESET:
+                *ret = ROOTPREFIX "/lib/systemd/system-preset";
+                return 0;
+
+        case SD_PATH_SYSTEMD_USER_UNIT:
+                *ret = USER_DATA_UNIT_DIR;
+                return 0;
+
+        case SD_PATH_SYSTEMD_USER_PRESET:
+                *ret = ROOTPREFIX "/lib/systemd/user-preset";
+                return 0;
+
+        case SD_PATH_SYSTEMD_SYSTEM_CONF:
+                *ret = SYSTEM_CONFIG_UNIT_DIR;
+                return 0;
+
+        case SD_PATH_SYSTEMD_USER_CONF:
+                *ret = USER_CONFIG_UNIT_DIR;
+                return 0;
+
+        case SD_PATH_SYSTEMD_SYSTEM_GENERATOR:
+                *ret = SYSTEM_GENERATOR_DIR;
+                return 0;
+
+        case SD_PATH_SYSTEMD_USER_GENERATOR:
+                *ret = USER_GENERATOR_DIR;
+                return 0;
+
+        case SD_PATH_SYSTEMD_SLEEP:
+                *ret = ROOTPREFIX "/lib/systemd/system-sleep";
+                return 0;
+
+        case SD_PATH_SYSTEMD_SHUTDOWN:
+                *ret = ROOTPREFIX "/lib/systemd/system-shutdown";
+                return 0;
+
+        /* FIXME: systemd.pc uses ${prefix}, but CONF_PATHS_NULSTR doesn't.
+         *        Should ${prefix} use in systemd.pc be removed? */
+        case SD_PATH_TMPFILES:
+                *ret = "/usr/lib/tmpfiles.d";
+                return 0;
+
+        case SD_PATH_SYSUSERS:
+                *ret = ROOTPREFIX "/lib/sysusers.d";
+                return 0;
+
+        case SD_PATH_SYSCTL:
+                *ret = ROOTPREFIX "/lib/sysctl.d";
+                return 0;
+
+        case SD_PATH_BINFMT:
+                *ret = ROOTPREFIX "/lib/binfmt.d";
+                return 0;
+
+        case SD_PATH_MODULES_LOAD:
+                *ret = ROOTPREFIX "/lib/modules-load.d";
+                return 0;
+
+        case SD_PATH_CATALOG:
+                *ret = "/usr/lib/systemd/catalog";
+                return 0;
         }
 
         return -EOPNOTSUPP;
 }
 
-_public_ int sd_path_home(uint64_t type, const char *suffix, char **path) {
+static int get_path_alloc(uint64_t type, const char *suffix, char **path) {
         _cleanup_free_ char *buffer = NULL;
+        char *buffer2 = NULL;
         const char *ret;
-        char *cc;
         int r;
 
-        assert_return(path, -EINVAL);
-
-        if (IN_SET(type,
-                   SD_PATH_SEARCH_BINARIES,
-                   SD_PATH_SEARCH_BINARIES_DEFAULT,
-                   SD_PATH_SEARCH_LIBRARY_PRIVATE,
-                   SD_PATH_SEARCH_LIBRARY_ARCH,
-                   SD_PATH_SEARCH_SHARED,
-                   SD_PATH_SEARCH_CONFIGURATION_FACTORY,
-                   SD_PATH_SEARCH_STATE_FACTORY,
-                   SD_PATH_SEARCH_CONFIGURATION)) {
+        assert(path);
 
-                _cleanup_strv_free_ char **l = NULL;
-
-                r = sd_path_search(type, suffix, &l);
-                if (r < 0)
-                        return r;
+        r = get_path(type, &buffer, &ret);
+        if (r < 0)
+                return r;
 
-                buffer = strv_join(l, ":");
+        if (suffix) {
+                suffix += strspn(suffix, "/");
+                buffer2 = path_join(ret, suffix);
+                if (!buffer2)
+                        return -ENOMEM;
+        } else if (!buffer) {
+                buffer = strdup(ret);
                 if (!buffer)
                         return -ENOMEM;
-
-                *path = TAKE_PTR(buffer);
-                return 0;
         }
 
-        r = get_path(type, &buffer, &ret);
-        if (r < 0)
+        *path = buffer2 ?: TAKE_PTR(buffer);
+        return 0;
+}
+
+_public_ int sd_path_lookup(uint64_t type, const char *suffix, char **path) {
+        int r;
+
+        assert_return(path, -EINVAL);
+
+        r = get_path_alloc(type, suffix, path);
+        if (r != -EOPNOTSUPP)
                 return r;
 
-        if (!suffix) {
-                if (!buffer) {
-                        buffer = strdup(ret);
-                        if (!buffer)
-                                return -ENOMEM;
-                }
+        /* Fall back to sd_path_lookup_strv */
+        _cleanup_strv_free_ char **l = NULL;
+        char *buffer;
 
-                *path = TAKE_PTR(buffer);
-                return 0;
-        }
+        r = sd_path_lookup_strv(type, suffix, &l);
+        if (r < 0)
+                return r;
 
-        suffix += strspn(suffix, "/");
-        cc = path_join(ret, suffix);
-        if (!cc)
+        buffer = strv_join(l, ":");
+        if (!buffer)
                 return -ENOMEM;
 
-        *path = TAKE_PTR(cc);
+        *path = buffer;
         return 0;
 }
 
@@ -454,6 +521,7 @@ static int search_from_environment(
 #endif
 
 static int get_search(uint64_t type, char ***list) {
+        int r;
 
         assert(list);
 
@@ -536,58 +604,69 @@ static int get_search(uint64_t type, char ***list) {
                                                "/etc",
                                                NULL);
 
-        case SD_PATH_SEARCH_BINARIES_DEFAULT: {
+        case SD_PATH_SEARCH_BINARIES_DEFAULT:
+                return strv_from_nulstr(list, DEFAULT_PATH_NULSTR);
+
+        case SD_PATH_SYSTEMD_SEARCH_SYSTEM_UNIT:
+        case SD_PATH_SYSTEMD_SEARCH_USER_UNIT: {
+                _cleanup_(lookup_paths_free) LookupPaths lp = {};
+                const UnitFileScope scope = type == SD_PATH_SYSTEMD_SEARCH_SYSTEM_UNIT ?
+                                                    UNIT_FILE_SYSTEM : UNIT_FILE_USER;
+
+                r = lookup_paths_init(&lp, scope, 0, NULL);
+                if (r < 0)
+                        return r;
+
+                *list = TAKE_PTR(lp.search_path);
+                return 0;
+        }
+
+        case SD_PATH_SYSTEMD_SEARCH_SYSTEM_GENERATOR:
+        case SD_PATH_SYSTEMD_SEARCH_USER_GENERATOR: {
                 char **t;
+                const UnitFileScope scope = type == SD_PATH_SYSTEMD_SEARCH_SYSTEM_GENERATOR ?
+                                                    UNIT_FILE_SYSTEM : UNIT_FILE_USER;
 
-                t = strv_split_nulstr(DEFAULT_PATH_NULSTR);
+                t = generator_binary_paths(scope);
                 if (!t)
                         return -ENOMEM;
 
                 *list = t;
                 return 0;
-        }}
+        }
+
+        case SD_PATH_SYSTEMD_SEARCH_NETWORK:
+                return strv_from_nulstr(list, NETWORK_DIRS_NULSTR);
+
+        }
 
         return -EOPNOTSUPP;
 }
 
-_public_ int sd_path_search(uint64_t type, const char *suffix, char ***paths) {
-        char **i, **j;
+_public_ int sd_path_lookup_strv(uint64_t type, const char *suffix, char ***paths) {
         _cleanup_strv_free_ char **l = NULL, **n = NULL;
         int r;
 
         assert_return(paths, -EINVAL);
 
-        if (!IN_SET(type,
-                    SD_PATH_SEARCH_BINARIES,
-                    SD_PATH_SEARCH_BINARIES_DEFAULT,
-                    SD_PATH_SEARCH_LIBRARY_PRIVATE,
-                    SD_PATH_SEARCH_LIBRARY_ARCH,
-                    SD_PATH_SEARCH_SHARED,
-                    SD_PATH_SEARCH_CONFIGURATION_FACTORY,
-                    SD_PATH_SEARCH_STATE_FACTORY,
-                    SD_PATH_SEARCH_CONFIGURATION)) {
-
-                char *p;
+        r = get_search(type, &l);
+        if (r == -EOPNOTSUPP) {
+                _cleanup_free_ char *t = NULL;
 
-                r = sd_path_home(type, suffix, &p);
+                r = get_path_alloc(type, suffix, &t);
                 if (r < 0)
                         return r;
 
                 l = new(char*, 2);
-                if (!l) {
-                        free(p);
+                if (!l)
                         return -ENOMEM;
-                }
-
-                l[0] = p;
+                l[0] = TAKE_PTR(t);
                 l[1] = NULL;
 
                 *paths = TAKE_PTR(l);
                 return 0;
-        }
 
-        r = get_search(type, &l);
-        if (r < 0)
+        } else if (r < 0)
                 return r;
 
         if (!suffix) {
@@ -599,7 +678,7 @@ _public_ int sd_path_search(uint64_t type, const char *suffix, char ***paths) {
         if (!n)
                 return -ENOMEM;
 
-        j = n;
+        char **i, **j = n;
         STRV_FOREACH(i, l) {
                 *j = path_join(*i, suffix);
                 if (!*j)
@@ -607,8 +686,8 @@ _public_ int sd_path_search(uint64_t type, const char *suffix, char ***paths) {
 
                 j++;
         }
-
         *j = NULL;
+
         *paths = TAKE_PTR(n);
         return 0;
 }
index 5f780e0be956e8ec7a7f2d70d0350b5081175a05..5bec7418b89df50143207a0246e594017cc8a44d 100644 (file)
@@ -9,6 +9,7 @@
 #include "device-monitor-private.h"
 #include "device-private.h"
 #include "device-util.h"
+#include "io-util.h"
 #include "libudev-device-internal.h"
 #include "string-util.h"
 
@@ -191,17 +192,11 @@ _public_ int udev_monitor_get_fd(struct udev_monitor *udev_monitor) {
 }
 
 static int udev_monitor_receive_sd_device(struct udev_monitor *udev_monitor, sd_device **ret) {
-        struct pollfd pfd;
         int r;
 
         assert(udev_monitor);
         assert(ret);
 
-        pfd = (struct pollfd) {
-                .fd = device_monitor_get_fd(udev_monitor->monitor),
-                .events = POLLIN,
-        };
-
         for (;;) {
                 /* r == 0 means a device is received but it does not pass the current filter. */
                 r = device_monitor_receive_device(udev_monitor->monitor, ret);
@@ -209,17 +204,18 @@ static int udev_monitor_receive_sd_device(struct udev_monitor *udev_monitor, sd_
                         return r;
 
                 for (;;) {
-                        /* wait next message */
-                        r = poll(&pfd, 1, 0);
+                        /* Wait for next message */
+                        r = fd_wait_for_event(device_monitor_get_fd(udev_monitor->monitor), POLLIN, 0);
                         if (r < 0) {
-                                if (IN_SET(errno, EINTR, EAGAIN))
+                                if (IN_SET(r, -EINTR, -EAGAIN))
                                         continue;
 
-                                return -errno;
-                        } else if (r == 0)
+                                return r;
+                        }
+                        if (r == 0)
                                 return -EAGAIN;
 
-                        /* receive next message */
+                        /* Receive next message */
                         break;
                 }
         }
index 37660d0313f0bbbdacb6d1096e62a5bca88f7f2d..4a471fb90d139bedb02f382e7196c980ee542ec7 100644 (file)
@@ -154,8 +154,8 @@ size_t util_replace_whitespace(const char *str, char *to, size_t len) {
         return j;
 }
 
-/* allow chars in whitelist, plain ascii, hex-escaping and valid utf8 */
-size_t util_replace_chars(char *str, const char *white) {
+/* allow chars in allow list, plain ascii, hex-escaping and valid utf8 */
+size_t util_replace_chars(char *str, const char *allow) {
         size_t i = 0, replaced = 0;
 
         assert(str);
@@ -163,7 +163,7 @@ size_t util_replace_chars(char *str, const char *white) {
         while (str[i] != '\0') {
                 int len;
 
-                if (whitelisted_char_for_devnode(str[i], white)) {
+                if (allow_listed_char_for_devnode(str[i], allow)) {
                         i++;
                         continue;
                 }
@@ -182,7 +182,7 @@ size_t util_replace_chars(char *str, const char *white) {
                 }
 
                 /* if space is allowed, replace whitespace with ordinary space */
-                if (isspace(str[i]) && white && strchr(white, ' ')) {
+                if (isspace(str[i]) && allow && strchr(allow, ' ')) {
                         str[i] = ' ';
                         i++;
                         replaced++;
index b4450727ca5e7351926af6c345634d08de9f357e..aa187b2b4cdfdcd86489d9b5641b81160031cc72 100644 (file)
@@ -106,7 +106,7 @@ _public_ struct udev *udev_unref(struct udev *udev) {
         assert(udev->n_ref > 0);
         udev->n_ref--;
         if (udev->n_ref > 0)
-                /* This is different from our convetion, but let's keep backward
+                /* This is different from our convention, but let's keep backward
                  * compatibility. So, do not use DEFINE_PUBLIC_TRIVIAL_UNREF_FUNC()
                  * macro to define this function. */
                 return udev;
index 30669a9359e58104898dcc1e9c460db27fee9562..233d0813002d8e6cbe071a05f182b628d555b20f 100644 (file)
@@ -267,9 +267,8 @@ int x11_read_data(Context *c, sd_bus_message *m) {
                                 else if (streq(a[1], "XkbOptions"))
                                         p = &c->x11_options;
 
-                                if (p) {
+                                if (p)
                                         free_and_replace(*p, a[2]);
-                                }
                         }
 
                 } else if (!in_section && first_word(l, "Section")) {
index 6f2d37d2226f22c01c32db1260792f43d3211dda..e0664de826bb0973f2fe197188b6f9fe75f8f0b5 100644 (file)
@@ -8,7 +8,8 @@
 #include "sd-bus.h"
 
 #include "bus-error.h"
-#include "bus-util.h"
+#include "bus-locator.h"
+#include "bus-map-properties.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "kbd-util.h"
@@ -163,13 +164,7 @@ static int set_locale(int argc, char **argv, void *userdata) {
 
         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
 
-        r = sd_bus_message_new_method_call(
-                        bus,
-                        &m,
-                        "org.freedesktop.locale1",
-                        "/org/freedesktop/locale1",
-                        "org.freedesktop.locale1",
-                        "SetLocale");
+        r = bus_message_new_method_call(bus, &m, bus_locale, "SetLocale");
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -215,11 +210,9 @@ static int set_vconsole_keymap(int argc, char **argv, void *userdata) {
         map = argv[1];
         toggle_map = argc > 2 ? argv[2] : "";
 
-        r = sd_bus_call_method(
+        r = bus_call_method(
                         bus,
-                        "org.freedesktop.locale1",
-                        "/org/freedesktop/locale1",
-                        "org.freedesktop.locale1",
+                        bus_locale,
                         "SetVConsoleKeyboard",
                         &error,
                         NULL,
@@ -258,11 +251,9 @@ static int set_x11_keymap(int argc, char **argv, void *userdata) {
         variant = argc > 3 ? argv[3] : "";
         options = argc > 4 ? argv[4] : "";
 
-        r = sd_bus_call_method(
+        r = bus_call_method(
                         bus,
-                        "org.freedesktop.locale1",
-                        "/org/freedesktop/locale1",
-                        "org.freedesktop.locale1",
+                        bus_locale,
                         "SetX11Keyboard",
                         &error,
                         NULL,
@@ -512,9 +503,7 @@ static int run(int argc, char *argv[]) {
         int r;
 
         setlocale(LC_ALL, "");
-        log_show_color(true);
-        log_parse_environment();
-        log_open();
+        log_setup_cli();
 
         r = parse_argv(argc, argv);
         if (r <= 0)
@@ -522,7 +511,7 @@ static int run(int argc, char *argv[]) {
 
         r = bus_connect_transport(arg_transport, arg_host, false, &bus);
         if (r < 0)
-                return log_error_errno(r, "Failed to create bus connection: %m");
+                return bus_log_connect_error(r);
 
         return localectl_main(bus, argc, argv);
 }
index 09f16d25f4c20e5fa928dcce2dabaf34e3d0982b..715ce5cac7e07c5bbcedc5552ab1eee3269561b7 100644 (file)
 
 #include "alloc-util.h"
 #include "bus-error.h"
+#include "bus-log-control-api.h"
 #include "bus-message.h"
 #include "bus-polkit.h"
 #include "def.h"
+#include "dlfcn-util.h"
 #include "keymap-util.h"
 #include "locale-util.h"
 #include "macro.h"
@@ -24,6 +26,7 @@
 #include "missing_capability.h"
 #include "path-util.h"
 #include "selinux-util.h"
+#include "service-util.h"
 #include "signal-util.h"
 #include "string-util.h"
 #include "strv.h"
@@ -256,18 +259,57 @@ static int property_get_xkb(
         return -EINVAL;
 }
 
+static int process_locale_list_item(
+                const char *assignment,
+                char *new_locale[static _VARIABLE_LC_MAX],
+                sd_bus_error *error) {
+
+        assert(assignment);
+        assert(new_locale);
+
+        for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++) {
+                const char *name, *e;
+
+                assert_se(name = locale_variable_to_string(p));
+
+                e = startswith(assignment, name);
+                if (!e)
+                        continue;
+
+                if (*e != '=')
+                        continue;
+
+                e++;
+
+                if (!locale_is_valid(e))
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Locale %s is not valid, refusing.", e);
+                if (locale_is_installed(e) <= 0)
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Locale %s not installed, refusing.", e);
+                if (new_locale[p])
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Locale variable %s set twice, refusing.", name);
+
+                new_locale[p] = strdup(e);
+                if (!new_locale[p])
+                        return -ENOMEM;
+
+                return 0;
+        }
+
+        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Locale assignment %s not valid, refusing.", assignment);
+}
+
 static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *error) {
         _cleanup_(locale_variables_freep) char *new_locale[_VARIABLE_LC_MAX] = {};
         _cleanup_strv_free_ char **settings = NULL, **l = NULL;
         Context *c = userdata;
         bool modified = false;
-        int interactive, p, r;
+        int interactive, r;
         char **i;
 
         assert(m);
         assert(c);
 
-        r = bus_message_read_strv_extend(m, &l);
+        r = sd_bus_message_read_strv(m, &l);
         if (r < 0)
                 return r;
 
@@ -276,11 +318,13 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er
                 return r;
 
         /* If single locale without variable name is provided, then we assume it is LANG=. */
-        if (strv_length(l) == 1 && !strchr(*l, '=')) {
-                if (!locale_is_valid(*l))
-                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid Locale data.");
+        if (strv_length(l) == 1 && !strchr(l[0], '=')) {
+                if (!locale_is_valid(l[0]))
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid locale specification: %s", l[0]);
+                if (locale_is_installed(l[0]) <= 0)
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified locale is not installed: %s", l[0]);
 
-                new_locale[VARIABLE_LANG] = strdup(*l);
+                new_locale[VARIABLE_LANG] = strdup(l[0]);
                 if (!new_locale[VARIABLE_LANG])
                         return -ENOMEM;
 
@@ -289,31 +333,9 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er
 
         /* Check whether a variable is valid */
         STRV_FOREACH(i, l) {
-                bool valid = false;
-
-                for (p = 0; p < _VARIABLE_LC_MAX; p++) {
-                        size_t k;
-                        const char *name;
-
-                        name = locale_variable_to_string(p);
-                        assert(name);
-
-                        k = strlen(name);
-                        if (startswith(*i, name) &&
-                            (*i)[k] == '=' &&
-                            locale_is_valid((*i) + k + 1)) {
-                                valid = true;
-
-                                new_locale[p] = strdup((*i) + k + 1);
-                                if (!new_locale[p])
-                                        return -ENOMEM;
-
-                                break;
-                        }
-                }
-
-                if (!valid)
-                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid Locale data.");
+                r = process_locale_list_item(*i, new_locale, error);
+                if (r < 0)
+                        return r;
         }
 
         /* If LANG was specified, but not LANGUAGE, check if we should
@@ -336,7 +358,7 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er
         }
 
         /* Merge with the current settings */
-        for (p = 0; p < _VARIABLE_LC_MAX; p++)
+        for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++)
                 if (!isempty(c->locale[p]) && isempty(new_locale[p])) {
                         new_locale[p] = strdup(c->locale[p]);
                         if (!new_locale[p])
@@ -345,7 +367,7 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er
 
         locale_simplify(new_locale);
 
-        for (p = 0; p < _VARIABLE_LC_MAX; p++)
+        for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++)
                 if (!streq_ptr(c->locale[p], new_locale[p])) {
                         modified = true;
                         break;
@@ -370,7 +392,7 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er
         if (r == 0)
                 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
 
-        for (p = 0; p < _VARIABLE_LC_MAX; p++)
+        for (LocaleVariable p = 0; p < _VARIABLE_LC_MAX; p++)
                 free_and_replace(c->locale[p], new_locale[p]);
 
         r = locale_write_data(c, &settings);
@@ -478,10 +500,9 @@ static void log_xkb(struct xkb_context *ctx, enum xkb_log_level lvl, const char
         const char *fmt;
 
         fmt = strjoina("libxkbcommon: ", format);
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+        DISABLE_WARNING_FORMAT_NONLITERAL;
         log_internalv(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, fmt, args);
-#pragma GCC diagnostic pop
+        REENABLE_WARNING;
 }
 
 #define LOAD_SYMBOL(symbol, dl, name)                                   \
@@ -510,7 +531,7 @@ static int verify_xkb_rmlvo(const char *model, const char *layout, const char *v
         };
         struct xkb_context *ctx = NULL;
         struct xkb_keymap *km = NULL;
-        void *dl;
+        _cleanup_(dlclosep) void *dl = NULL;
         int r;
 
         /* Compile keymap from RMLVO information to check out its validity */
@@ -562,7 +583,6 @@ finish:
         if (symbol_xkb_context_unref && ctx)
                 symbol_xkb_context_unref(ctx);
 
-        (void) dlclose(dl);
         return r;
 }
 
@@ -676,12 +696,44 @@ static const sd_bus_vtable locale_vtable[] = {
         SD_BUS_PROPERTY("X11Options", "s", property_get_xkb, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("VConsoleKeymap", "s", property_get_vconsole, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("VConsoleKeymapToggle", "s", property_get_vconsole, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
-        SD_BUS_METHOD("SetLocale", "asb", NULL, method_set_locale, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetVConsoleKeyboard", "ssbb", NULL, method_set_vc_keyboard, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetX11Keyboard", "ssssbb", NULL, method_set_x11_keyboard, SD_BUS_VTABLE_UNPRIVILEGED),
+
+        SD_BUS_METHOD_WITH_NAMES("SetLocale",
+                                 "asb",
+                                 SD_BUS_PARAM(locale)
+                                 SD_BUS_PARAM(interactive),
+                                 NULL,,
+                                 method_set_locale,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("SetVConsoleKeyboard",
+                                 "ssbb",
+                                 SD_BUS_PARAM(keymap)
+                                 SD_BUS_PARAM(keymap_toggle)
+                                 SD_BUS_PARAM(convert)
+                                 SD_BUS_PARAM(interactive),
+                                 NULL,,
+                                 method_set_vc_keyboard,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("SetX11Keyboard",
+                                 "ssssbb",
+                                 SD_BUS_PARAM(layout)
+                                 SD_BUS_PARAM(model)
+                                 SD_BUS_PARAM(variant)
+                                 SD_BUS_PARAM(options)
+                                 SD_BUS_PARAM(convert)
+                                 SD_BUS_PARAM(interactive),
+                                 NULL,,
+                                 method_set_x11_keyboard,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+
         SD_BUS_VTABLE_END
 };
 
+static const BusObjectImplementation manager_object = {
+        "/org/freedesktop/locale1",
+        "org.freedesktop.locale1",
+        .vtables = BUS_VTABLES(locale_vtable),
+};
+
 static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         int r;
@@ -694,9 +746,13 @@ static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
         if (r < 0)
                 return log_error_errno(r, "Failed to get system bus connection: %m");
 
-        r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/locale1", "org.freedesktop.locale1", locale_vtable, c);
+        r = bus_add_implementation(bus, &manager_object, c);
         if (r < 0)
-                return log_error_errno(r, "Failed to register object: %m");
+                return r;
+
+        r = bus_log_control_api_register(bus);
+        if (r < 0)
+                return r;
 
         r = sd_bus_request_name_async(bus, NULL, "org.freedesktop.locale1", 0, NULL, NULL);
         if (r < 0)
@@ -723,11 +779,19 @@ static int run(int argc, char *argv[]) {
 
         log_setup_service();
 
+        r = service_parse_argv("systemd-localed.service",
+                               "Manage system locale settings and key mappings.",
+                               BUS_IMPLEMENTATIONS(&manager_object,
+                                                   &log_control_object),
+                               argc, argv);
+        if (r <= 0)
+                return r;
+
         umask(0022);
-        mac_selinux_init();
 
-        if (argc != 1)
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments.");
+        r = mac_selinux_init();
+        if (r < 0)
+                return r;
 
         assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
 
index e87a10ebeb51fbfc08628b4bf2ef7b61c8a61b59..314b0a3d3799de5c2bdf97790962a0999f5e9f5b 100644 (file)
@@ -34,5 +34,5 @@ tests += [
           'src/locale/keymap-util.c',
           'src/locale/keymap-util.h'],
          [libshared],
-         [libdl]],
+         []],
 ]
index 2bbd18363e69a817dd0c769c489b53fbacbb1f08..60dd6add60da1cb0bbb863d430d9333db7ca5f3a 100644 (file)
@@ -30,6 +30,11 @@ SUBSYSTEM=="pci", ENV{ID_PCI_CLASS_FROM_DATABASE}=="Display controller", \
                   ENV{DRIVER}=="", IMPORT{cmdline}="nomodeset", TAG+="seat", TAG+="master-of-seat"
 
 SUBSYSTEM=="drm", KERNEL=="card[0-9]*", TAG+="seat", TAG+="master-of-seat"
+
+# Allow individual USB ports to be assigned to a seat
+SUBSYSTEM=="usb", ATTR{bDeviceClass}=="00", TAG+="seat"
+
+# Allow USB hubs (and all downstream ports) to be assigned to a seat
 SUBSYSTEM=="usb", ATTR{bDeviceClass}=="09", TAG+="seat"
 
 # 'Plugable' USB hub, sound, network, graphics adapter
index a1602031b4b6af0ad359b87d63531aa0a34146d8..e3866eee55a711010362c229c8ed80b50e669aac 100644 (file)
@@ -132,13 +132,13 @@ static int print_inhibitors(sd_bus *bus) {
         if (table_get_rows(table) > 1) {
                 r = table_set_sort(table, (size_t) 1, (size_t) 0, (size_t) 5, (size_t) 6, (size_t) -1);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to sort table: %m");
+                        return table_log_sort_error(r);
 
                 table_set_header(table, arg_legend);
 
                 r = table_print(table, NULL);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to show table: %m");
+                        return table_log_print_error(r);
         }
 
         if (arg_legend) {
index 2e39f557632fffb8e020478704a23b5b6bc6936f..4297a510082a35a17e3fbca2f2defb86a6c97e25 100644 (file)
@@ -9,8 +9,10 @@
 
 #include "alloc-util.h"
 #include "bus-error.h"
+#include "bus-locator.h"
+#include "bus-map-properties.h"
+#include "bus-print-properties.h"
 #include "bus-unit-procs.h"
-#include "bus-util.h"
 #include "cgroup-show.h"
 #include "cgroup-util.h"
 #include "format-table.h"
@@ -64,14 +66,7 @@ static int get_session_path(sd_bus *bus, const char *session_id, sd_bus_error *e
         int r;
         char *ans;
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.login1",
-                        "/org/freedesktop/login1",
-                        "org.freedesktop.login1.Manager",
-                        "GetSession",
-                        error, &reply,
-                        "s", session_id);
+        r = bus_call_method(bus, bus_login_mgr, "GetSession", error, &reply, "s", session_id);
         if (r < 0)
                 return r;
 
@@ -96,7 +91,7 @@ static int show_table(Table *table, const char *word) {
         if (table_get_rows(table) > 1 || OUTPUT_MODE_IS_JSON(arg_output)) {
                 r = table_set_sort(table, (size_t) 0, (size_t) -1);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to sort table: %m");
+                        return table_log_sort_error(r);
 
                 table_set_header(table, arg_legend);
 
@@ -105,7 +100,7 @@ static int show_table(Table *table, const char *word) {
                 else
                         r = table_print(table, NULL);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to show table: %m");
+                        return table_log_print_error(r);
         }
 
         if (arg_legend) {
@@ -130,14 +125,7 @@ static int list_sessions(int argc, char *argv[], void *userdata) {
 
         (void) pager_open(arg_pager_flags);
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.login1",
-                        "/org/freedesktop/login1",
-                        "org.freedesktop.login1.Manager",
-                        "ListSessions",
-                        &error, &reply,
-                        NULL);
+        r = bus_call_method(bus, bus_login_mgr, "ListSessions", &error, &reply, NULL);
         if (r < 0)
                 return log_error_errno(r, "Failed to list sessions: %s", bus_error_message(&error, r));
 
@@ -211,14 +199,7 @@ static int list_users(int argc, char *argv[], void *userdata) {
 
         (void) pager_open(arg_pager_flags);
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.login1",
-                        "/org/freedesktop/login1",
-                        "org.freedesktop.login1.Manager",
-                        "ListUsers",
-                        &error, &reply,
-                        NULL);
+        r = bus_call_method(bus, bus_login_mgr, "ListUsers", &error, &reply, NULL);
         if (r < 0)
                 return log_error_errno(r, "Failed to list users: %s", bus_error_message(&error, r));
 
@@ -268,14 +249,7 @@ static int list_seats(int argc, char *argv[], void *userdata) {
 
         (void) pager_open(arg_pager_flags);
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.login1",
-                        "/org/freedesktop/login1",
-                        "org.freedesktop.login1.Manager",
-                        "ListSeats",
-                        &error, &reply,
-                        NULL);
+        r = bus_call_method(bus, bus_login_mgr, "ListSeats", &error, &reply, NULL);
         if (r < 0)
                 return log_error_errno(r, "Failed to list seats: %s", bus_error_message(&error, r));
 
@@ -825,7 +799,15 @@ static int show_properties(sd_bus *bus, const char *path, bool *new_line) {
 
         *new_line = true;
 
-        r = bus_print_all_properties(bus, "org.freedesktop.login1", path, print_property, arg_property, arg_value, arg_all, NULL);
+        r = bus_print_all_properties(
+                        bus,
+                        "org.freedesktop.login1",
+                        path,
+                        print_property,
+                        arg_property,
+                        arg_value,
+                        arg_all,
+                        NULL);
         if (r < 0)
                 return bus_log_parse_error(r);
 
@@ -835,7 +817,7 @@ static int show_properties(sd_bus *bus, const char *path, bool *new_line) {
 static int show_session(int argc, char *argv[], void *userdata) {
         bool properties, new_line = false;
         sd_bus *bus = userdata;
-        int r, i;
+        int r;
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         _cleanup_free_ char *path = NULL;
 
@@ -854,7 +836,7 @@ static int show_session(int argc, char *argv[], void *userdata) {
                 return print_session_status_info(bus, "/org/freedesktop/login1/session/auto", &new_line);
         }
 
-        for (i = 1; i < argc; i++) {
+        for (int i = 1; i < argc; i++) {
                 r = get_session_path(bus, argv[i], &error, &path);
                 if (r < 0)
                         return log_error_errno(r, "Failed to get session path: %s", bus_error_message(&error, r));
@@ -874,7 +856,7 @@ static int show_session(int argc, char *argv[], void *userdata) {
 static int show_user(int argc, char *argv[], void *userdata) {
         bool properties, new_line = false;
         sd_bus *bus = userdata;
-        int r, i;
+        int r;
 
         assert(bus);
         assert(argv);
@@ -891,7 +873,7 @@ static int show_user(int argc, char *argv[], void *userdata) {
                 return print_user_status_info(bus, "/org/freedesktop/login1/user/self", &new_line);
         }
 
-        for (i = 1; i < argc; i++) {
+        for (int i = 1; i < argc; i++) {
                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
                 _cleanup_(sd_bus_message_unrefp) sd_bus_message * reply = NULL;
                 const char *path = NULL;
@@ -931,7 +913,7 @@ static int show_user(int argc, char *argv[], void *userdata) {
 static int show_seat(int argc, char *argv[], void *userdata) {
         bool properties, new_line = false;
         sd_bus *bus = userdata;
-        int r, i;
+        int r;
 
         assert(bus);
         assert(argv);
@@ -948,19 +930,12 @@ static int show_seat(int argc, char *argv[], void *userdata) {
                 return print_seat_status_info(bus, "/org/freedesktop/login1/seat/auto", &new_line);
         }
 
-        for (i = 1; i < argc; i++) {
+        for (int i = 1; i < argc; i++) {
                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
                 _cleanup_(sd_bus_message_unrefp) sd_bus_message * reply = NULL;
                 const char *path = NULL;
 
-                r = sd_bus_call_method(
-                                bus,
-                                "org.freedesktop.login1",
-                                "/org/freedesktop/login1",
-                                "org.freedesktop.login1.Manager",
-                                "GetSeat",
-                                &error, &reply,
-                                "s", argv[i]);
+                r = bus_call_method(bus, bus_login_mgr, "GetSeat", &error, &reply, "s", argv[i]);
                 if (r < 0)
                         return log_error_errno(r, "Failed to get seat: %s", bus_error_message(&error, r));
 
@@ -983,7 +958,7 @@ static int show_seat(int argc, char *argv[], void *userdata) {
 static int activate(int argc, char *argv[], void *userdata) {
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         sd_bus *bus = userdata;
-        int r, i;
+        int r;
 
         assert(bus);
         assert(argv);
@@ -1007,13 +982,11 @@ static int activate(int argc, char *argv[], void *userdata) {
                 return 0;
         }
 
-        for (i = 1; i < argc; i++) {
+        for (int i = 1; i < argc; i++) {
 
-                r = sd_bus_call_method(
+                r = bus_call_method(
                                 bus,
-                                "org.freedesktop.login1",
-                                "/org/freedesktop/login1",
-                                "org.freedesktop.login1.Manager",
+                                bus_login_mgr,
                                 streq(argv[0], "lock-session")      ? "LockSession" :
                                 streq(argv[0], "unlock-session")    ? "UnlockSession" :
                                 streq(argv[0], "terminate-session") ? "TerminateSession" :
@@ -1030,7 +1003,7 @@ static int activate(int argc, char *argv[], void *userdata) {
 static int kill_session(int argc, char *argv[], void *userdata) {
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         sd_bus *bus = userdata;
-        int r, i;
+        int r;
 
         assert(bus);
         assert(argv);
@@ -1040,16 +1013,14 @@ static int kill_session(int argc, char *argv[], void *userdata) {
         if (!arg_kill_who)
                 arg_kill_who = "all";
 
-        for (i = 1; i < argc; i++) {
+        for (int i = 1; i < argc; i++) {
 
-                r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.login1",
-                        "/org/freedesktop/login1",
-                        "org.freedesktop.login1.Manager",
-                        "KillSession",
-                        &error, NULL,
-                        "ssi", argv[i], arg_kill_who, arg_signal);
+                r = bus_call_method(
+                                bus,
+                                bus_login_mgr,
+                                "KillSession",
+                                &error, NULL,
+                                "ssi", argv[i], arg_kill_who, arg_signal);
                 if (r < 0)
                         return log_error_errno(r, "Could not kill session: %s", bus_error_message(&error, -r));
         }
@@ -1062,7 +1033,7 @@ static int enable_linger(int argc, char *argv[], void *userdata) {
         sd_bus *bus = userdata;
         char* short_argv[3];
         bool b;
-        int r, i;
+        int r;
 
         assert(bus);
         assert(argv);
@@ -1082,7 +1053,7 @@ static int enable_linger(int argc, char *argv[], void *userdata) {
                 argc = 2;
         }
 
-        for (i = 1; i < argc; i++) {
+        for (int i = 1; i < argc; i++) {
                 uid_t uid;
 
                 if (isempty(argv[i]))
@@ -1093,14 +1064,12 @@ static int enable_linger(int argc, char *argv[], void *userdata) {
                                 return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
                 }
 
-                r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.login1",
-                        "/org/freedesktop/login1",
-                        "org.freedesktop.login1.Manager",
-                        "SetUserLinger",
-                        &error, NULL,
-                        "ubb", (uint32_t) uid, b, true);
+                r = bus_call_method(
+                                bus,
+                                bus_login_mgr,
+                                "SetUserLinger",
+                                &error, NULL,
+                                "ubb", (uint32_t) uid, b, true);
                 if (r < 0)
                         return log_error_errno(r, "Could not enable linger: %s", bus_error_message(&error, -r));
         }
@@ -1111,28 +1080,21 @@ static int enable_linger(int argc, char *argv[], void *userdata) {
 static int terminate_user(int argc, char *argv[], void *userdata) {
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         sd_bus *bus = userdata;
-        int r, i;
+        int r;
 
         assert(bus);
         assert(argv);
 
         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
 
-        for (i = 1; i < argc; i++) {
+        for (int i = 1; i < argc; i++) {
                 uid_t uid;
 
                 r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL, 0);
                 if (r < 0)
                         return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
 
-                r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.login1",
-                        "/org/freedesktop/login1",
-                        "org.freedesktop.login1.Manager",
-                        "TerminateUser",
-                        &error, NULL,
-                        "u", (uint32_t) uid);
+                r = bus_call_method(bus, bus_login_mgr, "TerminateUser", &error, NULL, "u", (uint32_t) uid);
                 if (r < 0)
                         return log_error_errno(r, "Could not terminate user: %s", bus_error_message(&error, -r));
         }
@@ -1143,7 +1105,7 @@ static int terminate_user(int argc, char *argv[], void *userdata) {
 static int kill_user(int argc, char *argv[], void *userdata) {
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         sd_bus *bus = userdata;
-        int r, i;
+        int r;
 
         assert(bus);
         assert(argv);
@@ -1153,18 +1115,16 @@ static int kill_user(int argc, char *argv[], void *userdata) {
         if (!arg_kill_who)
                 arg_kill_who = "all";
 
-        for (i = 1; i < argc; i++) {
+        for (int i = 1; i < argc; i++) {
                 uid_t uid;
 
                 r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL, 0);
                 if (r < 0)
                         return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
 
-                r = sd_bus_call_method(
+                r = bus_call_method(
                         bus,
-                        "org.freedesktop.login1",
-                        "/org/freedesktop/login1",
-                        "org.freedesktop.login1.Manager",
+                        bus_login_mgr,
                         "KillUser",
                         &error, NULL,
                         "ui", (uint32_t) uid, arg_signal);
@@ -1178,24 +1138,21 @@ static int kill_user(int argc, char *argv[], void *userdata) {
 static int attach(int argc, char *argv[], void *userdata) {
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         sd_bus *bus = userdata;
-        int r, i;
+        int r;
 
         assert(bus);
         assert(argv);
 
         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
 
-        for (i = 2; i < argc; i++) {
+        for (int i = 2; i < argc; i++) {
 
-                r = sd_bus_call_method(
+                r = bus_call_method(
                         bus,
-                        "org.freedesktop.login1",
-                        "/org/freedesktop/login1",
-                        "org.freedesktop.login1.Manager",
+                        bus_login_mgr,
                         "AttachDevice",
                         &error, NULL,
                         "ssb", argv[1], argv[i], true);
-
                 if (r < 0)
                         return log_error_errno(r, "Could not attach device: %s", bus_error_message(&error, -r));
         }
@@ -1213,14 +1170,7 @@ static int flush_devices(int argc, char *argv[], void *userdata) {
 
         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.login1",
-                        "/org/freedesktop/login1",
-                        "org.freedesktop.login1.Manager",
-                        "FlushDevices",
-                        &error, NULL,
-                        "b", true);
+        r = bus_call_method(bus, bus_login_mgr, "FlushDevices", &error, NULL, "b", true);
         if (r < 0)
                 return log_error_errno(r, "Could not flush devices: %s", bus_error_message(&error, -r));
 
@@ -1237,11 +1187,9 @@ static int lock_sessions(int argc, char *argv[], void *userdata) {
 
         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
 
-        r = sd_bus_call_method(
+        r = bus_call_method(
                         bus,
-                        "org.freedesktop.login1",
-                        "/org/freedesktop/login1",
-                        "org.freedesktop.login1.Manager",
+                        bus_login_mgr,
                         streq(argv[0], "lock-sessions") ? "LockSessions" : "UnlockSessions",
                         &error, NULL,
                         NULL);
@@ -1254,23 +1202,16 @@ static int lock_sessions(int argc, char *argv[], void *userdata) {
 static int terminate_seat(int argc, char *argv[], void *userdata) {
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         sd_bus *bus = userdata;
-        int r, i;
+        int r;
 
         assert(bus);
         assert(argv);
 
         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
 
-        for (i = 1; i < argc; i++) {
+        for (int i = 1; i < argc; i++) {
 
-                r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.login1",
-                        "/org/freedesktop/login1",
-                        "org.freedesktop.login1.Manager",
-                        "TerminateSeat",
-                        &error, NULL,
-                        "s", argv[i]);
+                r = bus_call_method(bus, bus_login_mgr, "TerminateSeat", &error, NULL, "s", argv[i]);
                 if (r < 0)
                         return log_error_errno(r, "Could not terminate seat: %s", bus_error_message(&error, -r));
         }
@@ -1347,7 +1288,6 @@ static int help(int argc, char *argv[], void *userdata) {
 }
 
 static int parse_argv(int argc, char *argv[]) {
-
         enum {
                 ARG_VERSION = 0x100,
                 ARG_VALUE,
@@ -1486,7 +1426,6 @@ static int parse_argv(int argc, char *argv[]) {
 }
 
 static int loginctl_main(int argc, char *argv[], sd_bus *bus) {
-
         static const Verb verbs[] = {
                 { "help",              VERB_ANY, VERB_ANY, 0,            help              },
                 { "list-sessions",     VERB_ANY, 1,        VERB_DEFAULT, list_sessions     },
@@ -1523,9 +1462,7 @@ static int run(int argc, char *argv[]) {
         int r;
 
         setlocale(LC_ALL, "");
-        log_show_color(true);
-        log_parse_environment();
-        log_open();
+        log_setup_cli();
 
         /* The journal merging logic potentially needs a lot of fds. */
         (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE);
@@ -1538,7 +1475,7 @@ static int run(int argc, char *argv[]) {
 
         r = bus_connect_transport(arg_transport, arg_host, false, &bus);
         if (r < 0)
-                return log_error_errno(r, "Failed to create bus connection: %m");
+                return bus_log_connect_error(r);
 
         (void) sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
 
index ff192c53eb962f650fe7a9bee162b55601169d66..76af208af1a417687cdb055cc9bfc806750dc9ac 100644 (file)
@@ -206,7 +206,7 @@ int devnode_acl_all(const char *seat,
                         continue;
 
                 log_device_debug(d, "Found udev node %s for seat %s", node, seat);
-                r = set_put_strdup(nodes, node);
+                r = set_put_strdup(&nodes, node);
                 if (r < 0)
                         return r;
         }
index 3f4b65e1fdf16ab9333c1454d27b056e0a96cd92..450ec320443a4cf63bc3b28a01e6b7ec3713a4df 100644 (file)
@@ -174,15 +174,11 @@ static int set_add_message(Set **set, sd_bus_message *message) {
         if (r <= 0)
                 return r;
 
-        r = set_ensure_allocated(set, &bus_message_hash_ops);
-        if (r < 0)
-                return r;
-
-        r = set_put(*set, message);
-        if (r < 0)
+        r = set_ensure_put(set, &bus_message_hash_ops, message);
+        if (r <= 0)
                 return r;
-
         sd_bus_message_ref(message);
+
         return 1;
 }
 
index 22a42b077c0d6ff7a12f531382e1fa0c0f1b9401..480ec1927b03cc5b513815b26686fb9306823ad9 100644 (file)
@@ -4,9 +4,6 @@
 #include <sys/ioctl.h>
 #include <sys/types.h>
 #include <linux/vt.h>
-#if ENABLE_UTMP
-#include <utmpx.h>
-#endif
 
 #include "sd-device.h"
 
@@ -16,6 +13,7 @@
 #include "cgroup-util.h"
 #include "conf-parser.h"
 #include "device-util.h"
+#include "efi-loader.h"
 #include "errno-util.h"
 #include "fd-util.h"
 #include "limits-util.h"
@@ -28,6 +26,7 @@
 #include "udev-util.h"
 #include "user-util.h"
 #include "userdb.h"
+#include "utmp-wtmp.h"
 
 void manager_reset_config(Manager *m) {
         assert(m);
@@ -55,6 +54,7 @@ void manager_reset_config(Manager *m) {
         m->idle_action = HANDLE_IGNORE;
 
         m->runtime_dir_size = physical_memory_scale(10U, 100U); /* 10% */
+        m->runtime_dir_inodes = DIV_ROUND_UP(m->runtime_dir_size, 4096); /* 4k per inode */
         m->sessions_max = 8192;
         m->inhibitors_max = 8192;
 
@@ -67,11 +67,13 @@ void manager_reset_config(Manager *m) {
 int manager_parse_config_file(Manager *m) {
         assert(m);
 
-        return config_parse_many_nulstr(PKGSYSCONFDIR "/logind.conf",
-                                        CONF_PATHS_NULSTR("systemd/logind.conf.d"),
-                                        "Login\0",
-                                        config_item_perf_lookup, logind_gperf_lookup,
-                                        CONFIG_PARSE_WARN, m);
+        return config_parse_many_nulstr(
+                        PKGSYSCONFDIR "/logind.conf",
+                        CONF_PATHS_NULSTR("systemd/logind.conf.d"),
+                        "Login\0",
+                        config_item_perf_lookup, logind_gperf_lookup,
+                        CONFIG_PARSE_WARN, m,
+                        NULL);
 }
 
 int manager_add_device(Manager *m, const char *sysfs, bool master, Device **ret_device) {
@@ -171,7 +173,7 @@ int manager_add_user_by_name(
         assert(m);
         assert(name);
 
-        r = userdb_by_name(name, 0, &ur);
+        r = userdb_by_name(name, USERDB_AVOID_SHADOW, &ur);
         if (r < 0)
                 return r;
 
@@ -189,7 +191,7 @@ int manager_add_user_by_uid(
         assert(m);
         assert(uid_is_valid(uid));
 
-        r = userdb_by_uid(uid, 0, &ur);
+        r = userdb_by_uid(uid, USERDB_AVOID_SHADOW, &ur);
         if (r < 0)
                 return r;
 
@@ -597,10 +599,10 @@ static int manager_count_external_displays(Manager *m) {
                 if (sd_device_get_sysname(d, &nn) < 0)
                         continue;
 
-                /* Ignore internal displays: the type is encoded in the sysfs name, as the second dash separated item
-                 * (the first is the card name, the last the connector number). We implement a blacklist of external
-                 * displays here, rather than a whitelist of internal ones, to ensure we don't block suspends too
-                 * eagerly. */
+                /* Ignore internal displays: the type is encoded in the sysfs name, as the second dash
+                 * separated item (the first is the card name, the last the connector number). We implement a
+                 * deny list of external displays here, rather than an allow list of internal ones, to ensure
+                 * we don't block suspends too eagerly. */
                 dash = strchr(nn, '-');
                 if (!dash)
                         continue;
@@ -681,13 +683,14 @@ bool manager_all_buttons_ignored(Manager *m) {
 int manager_read_utmp(Manager *m) {
 #if ENABLE_UTMP
         int r;
+        _cleanup_(utxent_cleanup) bool utmpx = false;
 
         assert(m);
 
         if (utmpxname(_PATH_UTMPX) < 0)
                 return log_error_errno(errno, "Failed to set utmp path to " _PATH_UTMPX ": %m");
 
-        setutxent();
+        utmpx = utxent_start();
 
         for (;;) {
                 _cleanup_free_ char *t = NULL;
@@ -700,8 +703,7 @@ int manager_read_utmp(Manager *m) {
                 if (!u) {
                         if (errno != 0)
                                 log_warning_errno(errno, "Failed to read " _PATH_UTMPX ", ignoring: %m");
-                        r = 0;
-                        break;
+                        return 0;
                 }
 
                 if (u->ut_type != USER_PROCESS)
@@ -711,18 +713,14 @@ int manager_read_utmp(Manager *m) {
                         continue;
 
                 t = strndup(u->ut_line, sizeof(u->ut_line));
-                if (!t) {
-                        r = log_oom();
-                        break;
-                }
+                if (!t)
+                        return log_oom();
 
                 c = path_startswith(t, "/dev/");
                 if (c) {
                         r = free_and_strdup(&t, c);
-                        if (r < 0) {
-                                log_oom();
-                                break;
-                        }
+                        if (r < 0)
+                                return log_oom();
                 }
 
                 if (isempty(t))
@@ -752,8 +750,6 @@ int manager_read_utmp(Manager *m) {
                 log_debug("Acquired TTY information '%s' from utmp for session '%s'.", s->tty, s->id);
         }
 
-        endutxent();
-        return r;
 #else
         return 0;
 #endif
@@ -816,3 +812,27 @@ void manager_reconnect_utmp(Manager *m) {
         manager_connect_utmp(m);
 #endif
 }
+
+int manager_read_efi_boot_loader_entries(Manager *m) {
+#if ENABLE_EFI
+        int r;
+
+        assert(m);
+        if (m->efi_boot_loader_entries_set)
+                return 0;
+
+        r = efi_loader_get_entries(&m->efi_boot_loader_entries);
+        if (r == -ENOENT || ERRNO_IS_NOT_SUPPORTED(r)) {
+                log_debug_errno(r, "Boot loader reported no entries.");
+                m->efi_boot_loader_entries_set = true;
+                return 0;
+        }
+        if (r < 0)
+                return log_error_errno(r, "Failed to determine entries reported by boot loader: %m");
+
+        m->efi_boot_loader_entries_set = true;
+        return 1;
+#else
+        return 0;
+#endif
+}
index 762730d12253bfd87cb3d1b537251a6bb2882a76..75a48300adbcb212af606620405a183fe14c36d3 100644 (file)
@@ -12,6 +12,8 @@
 #include "bootspec.h"
 #include "bus-common-errors.h"
 #include "bus-error.h"
+#include "bus-get-properties.h"
+#include "bus-locator.h"
 #include "bus-polkit.h"
 #include "bus-unit-util.h"
 #include "bus-util.h"
@@ -885,7 +887,7 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
         if (r < 0)
                 goto fail;
 
-        session->type = t;
+        session->original_type = session->type = t;
         session->class = c;
         session->remote = remote;
         session->vtnr = vtnr;
@@ -1625,11 +1627,9 @@ static int execute_shutdown_or_sleep(
         if (w == INHIBIT_SHUTDOWN)
                 bus_manager_log_shutdown(m, unit_name);
 
-        r = sd_bus_call_method(
+        r = bus_call_method(
                         m->bus,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
+                        bus_systemd_mgr,
                         "StartUnit",
                         error,
                         &reply,
@@ -2753,8 +2753,6 @@ static int property_get_reboot_to_boot_loader_menu(
 
         r = getenv_bool("SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU");
         if (r == -ENXIO) {
-                _cleanup_free_ char *v = NULL;
-
                 /* EFI case: returns the current value of LoaderConfigTimeoutOneShot. Three cases are distuingished:
                  *
                  *     1. Variable not set, boot into boot loader menu is not enabled (we return UINT64_MAX to the user)
@@ -2762,20 +2760,10 @@ static int property_get_reboot_to_boot_loader_menu(
                  *     3. Variable set to numeric value formatted in ASCII, boot into boot loader menu with the specified timeout in seconds
                  */
 
-                r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderConfigTimeoutOneShot", &v);
+                r = efi_loader_get_config_timeout_one_shot(&x);
                 if (r < 0) {
                         if (r != -ENOENT)
-                                log_warning_errno(r, "Failed to read LoaderConfigTimeoutOneShot variable: %m");
-                } else {
-                        uint64_t sec;
-
-                        r = safe_atou64(v, &sec);
-                        if (r < 0)
-                                log_warning_errno(r, "Failed to parse LoaderConfigTimeoutOneShot value '%s': %m", v);
-                        else if (sec > (USEC_INFINITY / USEC_PER_SEC))
-                                log_warning("LoaderConfigTimeoutOneShot too large, ignoring: %m");
-                        else
-                                x = sec * USEC_PER_SEC; /* return in µs */
+                                log_warning_errno(r, "Failed to read LoaderConfigTimeoutOneShot variable, ignoring: %m");
                 }
 
         } else if (r < 0)
@@ -2934,24 +2922,25 @@ static int property_get_reboot_to_boot_loader_entry(
                 sd_bus_error *error) {
 
         _cleanup_free_ char *v = NULL;
+        Manager *m = userdata;
+        const char *x = NULL;
         int r;
 
         assert(bus);
         assert(reply);
-        assert(userdata);
+        assert(m);
 
         r = getenv_bool("SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY");
         if (r == -ENXIO) {
                 /* EFI case: let's read the LoaderEntryOneShot variable */
 
-                r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntryOneShot", &v);
+                r = efi_loader_update_entry_one_shot_cache(&m->efi_loader_entry_one_shot, &m->efi_loader_entry_one_shot_stat);
                 if (r < 0) {
                         if (r != -ENOENT)
-                                log_warning_errno(r, "Failed to read LoaderEntryOneShot variable: %m");
-                } else if (!efi_loader_entry_name_valid(v)) {
-                        log_warning("LoaderEntryOneShot contains invalid entry name '%s', ignoring.", v);
-                        v = mfree(v);
-                }
+                                log_warning_errno(r, "Failed to read LoaderEntryOneShot variable, ignoring: %m");
+                } else
+                        x = m->efi_loader_entry_one_shot;
+
         } else if (r < 0)
                 log_warning_errno(r, "Failed to parse $SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY: %m");
         else if (r > 0) {
@@ -2961,27 +2950,30 @@ static int property_get_reboot_to_boot_loader_entry(
                 r = read_one_line_file("/run/systemd/reboot-to-boot-loader-entry", &v);
                 if (r < 0) {
                         if (r != -ENOENT)
-                                log_warning_errno(r, "Failed to read /run/systemd/reboot-to-boot-loader-entry: %m");
-                } else if (!efi_loader_entry_name_valid(v)) {
+                                log_warning_errno(r, "Failed to read /run/systemd/reboot-to-boot-loader-entry, ignoring: %m");
+                } else if (!efi_loader_entry_name_valid(v))
                         log_warning("/run/systemd/reboot-to-boot-loader-entry is not valid, ignoring.");
-                        v = mfree(v);
-                }
+                else
+                        x = v;
         }
 
-        return sd_bus_message_append(reply, "s", v);
+        return sd_bus_message_append(reply, "s", x);
 }
 
-static int boot_loader_entry_exists(const char *id) {
+static int boot_loader_entry_exists(Manager *m, const char *id) {
         _cleanup_(boot_config_free) BootConfig config = {};
         int r;
 
+        assert(m);
         assert(id);
 
         r = boot_entries_load_config_auto(NULL, NULL, &config);
         if (r < 0 && r != -ENOKEY) /* don't complain if no GPT is found, hence skip ENOKEY */
                 return r;
 
-        (void) boot_entries_augment_from_loader(&config, true);
+        r = manager_read_efi_boot_loader_entries(m);
+        if (r >= 0)
+                (void) boot_entries_augment_from_loader(&config, m->efi_boot_loader_entries, true);
 
         return boot_config_has_entry(&config, id);
 }
@@ -3006,7 +2998,7 @@ static int method_set_reboot_to_boot_loader_entry(
         if (isempty(v))
                 v = NULL;
         else if (efi_loader_entry_name_valid(v)) {
-                r = boot_loader_entry_exists(v);
+                r = boot_loader_entry_exists(m, v);
                 if (r < 0)
                         return r;
                 if (r == 0)
@@ -3125,18 +3117,21 @@ static int property_get_boot_loader_entries(
                 sd_bus_error *error) {
 
         _cleanup_(boot_config_free) BootConfig config = {};
+        Manager *m = userdata;
         size_t i;
         int r;
 
         assert(bus);
         assert(reply);
-        assert(userdata);
+        assert(m);
 
         r = boot_entries_load_config_auto(NULL, NULL, &config);
         if (r < 0 && r != -ENOKEY) /* don't complain if there's no GPT found */
                 return r;
 
-        (void) boot_entries_augment_from_loader(&config, true);
+        r = manager_read_efi_boot_loader_entries(m);
+        if (r >= 0)
+                (void) boot_entries_augment_from_loader(&config, m->efi_boot_loader_entries, true);
 
         r = sd_bus_message_open_container(reply, 'a', "s");
         if (r < 0)
@@ -3322,7 +3317,7 @@ fail:
         return r;
 }
 
-const sd_bus_vtable manager_vtable[] = {
+static const sd_bus_vtable manager_vtable[] = {
         SD_BUS_VTABLE_START(0),
 
         SD_BUS_WRITABLE_PROPERTY("EnableWallMessages", "b", NULL, NULL, offsetof(Manager, enable_wall_messages), 0),
@@ -3361,76 +3356,409 @@ const sd_bus_vtable manager_vtable[] = {
         SD_BUS_PROPERTY("OnExternalPower", "b", property_get_on_external_power, 0, 0),
         SD_BUS_PROPERTY("RemoveIPC", "b", bus_property_get_bool, offsetof(Manager, remove_ipc), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("RuntimeDirectorySize", "t", NULL, offsetof(Manager, runtime_dir_size), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("RuntimeDirectoryInodesMax", "t", NULL, offsetof(Manager, runtime_dir_inodes), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("InhibitorsMax", "t", NULL, offsetof(Manager, inhibitors_max), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("NCurrentInhibitors", "t", property_get_hashmap_size, offsetof(Manager, inhibitors), 0),
         SD_BUS_PROPERTY("SessionsMax", "t", NULL, offsetof(Manager, sessions_max), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("NCurrentSessions", "t", property_get_hashmap_size, offsetof(Manager, sessions), 0),
         SD_BUS_PROPERTY("UserTasksMax", "t", property_get_compat_user_tasks_max, 0, SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
 
-        SD_BUS_METHOD("GetSession", "s", "o", method_get_session, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("GetSessionByPID", "u", "o", method_get_session_by_pid, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("GetUser", "u", "o", method_get_user, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("GetUserByPID", "u", "o", method_get_user_by_pid, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("GetSeat", "s", "o", method_get_seat, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("ListSessions", NULL, "a(susso)", method_list_sessions, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("ListUsers", NULL, "a(uso)", method_list_users, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("ListSeats", NULL, "a(so)", method_list_seats, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("ListInhibitors", NULL, "a(ssssuu)", method_list_inhibitors, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("CreateSession", "uusssssussbssa(sv)", "soshusub", method_create_session, 0),
-        SD_BUS_METHOD("ReleaseSession", "s", NULL, method_release_session, 0),
-        SD_BUS_METHOD("ActivateSession", "s", NULL, method_activate_session, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("ActivateSessionOnSeat", "ss", NULL, method_activate_session_on_seat, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("LockSession", "s", NULL, method_lock_session, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("UnlockSession", "s", NULL, method_lock_session, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("LockSessions", NULL, NULL, method_lock_sessions, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("UnlockSessions", NULL, NULL, method_lock_sessions, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("KillSession", "ssi", NULL, method_kill_session, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("KillUser", "ui", NULL, method_kill_user, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("TerminateSession", "s", NULL, method_terminate_session, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("TerminateUser", "u", NULL, method_terminate_user, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("TerminateSeat", "s", NULL, method_terminate_seat, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetUserLinger", "ubb", NULL, method_set_user_linger, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("AttachDevice", "ssb", NULL, method_attach_device, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("FlushDevices", "b", NULL, method_flush_devices, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("PowerOff", "b", NULL, method_poweroff, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("Reboot", "b", NULL, method_reboot, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("Halt", "b", NULL, method_halt, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("Suspend", "b", NULL, method_suspend, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("Hibernate", "b", NULL, method_hibernate, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("HybridSleep", "b", NULL, method_hybrid_sleep, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SuspendThenHibernate", "b", NULL, method_suspend_then_hibernate, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("CanPowerOff", NULL, "s", method_can_poweroff, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("CanReboot", NULL, "s", method_can_reboot, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("CanHalt", NULL, "s", method_can_halt, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("CanSuspend", NULL, "s", method_can_suspend, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("CanHibernate", NULL, "s", method_can_hibernate, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("CanHybridSleep", NULL, "s", method_can_hybrid_sleep, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("CanSuspendThenHibernate", NULL, "s", method_can_suspend_then_hibernate, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("ScheduleShutdown", "st", NULL, method_schedule_shutdown, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("CancelScheduledShutdown", NULL, "b", method_cancel_scheduled_shutdown, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("Inhibit", "ssss", "h", method_inhibit, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("CanRebootParameter", NULL, "s", method_can_reboot_parameter, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetRebootParameter", "s", NULL, method_set_reboot_parameter, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("CanRebootToFirmwareSetup", NULL, "s", method_can_reboot_to_firmware_setup, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetRebootToFirmwareSetup", "b", NULL, method_set_reboot_to_firmware_setup, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("CanRebootToBootLoaderMenu", NULL, "s", method_can_reboot_to_boot_loader_menu, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetRebootToBootLoaderMenu", "t", NULL, method_set_reboot_to_boot_loader_menu, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("CanRebootToBootLoaderEntry", NULL, "s", method_can_reboot_to_boot_loader_entry, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetRebootToBootLoaderEntry", "s", NULL, method_set_reboot_to_boot_loader_entry, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetWallMessage", "sb", NULL, method_set_wall_message, SD_BUS_VTABLE_UNPRIVILEGED),
-
-        SD_BUS_SIGNAL("SessionNew", "so", 0),
-        SD_BUS_SIGNAL("SessionRemoved", "so", 0),
-        SD_BUS_SIGNAL("UserNew", "uo", 0),
-        SD_BUS_SIGNAL("UserRemoved", "uo", 0),
-        SD_BUS_SIGNAL("SeatNew", "so", 0),
-        SD_BUS_SIGNAL("SeatRemoved", "so", 0),
-        SD_BUS_SIGNAL("PrepareForShutdown", "b", 0),
-        SD_BUS_SIGNAL("PrepareForSleep", "b", 0),
+        SD_BUS_METHOD_WITH_NAMES("GetSession",
+                                 "s",
+                                 SD_BUS_PARAM(session_id),
+                                 "o",
+                                 SD_BUS_PARAM(object_path),
+                                 method_get_session,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("GetSessionByPID",
+                                 "u",
+                                 SD_BUS_PARAM(pid),
+                                 "o",
+                                 SD_BUS_PARAM(object_path),
+                                 method_get_session_by_pid,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("GetUser",
+                                 "u",
+                                 SD_BUS_PARAM(uid),
+                                 "o",
+                                 SD_BUS_PARAM(object_path),
+                                 method_get_user,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("GetUserByPID",
+                                 "u",
+                                 SD_BUS_PARAM(pid),
+                                 "o",
+                                 SD_BUS_PARAM(object_path),
+                                 method_get_user_by_pid,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("GetSeat",
+                                 "s",
+                                 SD_BUS_PARAM(seat_id),
+                                 "o",
+                                 SD_BUS_PARAM(object_path),
+                                 method_get_seat,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("ListSessions",
+                                 NULL,,
+                                 "a(susso)",
+                                 SD_BUS_PARAM(sessions),
+                                 method_list_sessions,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("ListUsers",
+                                 NULL,,
+                                 "a(uso)",
+                                 SD_BUS_PARAM(users),
+                                 method_list_users,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("ListSeats",
+                                 NULL,,
+                                 "a(so)",
+                                 SD_BUS_PARAM(seats),
+                                 method_list_seats,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("ListInhibitors",
+                                 NULL,,
+                                 "a(ssssuu)",
+                                 SD_BUS_PARAM(inhibitors),
+                                 method_list_inhibitors,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("CreateSession",
+                                 "uusssssussbssa(sv)",
+                                 SD_BUS_PARAM(uid)
+                                 SD_BUS_PARAM(pid)
+                                 SD_BUS_PARAM(service)
+                                 SD_BUS_PARAM(type)
+                                 SD_BUS_PARAM(class)
+                                 SD_BUS_PARAM(desktop)
+                                 SD_BUS_PARAM(seat_id)
+                                 SD_BUS_PARAM(vtnr)
+                                 SD_BUS_PARAM(tty)
+                                 SD_BUS_PARAM(display)
+                                 SD_BUS_PARAM(remote)
+                                 SD_BUS_PARAM(remote_user)
+                                 SD_BUS_PARAM(remote_host)
+                                 SD_BUS_PARAM(properties),
+                                 "soshusub",
+                                 SD_BUS_PARAM(session_id)
+                                 SD_BUS_PARAM(object_path)
+                                 SD_BUS_PARAM(runtime_path)
+                                 SD_BUS_PARAM(fifo_fd)
+                                 SD_BUS_PARAM(uid)
+                                 SD_BUS_PARAM(seat_id)
+                                 SD_BUS_PARAM(vtnr)
+                                 SD_BUS_PARAM(existing),
+                                 method_create_session,
+                                 0),
+        SD_BUS_METHOD_WITH_NAMES("ReleaseSession",
+                                 "s",
+                                 SD_BUS_PARAM(session_id),
+                                 NULL,,
+                                 method_release_session,
+                                 0),
+        SD_BUS_METHOD_WITH_NAMES("ActivateSession",
+                                 "s",
+                                 SD_BUS_PARAM(session_id),
+                                 NULL,,
+                                 method_activate_session,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("ActivateSessionOnSeat",
+                                 "ss",
+                                 SD_BUS_PARAM(session_id)
+                                 SD_BUS_PARAM(seat_id),
+                                 NULL,,
+                                 method_activate_session_on_seat,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("LockSession",
+                                 "s",
+                                 SD_BUS_PARAM(session_id),
+                                 NULL,,
+                                 method_lock_session,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("UnlockSession",
+                                 "s",
+                                 SD_BUS_PARAM(session_id),
+                                 NULL,,
+                                 method_lock_session,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("LockSessions",
+                      NULL,
+                      NULL,
+                      method_lock_sessions,
+                      SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("UnlockSessions",
+                      NULL,
+                      NULL,
+                      method_lock_sessions,
+                      SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("KillSession",
+                                 "ssi",
+                                 SD_BUS_PARAM(session_id)
+                                 SD_BUS_PARAM(who)
+                                 SD_BUS_PARAM(signal_number),
+                                 NULL,,
+                                 method_kill_session,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("KillUser",
+                                 "ui",
+                                 SD_BUS_PARAM(uid)
+                                 SD_BUS_PARAM(signal_number),
+                                 NULL,,
+                                 method_kill_user,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("TerminateSession",
+                                 "s",
+                                 SD_BUS_PARAM(session_id),
+                                 NULL,,
+                                 method_terminate_session,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("TerminateUser",
+                                 "u",
+                                 SD_BUS_PARAM(uid),
+                                 NULL,,
+                                 method_terminate_user,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("TerminateSeat",
+                                 "s",
+                                 SD_BUS_PARAM(seat_id),
+                                 NULL,,
+                                 method_terminate_seat,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("SetUserLinger",
+                                 "ubb",
+                                 SD_BUS_PARAM(uid)
+                                 SD_BUS_PARAM(enable)
+                                 SD_BUS_PARAM(interactive),
+                                 NULL,,
+                                 method_set_user_linger,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("AttachDevice",
+                                 "ssb",
+                                 SD_BUS_PARAM(seat_id)
+                                 SD_BUS_PARAM(sysfs_path)
+                                 SD_BUS_PARAM(interactive),
+                                 NULL,,
+                                 method_attach_device,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("FlushDevices",
+                                 "b",
+                                 SD_BUS_PARAM(interactive),
+                                 NULL,,
+                                 method_flush_devices,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("PowerOff",
+                                 "b",
+                                 SD_BUS_PARAM(interactive),
+                                 NULL,,
+                                 method_poweroff,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("Reboot",
+                                 "b",
+                                 SD_BUS_PARAM(interactive),
+                                 NULL,,
+                                 method_reboot,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("Halt",
+                                 "b",
+                                 SD_BUS_PARAM(interactive),
+                                 NULL,,
+                                 method_halt,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("Suspend",
+                                 "b",
+                                 SD_BUS_PARAM(interactive),
+                                 NULL,,
+                                 method_suspend,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("Hibernate",
+                                 "b",
+                                 SD_BUS_PARAM(interactive),
+                                 NULL,,
+                                 method_hibernate,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("HybridSleep",
+                                 "b",
+                                 SD_BUS_PARAM(interactive),
+                                 NULL,,
+                                 method_hybrid_sleep,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("SuspendThenHibernate",
+                                 "b",
+                                 SD_BUS_PARAM(interactive),
+                                 NULL,,
+                                 method_suspend_then_hibernate,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("CanPowerOff",
+                                 NULL,,
+                                 "s",
+                                 SD_BUS_PARAM(result),
+                                 method_can_poweroff,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("CanReboot",
+                                 NULL,,
+                                 "s",
+                                 SD_BUS_PARAM(result),
+                                 method_can_reboot,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("CanHalt",
+                                 NULL,,
+                                 "s",
+                                 SD_BUS_PARAM(result),
+                                 method_can_halt,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("CanSuspend",
+                                 NULL,,
+                                 "s",
+                                 SD_BUS_PARAM(result),
+                                 method_can_suspend,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("CanHibernate",
+                                 NULL,,
+                                 "s",
+                                 SD_BUS_PARAM(result),
+                                 method_can_hibernate,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("CanHybridSleep",
+                                 NULL,,
+                                 "s",
+                                 SD_BUS_PARAM(result),
+                                 method_can_hybrid_sleep,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("CanSuspendThenHibernate",
+                                 NULL,,
+                                 "s",
+                                 SD_BUS_PARAM(result),
+                                 method_can_suspend_then_hibernate,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("ScheduleShutdown",
+                                 "st",
+                                 SD_BUS_PARAM(type)
+                                 SD_BUS_PARAM(usec),
+                                 NULL,,
+                                 method_schedule_shutdown,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("CancelScheduledShutdown",
+                                 NULL,,
+                                 "b",
+                                 SD_BUS_PARAM(cancelled),
+                                 method_cancel_scheduled_shutdown,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("Inhibit",
+                                 "ssss",
+                                 SD_BUS_PARAM(what)
+                                 SD_BUS_PARAM(who)
+                                 SD_BUS_PARAM(why)
+                                 SD_BUS_PARAM(mode),
+                                 "h",
+                                 SD_BUS_PARAM(pipe_fd),
+                                 method_inhibit,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("CanRebootParameter",
+                                 NULL,,
+                                 "s",
+                                 SD_BUS_PARAM(result),
+                                 method_can_reboot_parameter,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("SetRebootParameter",
+                                 "s",
+                                 SD_BUS_PARAM(parameter),
+                                 NULL,,
+                                 method_set_reboot_parameter,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("CanRebootToFirmwareSetup",
+                                 NULL,,
+                                 "s",
+                                 SD_BUS_PARAM(result),
+                                 method_can_reboot_to_firmware_setup,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("SetRebootToFirmwareSetup",
+                                 "b",
+                                 SD_BUS_PARAM(enable),
+                                 NULL,,
+                                 method_set_reboot_to_firmware_setup,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("CanRebootToBootLoaderMenu",
+                                 NULL,,
+                                 "s",
+                                 SD_BUS_PARAM(result),
+                                 method_can_reboot_to_boot_loader_menu,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("SetRebootToBootLoaderMenu",
+                                 "t",
+                                 SD_BUS_PARAM(timeout),
+                                 NULL,,
+                                 method_set_reboot_to_boot_loader_menu,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("CanRebootToBootLoaderEntry",
+                                 NULL,,
+                                 "s",
+                                 SD_BUS_PARAM(result),
+                                 method_can_reboot_to_boot_loader_entry,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("SetRebootToBootLoaderEntry",
+                                 "s",
+                                 SD_BUS_PARAM(boot_loader_entry),
+                                 NULL,,
+                                 method_set_reboot_to_boot_loader_entry,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("SetWallMessage",
+                                 "sb",
+                                 SD_BUS_PARAM(wall_message)
+                                 SD_BUS_PARAM(enable),
+                                 NULL,,
+                                 method_set_wall_message,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+
+        SD_BUS_SIGNAL_WITH_NAMES("SessionNew",
+                                 "so",
+                                 SD_BUS_PARAM(session_id)
+                                 SD_BUS_PARAM(object_path),
+                                 0),
+        SD_BUS_SIGNAL_WITH_NAMES("SessionRemoved",
+                                 "so",
+                                 SD_BUS_PARAM(session_id)
+                                 SD_BUS_PARAM(object_path),
+                                 0),
+        SD_BUS_SIGNAL_WITH_NAMES("UserNew",
+                                 "uo",
+                                 SD_BUS_PARAM(uid)
+                                 SD_BUS_PARAM(object_path),
+                                 0),
+        SD_BUS_SIGNAL_WITH_NAMES("UserRemoved",
+                                 "uo",
+                                 SD_BUS_PARAM(uid)
+                                 SD_BUS_PARAM(object_path),
+                                 0),
+        SD_BUS_SIGNAL_WITH_NAMES("SeatNew",
+                                 "so",
+                                 SD_BUS_PARAM(seat_id)
+                                 SD_BUS_PARAM(object_path),
+                                 0),
+        SD_BUS_SIGNAL_WITH_NAMES("SeatRemoved",
+                                 "so",
+                                 SD_BUS_PARAM(seat_id)
+                                 SD_BUS_PARAM(object_path),
+                                 0),
+        SD_BUS_SIGNAL_WITH_NAMES("PrepareForShutdown",
+                                 "b",
+                                 SD_BUS_PARAM(start),
+                                 0),
+        SD_BUS_SIGNAL_WITH_NAMES("PrepareForSleep",
+                                 "b",
+                                 SD_BUS_PARAM(start),
+                                 0),
 
         SD_BUS_VTABLE_END
 };
 
+const BusObjectImplementation manager_object = {
+        "/org/freedesktop/login1",
+        "org.freedesktop.login1.Manager",
+        .vtables = BUS_VTABLES(manager_vtable),
+        .children = BUS_IMPLEMENTATIONS(&seat_object,
+                                        &session_object,
+                                        &user_object),
+};
+
 static int session_jobs_reply(Session *s, uint32_t jid, const char *unit, const char *result) {
         assert(s);
         assert(unit);
@@ -3649,13 +3977,7 @@ int manager_start_scope(
         assert(pid > 1);
         assert(job);
 
-        r = sd_bus_message_new_method_call(
-                        manager->bus,
-                        &m,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "StartTransientUnit");
+        r = bus_message_new_method_call(manager->bus, &m, bus_systemd_mgr, "StartTransientUnit");
         if (r < 0)
                 return r;
 
@@ -3742,11 +4064,9 @@ int manager_start_unit(Manager *manager, const char *unit, sd_bus_error *error,
         assert(unit);
         assert(job);
 
-        r = sd_bus_call_method(
+        r = bus_call_method(
                         manager->bus,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
+                        bus_systemd_mgr,
                         "StartUnit",
                         error,
                         &reply,
@@ -3765,11 +4085,9 @@ int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, c
         assert(unit);
         assert(job);
 
-        r = sd_bus_call_method(
+        r = bus_call_method(
                         manager->bus,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
+                        bus_systemd_mgr,
                         "StopUnit",
                         error,
                         &reply,
@@ -3827,11 +4145,9 @@ int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo
         assert(manager);
         assert(unit);
 
-        return sd_bus_call_method(
+        return bus_call_method(
                         manager->bus,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
+                        bus_systemd_mgr,
                         "KillUnit",
                         error,
                         NULL,
index 6c73a9654f69cb8eca6798c4b80556ca4f3703c3..77075875452997d19fc25d2c7b7b37fbb887573f 100644 (file)
@@ -3,9 +3,10 @@
 
 #include "sd-bus.h"
 
-#include "logind.h"
+#include "bus-object.h"
 #include "logind-session.h"
 #include "logind-user.h"
+#include "logind.h"
 
 int manager_get_session_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Session **ret);
 int manager_get_user_from_creds(Manager *m, sd_bus_message *message, uid_t uid, sd_bus_error *error, User **ret);
@@ -29,3 +30,5 @@ int manager_abandon_scope(Manager *manager, const char *scope, sd_bus_error *err
 int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, sd_bus_error *error);
 int manager_unit_is_active(Manager *manager, const char *unit, sd_bus_error *error);
 int manager_job_is_active(Manager *manager, const char *path, sd_bus_error *error);
+
+extern const BusObjectImplementation manager_object;
index 983795da40d598a93f8ce33aac902c77a56e4bb6..73d96ff4367c6a7c4e739c8af94e20c68e37bed6 100644 (file)
@@ -38,6 +38,7 @@ Login.HoldoffTimeoutSec,            config_parse_sec,                   0, offse
 Login.IdleAction,                   config_parse_handle_action,         0, offsetof(Manager, idle_action)
 Login.IdleActionSec,                config_parse_sec,                   0, offsetof(Manager, idle_action_usec)
 Login.RuntimeDirectorySize,         config_parse_tmpfs_size,            0, offsetof(Manager, runtime_dir_size)
+Login.RuntimeDirectoryInodesMax,    config_parse_uint64,                0, offsetof(Manager, runtime_dir_inodes)
 Login.RemoveIPC,                    config_parse_bool,                  0, offsetof(Manager, remove_ipc)
 Login.InhibitorsMax,                config_parse_uint64,                0, offsetof(Manager, inhibitors_max)
 Login.SessionsMax,                  config_parse_uint64,                0, offsetof(Manager, sessions_max)
index 0a5df937cc1a630523383bbf85ec98462647587f..a91765205c83ab47a6ba41e2f9db10939df18205 100644 (file)
@@ -4,6 +4,7 @@
 
 #include "alloc-util.h"
 #include "bus-common-errors.h"
+#include "bus-get-properties.h"
 #include "bus-label.h"
 #include "bus-polkit.h"
 #include "bus-util.h"
@@ -17,7 +18,7 @@
 #include "user-util.h"
 #include "util.h"
 
-static BUS_DEFINE_PROPERTY_GET(property_get_can_multi_session, "b", Seat, seat_can_multi_session);
+static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_const_true, "b", true);
 static BUS_DEFINE_PROPERTY_GET(property_get_can_tty, "b", Seat, seat_can_tty);
 static BUS_DEFINE_PROPERTY_GET(property_get_can_graphical, "b", Seat, seat_can_graphical);
 
@@ -291,29 +292,7 @@ static int method_switch_to_previous(sd_bus_message *message, void *userdata, sd
         return sd_bus_reply_method_return(message, NULL);
 }
 
-const sd_bus_vtable seat_vtable[] = {
-        SD_BUS_VTABLE_START(0),
-
-        SD_BUS_PROPERTY("Id", "s", NULL, offsetof(Seat, id), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("ActiveSession", "(so)", property_get_active_session, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
-        SD_BUS_PROPERTY("CanMultiSession", "b", property_get_can_multi_session, 0, SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("CanTTY", "b", property_get_can_tty, 0, SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("CanGraphical", "b", property_get_can_graphical, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
-        SD_BUS_PROPERTY("Sessions", "a(so)", property_get_sessions, 0, 0),
-        SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
-        SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
-        SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
-
-        SD_BUS_METHOD("Terminate", NULL, NULL, bus_seat_method_terminate, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("ActivateSession", "s", NULL, method_activate_session, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SwitchTo", "u", NULL, method_switch_to, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SwitchToNext", NULL, NULL, method_switch_to_next, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SwitchToPrevious", NULL, NULL, method_switch_to_previous, SD_BUS_VTABLE_UNPRIVILEGED),
-
-        SD_BUS_VTABLE_END
-};
-
-int seat_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
+static int seat_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
         _cleanup_free_ char *e = NULL;
         sd_bus_message *message;
         Manager *m = userdata;
@@ -361,7 +340,7 @@ char *seat_bus_path(Seat *s) {
         return strjoin("/org/freedesktop/login1/seat/", t);
 }
 
-int seat_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
+static int seat_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
         _cleanup_strv_free_ char **l = NULL;
         sd_bus_message *message;
         Manager *m = userdata;
@@ -466,3 +445,44 @@ int seat_send_changed(Seat *s, const char *properties, ...) {
 
         return sd_bus_emit_properties_changed_strv(s->manager->bus, p, "org.freedesktop.login1.Seat", l);
 }
+
+static const sd_bus_vtable seat_vtable[] = {
+        SD_BUS_VTABLE_START(0),
+
+        SD_BUS_PROPERTY("Id", "s", NULL, offsetof(Seat, id), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("ActiveSession", "(so)", property_get_active_session, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("CanMultiSession", "b", property_get_const_true, 0, SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
+        SD_BUS_PROPERTY("CanTTY", "b", property_get_can_tty, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("CanGraphical", "b", property_get_can_graphical, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("Sessions", "a(so)", property_get_sessions, 0, 0),
+        SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+
+        SD_BUS_METHOD("Terminate", NULL, NULL, bus_seat_method_terminate, SD_BUS_VTABLE_UNPRIVILEGED),
+
+        SD_BUS_METHOD_WITH_NAMES("ActivateSession",
+                                 "s",
+                                 SD_BUS_PARAM(session_id),
+                                 NULL,,
+                                 method_activate_session,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("SwitchTo",
+                                 "u",
+                                 SD_BUS_PARAM(vtnr),
+                                 NULL,,
+                                 method_switch_to,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+
+        SD_BUS_METHOD("SwitchToNext", NULL, NULL, method_switch_to_next, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("SwitchToPrevious", NULL, NULL, method_switch_to_previous, SD_BUS_VTABLE_UNPRIVILEGED),
+
+        SD_BUS_VTABLE_END
+};
+
+const BusObjectImplementation seat_object = {
+        "/org/freedesktop/login1/seat",
+        "org.freedesktop.login1.Seat",
+        .fallback_vtables = BUS_FALLBACK_VTABLES({seat_vtable, seat_object_find}),
+        .node_enumerator = seat_node_enumerator,
+};
index 2590f64922b8e4f831e779ca2daad8a04c5f46fa..6169cfe1edf734bf3e73f96902677741f4dabcda 100644 (file)
@@ -3,12 +3,11 @@
 
 #include "sd-bus.h"
 
+#include "bus-object.h"
 #include "logind-seat.h"
 
-extern const sd_bus_vtable seat_vtable[];
+extern const BusObjectImplementation seat_object;
 
-int seat_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error);
-int seat_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error);
 char *seat_bus_path(Seat *s);
 
 int seat_send_signal(Seat *s, bool new_seat);
index 394f89dd7e0158d1300b73b5ac11e94f428c7af7..157fc9423cbd804b13a798d4a8d155439c7ee577 100644 (file)
@@ -104,11 +104,10 @@ int seat_save(Seat *s) {
         fprintf(f,
                 "# This is private data. Do not parse.\n"
                 "IS_SEAT0=%i\n"
-                "CAN_MULTI_SESSION=%i\n"
+                "CAN_MULTI_SESSION=1\n"
                 "CAN_TTY=%i\n"
                 "CAN_GRAPHICAL=%i\n",
                 seat_is_seat0(s),
-                seat_can_multi_session(s),
                 seat_can_tty(s),
                 seat_can_graphical(s));
 
@@ -558,12 +557,6 @@ bool seat_is_seat0(Seat *s) {
         return s->manager->seat0 == s;
 }
 
-bool seat_can_multi_session(Seat *s) {
-        assert(s);
-
-        return seat_has_vts(s);
-}
-
 bool seat_can_tty(Seat *s) {
         assert(s);
 
index 64cdf2f25ae78fc9372432cb20f26f3f9570cf73..f4b57ce8d2272180b978607d9ccf41cf8ef0a151 100644 (file)
@@ -51,7 +51,6 @@ void seat_claim_position(Seat *s, Session *session, unsigned pos);
 
 bool seat_has_vts(Seat *s);
 bool seat_is_seat0(Seat *s);
-bool seat_can_multi_session(Seat *s);
 bool seat_can_tty(Seat *s);
 bool seat_has_master_device(Seat *s);
 bool seat_can_graphical(Seat *s);
index 80ec89ba0a0084eeef3cb08f5e881d864fea7aa4..5ed20ebd26cd14f675fd371ed585f5fa22496215 100644 (file)
@@ -4,6 +4,7 @@
 
 #include "alloc-util.h"
 #include "bus-common-errors.h"
+#include "bus-get-properties.h"
 #include "bus-label.h"
 #include "bus-polkit.h"
 #include "bus-util.h"
@@ -393,6 +394,32 @@ static int method_release_control(sd_bus_message *message, void *userdata, sd_bu
         return sd_bus_reply_method_return(message, NULL);
 }
 
+static int method_set_type(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        Session *s = userdata;
+        const char *t;
+        SessionType type;
+        int r;
+
+        assert(message);
+        assert(s);
+
+        r = sd_bus_message_read(message, "s", &t);
+        if (r < 0)
+                return r;
+
+        type = session_type_from_string(t);
+        if (type < 0)
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+                                         "Invalid session type '%s'", t);
+
+        if (!session_is_controller(s, sd_bus_message_get_sender(message)))
+                return sd_bus_error_setf(error, BUS_ERROR_NOT_IN_CONTROL, "You must be in control of this session to set type");
+
+        session_set_type(s, type);
+
+        return sd_bus_reply_method_return(message, NULL);
+}
+
 static int method_take_device(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         Session *s = userdata;
         uint32_t major, minor;
@@ -555,57 +582,7 @@ static int method_set_brightness(sd_bus_message *message, void *userdata, sd_bus
         return 1;
 }
 
-const sd_bus_vtable session_vtable[] = {
-        SD_BUS_VTABLE_START(0),
-
-        SD_BUS_PROPERTY("Id", "s", NULL, offsetof(Session, id), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("User", "(uo)", property_get_user, 0, SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("Name", "s", property_get_name, 0, SD_BUS_VTABLE_PROPERTY_CONST),
-        BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Session, timestamp), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("VTNr", "u", NULL, offsetof(Session, vtnr), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("Seat", "(so)", property_get_seat, 0, SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("TTY", "s", NULL, offsetof(Session, tty), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("Display", "s", NULL, offsetof(Session, display), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("Remote", "b", bus_property_get_bool, offsetof(Session, remote), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("RemoteHost", "s", NULL, offsetof(Session, remote_host), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("RemoteUser", "s", NULL, offsetof(Session, remote_user), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Session, service), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("Desktop", "s", NULL, offsetof(Session, desktop), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Session, scope), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("Leader", "u", bus_property_get_pid, offsetof(Session, leader), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("Audit", "u", NULL, offsetof(Session, audit_id), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Session, type), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Session, class), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("Active", "b", property_get_active, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
-        SD_BUS_PROPERTY("State", "s", property_get_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
-        SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
-        SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
-        SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
-        SD_BUS_PROPERTY("LockedHint", "b", property_get_locked_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
-
-        SD_BUS_METHOD("Terminate", NULL, NULL, bus_session_method_terminate, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("Activate", NULL, NULL, bus_session_method_activate, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("Lock", NULL, NULL, bus_session_method_lock, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("Unlock", NULL, NULL, bus_session_method_lock, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetIdleHint", "b", NULL, method_set_idle_hint, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetLockedHint", "b", NULL, method_set_locked_hint, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("Kill", "si", NULL, bus_session_method_kill, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("TakeControl", "b", NULL, method_take_control, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("ReleaseControl", NULL, NULL, method_release_control, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("TakeDevice", "uu", "hb", method_take_device, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("ReleaseDevice", "uu", NULL, method_release_device, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("PauseDeviceComplete", "uu", NULL, method_pause_device_complete, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetBrightness", "ssu", NULL, method_set_brightness, SD_BUS_VTABLE_UNPRIVILEGED),
-
-        SD_BUS_SIGNAL("PauseDevice", "uus", 0),
-        SD_BUS_SIGNAL("ResumeDevice", "uuh", 0),
-        SD_BUS_SIGNAL("Lock", NULL, 0),
-        SD_BUS_SIGNAL("Unlock", NULL, 0),
-
-        SD_BUS_VTABLE_END
-};
-
-int session_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
+static int session_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
         _cleanup_free_ char *e = NULL;
         sd_bus_message *message;
         Manager *m = userdata;
@@ -653,7 +630,7 @@ char *session_bus_path(Session *s) {
         return strjoin("/org/freedesktop/login1/session/", t);
 }
 
-int session_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
+static int session_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
         _cleanup_strv_free_ char **l = NULL;
         sd_bus_message *message;
         Manager *m = userdata;
@@ -854,3 +831,144 @@ int session_send_create_reply(Session *s, sd_bus_error *error) {
                         (uint32_t) s->vtnr,
                         false);
 }
+
+static const sd_bus_vtable session_vtable[] = {
+        SD_BUS_VTABLE_START(0),
+
+        SD_BUS_PROPERTY("Id", "s", NULL, offsetof(Session, id), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("User", "(uo)", property_get_user, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("Name", "s", property_get_name, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+        BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Session, timestamp), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("VTNr", "u", NULL, offsetof(Session, vtnr), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("Seat", "(so)", property_get_seat, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("TTY", "s", NULL, offsetof(Session, tty), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("Display", "s", NULL, offsetof(Session, display), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("Remote", "b", bus_property_get_bool, offsetof(Session, remote), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("RemoteHost", "s", NULL, offsetof(Session, remote_host), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("RemoteUser", "s", NULL, offsetof(Session, remote_user), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Session, service), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("Desktop", "s", NULL, offsetof(Session, desktop), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Session, scope), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("Leader", "u", bus_property_get_pid, offsetof(Session, leader), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("Audit", "u", NULL, offsetof(Session, audit_id), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Session, type), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Session, class), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("Active", "b", property_get_active, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("State", "s", property_get_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("LockedHint", "b", property_get_locked_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+
+        SD_BUS_METHOD("Terminate",
+                      NULL,
+                      NULL,
+                      bus_session_method_terminate,
+                      SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("Activate",
+                      NULL,
+                      NULL,
+                      bus_session_method_activate,
+                      SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("Lock",
+                      NULL,
+                      NULL,
+                      bus_session_method_lock,
+                      SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("Unlock",
+                      NULL,
+                      NULL,
+                      bus_session_method_lock,
+                      SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("SetIdleHint",
+                                 "b",
+                                 SD_BUS_PARAM(idle),
+                                 NULL,,
+                                 method_set_idle_hint,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("SetLockedHint",
+                                 "b",
+                                 SD_BUS_PARAM(locked),
+                                 NULL,,
+                                 method_set_locked_hint,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("Kill",
+                                 "si",
+                                 SD_BUS_PARAM(who)
+                                 SD_BUS_PARAM(signal_number),
+                                 NULL,,
+                                 bus_session_method_kill,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("TakeControl",
+                                 "b",
+                                 SD_BUS_PARAM(force),
+                                 NULL,,
+                                 method_take_control,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("ReleaseControl",
+                      NULL,
+                      NULL,
+                      method_release_control,
+                      SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("SetType",
+                                 "s",
+                                 SD_BUS_PARAM(type),
+                                 NULL,,
+                                 method_set_type,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("TakeDevice",
+                                 "uu",
+                                 SD_BUS_PARAM(major)
+                                 SD_BUS_PARAM(minor),
+                                 "hb",
+                                 SD_BUS_PARAM(fd)
+                                 SD_BUS_PARAM(inactive),
+                                 method_take_device,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("ReleaseDevice",
+                                 "uu",
+                                 SD_BUS_PARAM(major)
+                                 SD_BUS_PARAM(minor),
+                                 NULL,,
+                                 method_release_device,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("PauseDeviceComplete",
+                                 "uu",
+                                 SD_BUS_PARAM(major)
+                                 SD_BUS_PARAM(minor),
+                                 NULL,,
+                                 method_pause_device_complete,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("SetBrightness",
+                                 "ssu",
+                                 SD_BUS_PARAM(subsystem)
+                                 SD_BUS_PARAM(name)
+                                 SD_BUS_PARAM(brightness),
+                                 NULL,,
+                                 method_set_brightness,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+
+        SD_BUS_SIGNAL_WITH_NAMES("PauseDevice",
+                                 "uus",
+                                 SD_BUS_PARAM(major)
+                                 SD_BUS_PARAM(minor)
+                                 SD_BUS_PARAM(type),
+                                 0),
+        SD_BUS_SIGNAL_WITH_NAMES("ResumeDevice",
+                                 "uuh",
+                                 SD_BUS_PARAM(major)
+                                 SD_BUS_PARAM(minor)
+                                 SD_BUS_PARAM(fd),
+                                 0),
+        SD_BUS_SIGNAL("Lock", NULL, 0),
+        SD_BUS_SIGNAL("Unlock", NULL, 0),
+
+        SD_BUS_VTABLE_END
+};
+
+const BusObjectImplementation session_object = {
+        "/org/freedesktop/login1/session",
+        "org.freedesktop.login1.Session",
+        .fallback_vtables = BUS_FALLBACK_VTABLES({session_vtable, session_object_find}),
+        .node_enumerator = session_node_enumerator,
+};
index 9d2315cc60d482dfffdc956d81e7d462e2227e2d..97f7c413cbff8c2c00e4ab5928768e268762d71c 100644 (file)
@@ -3,11 +3,11 @@
 
 #include "sd-bus.h"
 
+#include "bus-object.h"
 #include "logind-session.h"
 
-extern const sd_bus_vtable session_vtable[];
-int session_node_enumerator(sd_bus *bus, const char *path,void *userdata, char ***nodes, sd_bus_error *error);
-int session_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error);
+extern const BusObjectImplementation session_object;
+
 char *session_bus_path(Session *s);
 
 int session_send_signal(Session *s, bool new_session);
index 48505122a9decf670e04c1ea6401cc97131b8e1d..5c4149e1bd377328496ab2c423fe7b47bdeb3562 100644 (file)
@@ -240,6 +240,9 @@ int session_save(Session *s) {
         if (s->type >= 0)
                 fprintf(f, "TYPE=%s\n", session_type_to_string(s->type));
 
+        if (s->original_type >= 0)
+                fprintf(f, "ORIGINAL_TYPE=%s\n", session_type_to_string(s->original_type));
+
         if (s->class >= 0)
                 fprintf(f, "CLASS=%s\n", session_class_to_string(s->class));
 
@@ -402,6 +405,7 @@ int session_load(Session *s) {
                 *position = NULL,
                 *leader = NULL,
                 *type = NULL,
+                *original_type = NULL,
                 *class = NULL,
                 *uid = NULL,
                 *realtime = NULL,
@@ -433,6 +437,7 @@ int session_load(Session *s) {
                            "POSITION",       &position,
                            "LEADER",         &leader,
                            "TYPE",           &type,
+                           "ORIGINAL_TYPE",  &original_type,
                            "CLASS",          &class,
                            "UID",            &uid,
                            "REALTIME",       &realtime,
@@ -529,6 +534,16 @@ int session_load(Session *s) {
                         s->type = t;
         }
 
+        if (original_type) {
+                SessionType ot;
+
+                ot = session_type_from_string(original_type);
+                if (ot >= 0)
+                        s->original_type = ot;
+        } else
+                /* Pre-v246 compat: initialize original_type if not set in the state file */
+                s->original_type = s->type;
+
         if (class) {
                 SessionClass c;
 
@@ -1018,6 +1033,18 @@ void session_set_locked_hint(Session *s, bool b) {
         session_send_changed(s, "LockedHint", NULL);
 }
 
+void session_set_type(Session *s, SessionType t) {
+        assert(s);
+
+        if (s->type == t)
+                return;
+
+        s->type = t;
+        session_save(s);
+
+        session_send_changed(s, "Type", NULL);
+}
+
 static int session_dispatch_fifo(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
         Session *s = userdata;
 
@@ -1385,6 +1412,7 @@ void session_drop_controller(Session *s) {
                 return;
 
         s->track = sd_bus_track_unref(s->track);
+        session_set_type(s, s->original_type);
         session_release_controller(s, false);
         session_save(s);
         session_restore_vt(s);
index c51392bef655ef533c8d5ed95313aeabab69d269..b87c7316721353360db6a7df0c3b4198e8726d8f 100644 (file)
@@ -61,6 +61,7 @@ struct Session {
         const char *id;
         unsigned position;
         SessionType type;
+        SessionType original_type;
         SessionClass class;
 
         char *state_file;
@@ -135,6 +136,7 @@ int session_get_idle_hint(Session *s, dual_timestamp *t);
 int session_set_idle_hint(Session *s, bool b);
 int session_get_locked_hint(Session *s);
 void session_set_locked_hint(Session *s, bool b);
+void session_set_type(Session *s, SessionType t);
 int session_create_fifo(Session *s);
 int session_start(Session *s, sd_bus_message *properties, sd_bus_error *error);
 int session_stop(Session *s, bool force);
index 9bf68a610e409ac2f9cef55e673008c19864f0ac..237723729e38c17bd0c240ec1027f6765f537c2d 100644 (file)
@@ -3,6 +3,7 @@
 #include <errno.h>
 
 #include "alloc-util.h"
+#include "bus-get-properties.h"
 #include "bus-polkit.h"
 #include "bus-util.h"
 #include "format-util.h"
@@ -258,31 +259,7 @@ int bus_user_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *
         return sd_bus_reply_method_return(message, NULL);
 }
 
-const sd_bus_vtable user_vtable[] = {
-        SD_BUS_VTABLE_START(0),
-
-        SD_BUS_PROPERTY("UID", "u", property_get_uid, 0, SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("GID", "u", property_get_gid, 0, SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("Name", "s", property_get_name, 0, SD_BUS_VTABLE_PROPERTY_CONST),
-        BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(User, timestamp), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("RuntimePath", "s", NULL, offsetof(User, runtime_path), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("Service", "s", NULL, offsetof(User, service), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("Slice", "s", NULL, offsetof(User, slice), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("Display", "(so)", property_get_display, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
-        SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0),
-        SD_BUS_PROPERTY("Sessions", "a(so)", property_get_sessions, 0, 0),
-        SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
-        SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
-        SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
-        SD_BUS_PROPERTY("Linger", "b", property_get_linger, 0, 0),
-
-        SD_BUS_METHOD("Terminate", NULL, NULL, bus_user_method_terminate, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("Kill", "i", NULL, bus_user_method_kill, SD_BUS_VTABLE_UNPRIVILEGED),
-
-        SD_BUS_VTABLE_END
-};
-
-int user_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
+static int user_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
         Manager *m = userdata;
         uid_t uid;
         User *user;
@@ -337,7 +314,7 @@ char *user_bus_path(User *u) {
         return s;
 }
 
-int user_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
+static int user_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
         _cleanup_strv_free_ char **l = NULL;
         sd_bus_message *message;
         Manager *m = userdata;
@@ -386,6 +363,42 @@ int user_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***
         return 1;
 }
 
+static const sd_bus_vtable user_vtable[] = {
+        SD_BUS_VTABLE_START(0),
+
+        SD_BUS_PROPERTY("UID", "u", property_get_uid, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("GID", "u", property_get_gid, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("Name", "s", property_get_name, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+        BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(User, timestamp), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("RuntimePath", "s", NULL, offsetof(User, runtime_path), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("Service", "s", NULL, offsetof(User, service), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("Slice", "s", NULL, offsetof(User, slice), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("Display", "(so)", property_get_display, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0),
+        SD_BUS_PROPERTY("Sessions", "a(so)", property_get_sessions, 0, 0),
+        SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("Linger", "b", property_get_linger, 0, 0),
+
+        SD_BUS_METHOD("Terminate", NULL, NULL, bus_user_method_terminate, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("Kill",
+                                 "i",
+                                 SD_BUS_PARAM(signal_number),
+                                 NULL,,
+                                 bus_user_method_kill,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+
+        SD_BUS_VTABLE_END
+};
+
+const BusObjectImplementation user_object = {
+        "/org/freedesktop/login1/user",
+        "org.freedesktop.login1.User",
+        .fallback_vtables = BUS_FALLBACK_VTABLES({user_vtable, user_object_find}),
+        .node_enumerator = user_node_enumerator,
+};
+
 int user_send_signal(User *u, bool new_user) {
         _cleanup_free_ char *p = NULL;
 
index acfcb981cf3639de55cd1ef90e7d1eb0f5360d66..b3f990c5af720e608f689f28834a4387b6ae7ae3 100644 (file)
@@ -5,9 +5,8 @@
 
 #include "logind-user.h"
 
-extern const sd_bus_vtable user_vtable[];
-int user_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error);
-int user_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error);
+extern const BusObjectImplementation user_object;
+
 char *user_bus_path(User *s);
 
 int user_send_signal(User *u, bool new_user);
index caf7a169b72caebbaca0e634c06d320c59e13802..c50a083b0325624809cb03e84741aba920f9d5a4 100644 (file)
@@ -10,6 +10,8 @@
 
 #include "alloc-util.h"
 #include "bus-error.h"
+#include "bus-locator.h"
+#include "bus-log-control-api.h"
 #include "bus-polkit.h"
 #include "cgroup-util.h"
 #include "def.h"
@@ -27,6 +29,7 @@
 #include "parse-util.h"
 #include "process-util.h"
 #include "selinux-util.h"
+#include "service-util.h"
 #include "signal-util.h"
 #include "strv.h"
 #include "terminal-util.h"
@@ -165,6 +168,9 @@ static Manager* manager_unref(Manager *m) {
         free(m->wall_message);
         free(m->action_job);
 
+        strv_free(m->efi_boot_loader_entries);
+        free(m->efi_loader_entry_one_shot);
+
         return mfree(m);
 }
 
@@ -631,53 +637,19 @@ static int manager_connect_bus(Manager *m) {
         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/login1", "org.freedesktop.login1.Manager", manager_vtable, m);
-        if (r < 0)
-                return log_error_errno(r, "Failed to add manager object vtable: %m");
-
-        r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/login1/seat", "org.freedesktop.login1.Seat", seat_vtable, seat_object_find, m);
-        if (r < 0)
-                return log_error_errno(r, "Failed to add seat object vtable: %m");
-
-        r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/login1/seat", seat_node_enumerator, m);
-        if (r < 0)
-                return log_error_errno(r, "Failed to add seat enumerator: %m");
-
-        r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/login1/session", "org.freedesktop.login1.Session", session_vtable, session_object_find, m);
-        if (r < 0)
-                return log_error_errno(r, "Failed to add session object vtable: %m");
-
-        r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/login1/session", session_node_enumerator, m);
+        r = bus_add_implementation(m->bus, &manager_object, m);
         if (r < 0)
-                return log_error_errno(r, "Failed to add session enumerator: %m");
+                return r;
 
-        r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/login1/user", "org.freedesktop.login1.User", user_vtable, user_object_find, m);
+        r = bus_log_control_api_register(m->bus);
         if (r < 0)
-                return log_error_errno(r, "Failed to add user object vtable: %m");
+                return r;
 
-        r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/login1/user", user_node_enumerator, m);
-        if (r < 0)
-                return log_error_errno(r, "Failed to add user enumerator: %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);
+        r = bus_match_signal_async(m->bus, NULL, bus_systemd_mgr, "JobRemoved", match_job_removed, NULL, m);
         if (r < 0)
                 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);
+        r = bus_match_signal_async(m->bus, NULL, bus_systemd_mgr, "UnitRemoved", match_unit_removed, NULL, m);
         if (r < 0)
                 return log_error_errno(r, "Failed to request match for UnitRemoved: %m");
 
@@ -692,26 +664,11 @@ static int manager_connect_bus(Manager *m) {
         if (r < 0)
                 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);
+        r = bus_match_signal_async(m->bus, NULL, bus_systemd_mgr, "Reloading", match_reloading, NULL, m);
         if (r < 0)
                 return log_error_errno(r, "Failed to request match for Reloading: %m");
 
-        r = sd_bus_call_method_async(
-                        m->bus,
-                        NULL,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "Subscribe",
-                        NULL, NULL,
-                        NULL);
+        r = bus_call_method_async(m->bus, NULL, bus_systemd_mgr, "Subscribe", NULL, NULL, NULL);
         if (r < 0)
                 return log_error_errno(r, "Failed to enable subscription: %m");
 
@@ -1205,16 +1162,19 @@ static int run(int argc, char *argv[]) {
         log_set_facility(LOG_AUTH);
         log_setup_service();
 
-        umask(0022);
+        r = service_parse_argv("systemd-logind.service",
+                               "Manager for user logins and devices and privileged operations.",
+                               BUS_IMPLEMENTATIONS(&manager_object,
+                                                   &log_control_object),
+                               argc, argv);
+        if (r <= 0)
+                return r;
 
-        if (argc != 1) {
-                log_error("This program takes no arguments.");
-                return -EINVAL;
-        }
+        umask(0022);
 
         r = mac_selinux_init();
         if (r < 0)
-                return log_error_errno(r, "Could not initialize labelling: %m");
+                return r;
 
         /* Always create the directories people can create inotify watches in. Note that some applications might check
          * for the existence of /run/systemd/seats/ to determine whether logind is available, so please always make
index 1029e29bc73565f064fc521b0caf63051fbff354..ed1084b06e295578a628fda374716cbc73b1f80b 100644 (file)
@@ -32,6 +32,7 @@
 #IdleAction=ignore
 #IdleActionSec=30min
 #RuntimeDirectorySize=10%
+#RuntimeDirectoryInodes=400k
 #RemoveIPC=yes
 #InhibitorsMax=8192
 #SessionsMax=8192
index 7e4673d27d3c28b851643b137362a94f208f4e9d..e64ecce8e2810b8380465d7b2b2cb96000c0a89c 100644 (file)
@@ -120,8 +120,15 @@ struct Manager {
         sd_event_source *lid_switch_ignore_event_source;
 
         uint64_t runtime_dir_size;
+        uint64_t runtime_dir_inodes;
         uint64_t sessions_max;
         uint64_t inhibitors_max;
+
+        char **efi_boot_loader_entries;
+        bool efi_boot_loader_entries_set;
+
+        char *efi_loader_entry_one_shot;
+        struct stat efi_loader_entry_one_shot_stat;
 };
 
 void manager_reset_config(Manager *m);
@@ -157,8 +164,6 @@ int manager_read_utmp(Manager *m);
 void manager_connect_utmp(Manager *m);
 void manager_reconnect_utmp(Manager *m);
 
-extern const sd_bus_vtable manager_vtable[];
-
 /* gperf lookup function */
 const struct ConfigPerfItem* logind_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
 
@@ -169,3 +174,5 @@ CONFIG_PARSER_PROTOTYPE(config_parse_tmpfs_size);
 
 int manager_setup_wall_message_timer(Manager *m);
 bool logind_wall_tty_filter(const char *tty, void *userdata);
+
+int manager_read_efi_boot_loader_entries(Manager *m);
index 124a25810e313f2ab13c4d13f4b0fd4ad494dcc2..df46e417c8fc7baa59a02b878de4bacb4c1a5fd3 100644 (file)
                        send_interface="org.freedesktop.login1.Session"
                        send_member="ReleaseControl"/>
 
+                <allow send_destination="org.freedesktop.login1"
+                       send_interface="org.freedesktop.login1.Session"
+                       send_member="SetType"/>
+
                 <allow send_destination="org.freedesktop.login1"
                        send_interface="org.freedesktop.login1.Session"
                        send_member="TakeDevice"/>
index 51d0610bf62c36b549b43f87b2b3ea3f5e7fce6b..16f42895854df7fa5ec58c3bf5eb4728e1e680e0 100644 (file)
@@ -20,7 +20,7 @@
 #include "bus-common-errors.h"
 #include "bus-error.h"
 #include "bus-internal.h"
-#include "bus-util.h"
+#include "bus-locator.h"
 #include "cgroup-setup.h"
 #include "errno-util.h"
 #include "fd-util.h"
@@ -99,6 +99,7 @@ static int acquire_user_record(
 
         _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
         const char *username = NULL, *json = NULL;
+        _cleanup_free_ char *field = NULL;
         int r;
 
         assert(handle);
@@ -114,39 +115,18 @@ static int acquire_user_record(
                 return PAM_SERVICE_ERR;
         }
 
-        /* If pam_systemd_homed (or some other module) already acqired the user record we can reuse it
+        /* If pam_systemd_homed (or some other module) already acquired the user record we can reuse it
          * here. */
-        r = pam_get_data(handle, "systemd-user-record", (const void**) &json);
-        if (r != PAM_SUCCESS || !json) {
-                _cleanup_free_ char *formatted = NULL;
-
-                if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) {
-                        pam_syslog(handle, LOG_ERR, "Failed to get PAM user record data: %s", pam_strerror(handle, r));
-                        return r;
-                }
-
-                /* Request the record ourselves */
-                r = userdb_by_name(username, 0, &ur);
-                if (r < 0) {
-                        pam_syslog(handle, LOG_ERR, "Failed to get user record: %s", strerror_safe(r));
-                        return PAM_USER_UNKNOWN;
-                }
-
-                r = json_variant_format(ur->json, 0, &formatted);
-                if (r < 0) {
-                        pam_syslog(handle, LOG_ERR, "Failed to format user JSON: %s", strerror_safe(r));
-                        return PAM_SERVICE_ERR;
-                }
-
-                /* And cache it for everyone else */
-                r = pam_set_data(handle, "systemd-user-record", formatted, pam_cleanup_free);
-                if (r < 0) {
-                        pam_syslog(handle, LOG_ERR, "Failed to set PAM user record data: %s", pam_strerror(handle, r));
-                        return r;
-                }
+        field = strjoin("systemd-user-record-", username);
+        if (!field)
+                return pam_log_oom(handle);
 
-                TAKE_PTR(formatted);
-        } else {
+        r = pam_get_data(handle, field, (const void**) &json);
+        if (!IN_SET(r, PAM_SUCCESS, PAM_NO_MODULE_DATA)) {
+                pam_syslog(handle, LOG_ERR, "Failed to get PAM user record data: %s", pam_strerror(handle, r));
+                return r;
+        }
+        if (r == PAM_SUCCESS && json) {
                 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
 
                 /* Parse cached record */
@@ -171,6 +151,31 @@ static int acquire_user_record(
                         pam_syslog(handle, LOG_ERR, "Acquired user record does not match user name.");
                         return PAM_SERVICE_ERR;
                 }
+        } else {
+                _cleanup_free_ char *formatted = NULL;
+
+                /* Request the record ourselves */
+                r = userdb_by_name(username, 0, &ur);
+                if (r < 0) {
+                        pam_syslog(handle, LOG_ERR, "Failed to get user record: %s", strerror_safe(r));
+                        return PAM_USER_UNKNOWN;
+                }
+
+                r = json_variant_format(ur->json, 0, &formatted);
+                if (r < 0) {
+                        pam_syslog(handle, LOG_ERR, "Failed to format user JSON: %s", strerror_safe(r));
+                        return PAM_SERVICE_ERR;
+                }
+
+                /* And cache it for everyone else */
+                r = pam_set_data(handle, field, formatted, pam_cleanup_free);
+                if (r < 0) {
+                        pam_syslog(handle, LOG_ERR, "Failed to set PAM user record data '%s': %s",
+                                   field, pam_strerror(handle, r));
+                        return r;
+                }
+
+                TAKE_PTR(formatted);
         }
 
         if (!uid_is_valid(ur->uid)) {
@@ -280,7 +285,6 @@ static int get_seat_from_display(const char *display, const char **seat, uint32_
 
 static int export_legacy_dbus_address(
                 pam_handle_t *handle,
-                uid_t uid,
                 const char *runtime) {
 
         const char *s;
@@ -461,8 +465,17 @@ static bool validate_runtime_directory(pam_handle_t *handle, const char *path, u
         assert(handle);
         assert(path);
 
-        /* Just some extra paranoia: let's not set $XDG_RUNTIME_DIR if the directory we'd set it to isn't actually set
-         * up properly for us. */
+        /* Some extra paranoia: let's not set $XDG_RUNTIME_DIR if the directory we'd set it to isn't actually
+         * set up properly for us. This is supposed to provide a careful safety net for supporting su/sudo
+         * type transitions: in that case the UID changes, but the session and thus the user owning it
+         * doesn't change. Since the $XDG_RUNTIME_DIR lifecycle is bound to the session's user being logged
+         * in at least once we should be particularly careful when setting the environment variable, since
+         * otherwise we might end up setting $XDG_RUNTIME_DIR to some directory owned by the wrong user. */
+
+        if (!path_is_absolute(path)) {
+                pam_syslog(handle, LOG_ERR, "Provided runtime directory '%s' is not absolute.", path);
+                goto fail;
+        }
 
         if (lstat(path, &st) < 0) {
                 pam_syslog(handle, LOG_ERR, "Failed to stat() runtime directory '%s': %s", path, strerror_safe(errno));
@@ -580,9 +593,9 @@ static int apply_user_record_settings(pam_handle_t *handle, UserRecord *ur, bool
                 if (pam_getenv(handle, "LANG")) {
                         if (debug)
                                 pam_syslog(handle, LOG_DEBUG, "PAM environment variable $LANG already set, not changing based on user record.");
-                } else if (!locale_is_valid(ur->preferred_language)) {
+                } else if (locale_is_installed(ur->preferred_language) <= 0) {
                         if (debug)
-                                pam_syslog(handle, LOG_DEBUG, "Preferred language specified in user record is not valid locally, not setting $LANG.");
+                                pam_syslog(handle, LOG_DEBUG, "Preferred language specified in user record is not valid or not installed, not setting $LANG.");
                 } else {
                         _cleanup_free_ char *joined = NULL;
 
@@ -618,6 +631,29 @@ static int apply_user_record_settings(pam_handle_t *handle, UserRecord *ur, bool
         return PAM_SUCCESS;
 }
 
+static int configure_runtime_directory(
+                pam_handle_t *handle,
+                UserRecord *ur,
+                const char *rt) {
+
+        int r;
+
+        assert(handle);
+        assert(ur);
+        assert(rt);
+
+        if (!validate_runtime_directory(handle, rt, ur->uid))
+                return PAM_SUCCESS;
+
+        r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", rt, 0);
+        if (r != PAM_SUCCESS) {
+                pam_syslog(handle, LOG_ERR, "Failed to set runtime dir: %s", pam_strerror(handle, r));
+                return r;
+        }
+
+        return export_legacy_dbus_address(handle, rt);
+}
+
 _public_ PAM_EXTERN int pam_sm_open_session(
                 pam_handle_t *handle,
                 int flags,
@@ -643,10 +679,6 @@ _public_ PAM_EXTERN int pam_sm_open_session(
 
         assert(handle);
 
-        /* Make this a NOP on non-logind systems */
-        if (!logind_running())
-                return PAM_SUCCESS;
-
         if (parse_argv(handle,
                        argc, argv,
                        &class_pam,
@@ -662,6 +694,10 @@ _public_ PAM_EXTERN int pam_sm_open_session(
         if (r != PAM_SUCCESS)
                 return r;
 
+        /* Make most of this a NOP on non-logind systems */
+        if (!logind_running())
+                goto success;
+
         /* Make sure we don't enter a loop by talking to
          * systemd-logind when it is actually waiting for the
          * background to finish start-up. If the service is
@@ -673,23 +709,11 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                 char rt[STRLEN("/run/user/") + DECIMAL_STR_MAX(uid_t)];
 
                 xsprintf(rt, "/run/user/"UID_FMT, ur->uid);
-                if (validate_runtime_directory(handle, rt, ur->uid)) {
-                        r = pam_misc_setenv(handle, "XDG_RUNTIME_DIR", rt, 0);
-                        if (r != PAM_SUCCESS) {
-                                pam_syslog(handle, LOG_ERR, "Failed to set runtime dir: %s", pam_strerror(handle, r));
-                                return r;
-                        }
-                }
-
-                r = export_legacy_dbus_address(handle, ur->uid, rt);
-                if (r != PAM_SUCCESS)
-                        return r;
-
-                r = apply_user_record_settings(handle, ur, debug);
+                r = configure_runtime_directory(handle, ur, rt);
                 if (r != PAM_SUCCESS)
                         return r;
 
-                return PAM_SUCCESS;
+                goto success;
         }
 
         /* Otherwise, we ask logind to create a session for us */
@@ -789,13 +813,7 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                            strna(memory_max), strna(tasks_max), strna(cpu_weight), strna(io_weight), strna(runtime_max_sec));
         }
 
-        r = sd_bus_message_new_method_call(
-                        bus,
-                        &m,
-                        "org.freedesktop.login1",
-                        "/org/freedesktop/login1",
-                        "org.freedesktop.login1.Manager",
-                        "CreateSession");
+        r = bus_message_new_method_call(bus, &m, bus_login_mgr, "CreateSession");
         if (r < 0)
                 return pam_bus_log_create_error(handle, r);
 
@@ -849,7 +867,9 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                 if (sd_bus_error_has_name(&error, BUS_ERROR_SESSION_BUSY)) {
                         if (debug)
                                 pam_syslog(handle, LOG_DEBUG, "Not creating session: %s", bus_error_message(&error, r));
-                        return PAM_SUCCESS;
+
+                        /* We are already in a session, don't do anything */
+                        goto success;
                 } else {
                         pam_syslog(handle, LOG_ERR, "Failed to create session: %s", bus_error_message(&error, r));
                         return PAM_SESSION_ERR;
@@ -879,19 +899,11 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                 return r;
 
         if (original_uid == ur->uid) {
-                /* Don't set $XDG_RUNTIME_DIR if the user we now
-                 * authenticated for does not match the original user
-                 * of the session. We do this in order not to result
-                 * in privileged apps clobbering the runtime directory
-                 * unnecessarily. */
-
-                if (validate_runtime_directory(handle, runtime_path, ur->uid)) {
-                        r = update_environment(handle, "XDG_RUNTIME_DIR", runtime_path);
-                        if (r != PAM_SUCCESS)
-                                return r;
-                }
+                /* Don't set $XDG_RUNTIME_DIR if the user we now authenticated for does not match the
+                 * original user of the session. We do this in order not to result in privileged apps
+                 * clobbering the runtime directory unnecessarily. */
 
-                r = export_legacy_dbus_address(handle, ur->uid, runtime_path);
+                r = configure_runtime_directory(handle, ur, runtime_path);
                 if (r != PAM_SUCCESS)
                         return r;
         }
@@ -946,6 +958,7 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                 }
         }
 
+success:
         r = apply_user_record_settings(handle, ur, debug);
         if (r != PAM_SUCCESS)
                 return r;
@@ -996,15 +1009,7 @@ _public_ PAM_EXTERN int pam_sm_close_session(
                 if (r != PAM_SUCCESS)
                         return r;
 
-                r = sd_bus_call_method(bus,
-                                       "org.freedesktop.login1",
-                                       "/org/freedesktop/login1",
-                                       "org.freedesktop.login1.Manager",
-                                       "ReleaseSession",
-                                       &error,
-                                       NULL,
-                                       "s",
-                                       id);
+                r = bus_call_method(bus, bus_login_mgr, "ReleaseSession", &error, NULL, "s", id);
                 if (r < 0) {
                         pam_syslog(handle, LOG_ERR, "Failed to release session: %s", bus_error_message(&error, r));
                         return PAM_SESSION_ERR;
index 1f98898b695e778a3bfe206741aa6a5d04a83e2d..38058d7b2a3f918ff60c0cd039e3216cd44625d5 100644 (file)
@@ -22,7 +22,7 @@
 #include "strv.h"
 #include "user-util.h"
 
-static int acquire_runtime_dir_size(uint64_t *ret) {
+static int acquire_runtime_dir_properties(uint64_t *size, uint64_t *inodes) {
         _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;
@@ -31,10 +31,14 @@ static int acquire_runtime_dir_size(uint64_t *ret) {
         if (r < 0)
                 return log_error_errno(r, "Failed to connect to system bus: %m");
 
-        r = sd_bus_get_property_trivial(bus, "org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", "RuntimeDirectorySize", &error, 't', ret);
+        r = sd_bus_get_property_trivial(bus, "org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", "RuntimeDirectorySize", &error, 't', size);
         if (r < 0)
                 return log_error_errno(r, "Failed to acquire runtime directory size: %s", bus_error_message(&error, r));
 
+        r = sd_bus_get_property_trivial(bus, "org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", "RuntimeDirectoryInodesMax", &error, 't', inodes);
+        if (r < 0)
+                return log_error_errno(r, "Failed to acquire number of inodes for runtime directory: %s", bus_error_message(&error, r));
+
         return 0;
 }
 
@@ -42,7 +46,8 @@ static int user_mkdir_runtime_path(
                 const char *runtime_path,
                 uid_t uid,
                 gid_t gid,
-                uint64_t runtime_dir_size) {
+                uint64_t runtime_dir_size,
+                uint64_t runtime_dir_inodes) {
 
         int r;
 
@@ -58,21 +63,22 @@ static int user_mkdir_runtime_path(
         if (path_is_mount_point(runtime_path, NULL, 0) >= 0)
                 log_debug("%s is already a mount point", runtime_path);
         else {
-                char options[sizeof("mode=0700,uid=,gid=,size=,smackfsroot=*")
+                char options[sizeof("mode=0700,uid=,gid=,size=,nr_inodes=,smackfsroot=*")
                              + DECIMAL_STR_MAX(uid_t)
                              + DECIMAL_STR_MAX(gid_t)
+                             + DECIMAL_STR_MAX(uint64_t)
                              + DECIMAL_STR_MAX(uint64_t)];
 
                 xsprintf(options,
-                         "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%" PRIu64 "%s",
-                         uid, gid, runtime_dir_size,
+                         "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%" PRIu64 ",nr_inodes=%" PRIu64 "%s",
+                         uid, gid, runtime_dir_size, runtime_dir_inodes,
                          mac_smack_use() ? ",smackfsroot=*" : "");
 
                 (void) mkdir_label(runtime_path, 0700);
 
                 r = mount("tmpfs", runtime_path, "tmpfs", MS_NODEV|MS_NOSUID, options);
                 if (r < 0) {
-                        if (!IN_SET(errno, EPERM, EACCES)) {
+                        if (!ERRNO_IS_PRIVILEGE(errno)) {
                                 r = log_error_errno(errno, "Failed to mount per-user tmpfs directory %s: %m", runtime_path);
                                 goto fail;
                         }
@@ -127,7 +133,7 @@ static int user_remove_runtime_path(const char *runtime_path) {
 
 static int do_mount(const char *user) {
         char runtime_path[sizeof("/run/user") + DECIMAL_STR_MAX(uid_t)];
-        uint64_t runtime_dir_size;
+        uint64_t runtime_dir_size, runtime_dir_inodes;
         uid_t uid;
         gid_t gid;
         int r;
@@ -140,14 +146,14 @@ static int do_mount(const char *user) {
                                                     : "Failed to look up user \"%s\": %m",
                                        user);
 
-        r = acquire_runtime_dir_size(&runtime_dir_size);
+        r = acquire_runtime_dir_properties(&runtime_dir_size, &runtime_dir_inodes);
         if (r < 0)
                 return r;
 
         xsprintf(runtime_path, "/run/user/" UID_FMT, uid);
 
         log_debug("Will mount %s owned by "UID_FMT":"GID_FMT, runtime_path, uid, gid);
-        return user_mkdir_runtime_path(runtime_path, uid, gid, runtime_dir_size);
+        return user_mkdir_runtime_path(runtime_path, uid, gid, runtime_dir_size, runtime_dir_inodes);
 }
 
 static int do_umount(const char *user) {
@@ -186,11 +192,11 @@ static int run(int argc, char *argv[]) {
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                        "First argument must be either \"start\" or \"stop\".");
 
+        umask(0022);
+
         r = mac_selinux_init();
         if (r < 0)
-                return log_error_errno(r, "Could not initialize labelling: %m\n");
-
-        umask(0022);
+                return r;
 
         if (streq(argv[1], "start"))
                 return do_mount(argv[2]);
index 294ef349321f4c0397d9d9aecc9a3dfa8b1484b6..7a15bcc49a44942fa38e73eda43ddba887376f52 100644 (file)
@@ -4,9 +4,9 @@
 #include <sys/mount.h>
 
 #include "alloc-util.h"
+#include "bus-get-properties.h"
 #include "bus-label.h"
 #include "bus-polkit.h"
-#include "bus-util.h"
 #include "copy.h"
 #include "dissect-image.h"
 #include "fd-util.h"
@@ -354,30 +354,6 @@ int bus_image_method_get_os_release(
         return bus_reply_pair_array(message, image->os_release);
 }
 
-const sd_bus_vtable image_vtable[] = {
-        SD_BUS_VTABLE_START(0),
-        SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Image, name), 0),
-        SD_BUS_PROPERTY("Path", "s", NULL, offsetof(Image, path), 0),
-        SD_BUS_PROPERTY("Type", "s", property_get_type,  offsetof(Image, type), 0),
-        SD_BUS_PROPERTY("ReadOnly", "b", bus_property_get_bool, offsetof(Image, read_only), 0),
-        SD_BUS_PROPERTY("CreationTimestamp", "t", NULL, offsetof(Image, crtime), 0),
-        SD_BUS_PROPERTY("ModificationTimestamp", "t", NULL, offsetof(Image, mtime), 0),
-        SD_BUS_PROPERTY("Usage", "t", NULL, offsetof(Image, usage), 0),
-        SD_BUS_PROPERTY("Limit", "t", NULL, offsetof(Image, limit), 0),
-        SD_BUS_PROPERTY("UsageExclusive", "t", NULL, offsetof(Image, usage_exclusive), 0),
-        SD_BUS_PROPERTY("LimitExclusive", "t", NULL, offsetof(Image, limit_exclusive), 0),
-        SD_BUS_METHOD("Remove", NULL, NULL, bus_image_method_remove, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("Rename", "s", NULL, bus_image_method_rename, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("Clone", "sb", NULL, bus_image_method_clone, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("MarkReadOnly", "b", NULL, bus_image_method_mark_read_only, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetLimit", "t", NULL, bus_image_method_set_limit, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("GetHostname", NULL, "s", bus_image_method_get_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("GetMachineID", NULL, "ay", bus_image_method_get_machine_id, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("GetMachineInfo", NULL, "a{ss}", bus_image_method_get_machine_info, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_image_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_VTABLE_END
-};
-
 static int image_flush_cache(sd_event_source *s, void *userdata) {
         Manager *m = userdata;
 
@@ -388,7 +364,7 @@ static int image_flush_cache(sd_event_source *s, void *userdata) {
         return 0;
 }
 
-int image_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
+static int image_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
         _cleanup_free_ char *e = NULL;
         Manager *m = userdata;
         Image *image = NULL;
@@ -462,7 +438,7 @@ char *image_bus_path(const char *name) {
         return strjoin("/org/freedesktop/machine1/image/", e);
 }
 
-int image_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
+static int image_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
         _cleanup_hashmap_free_ Hashmap *images = NULL;
         _cleanup_strv_free_ char **l = NULL;
         Image *image;
@@ -497,3 +473,34 @@ int image_node_enumerator(sd_bus *bus, const char *path, void *userdata, char **
 
         return 1;
 }
+
+const sd_bus_vtable image_vtable[] = {
+        SD_BUS_VTABLE_START(0),
+        SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Image, name), 0),
+        SD_BUS_PROPERTY("Path", "s", NULL, offsetof(Image, path), 0),
+        SD_BUS_PROPERTY("Type", "s", property_get_type,  offsetof(Image, type), 0),
+        SD_BUS_PROPERTY("ReadOnly", "b", bus_property_get_bool, offsetof(Image, read_only), 0),
+        SD_BUS_PROPERTY("CreationTimestamp", "t", NULL, offsetof(Image, crtime), 0),
+        SD_BUS_PROPERTY("ModificationTimestamp", "t", NULL, offsetof(Image, mtime), 0),
+        SD_BUS_PROPERTY("Usage", "t", NULL, offsetof(Image, usage), 0),
+        SD_BUS_PROPERTY("Limit", "t", NULL, offsetof(Image, limit), 0),
+        SD_BUS_PROPERTY("UsageExclusive", "t", NULL, offsetof(Image, usage_exclusive), 0),
+        SD_BUS_PROPERTY("LimitExclusive", "t", NULL, offsetof(Image, limit_exclusive), 0),
+        SD_BUS_METHOD("Remove", NULL, NULL, bus_image_method_remove, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("Rename", "s", NULL, bus_image_method_rename, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("Clone", "sb", NULL, bus_image_method_clone, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("MarkReadOnly", "b", NULL, bus_image_method_mark_read_only, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("SetLimit", "t", NULL, bus_image_method_set_limit, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("GetHostname", NULL, "s", bus_image_method_get_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("GetMachineID", NULL, "ay", bus_image_method_get_machine_id, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("GetMachineInfo", NULL, "a{ss}", bus_image_method_get_machine_info, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_image_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_VTABLE_END
+};
+
+const BusObjectImplementation image_object = {
+        "/org/freedesktop/machine1/image",
+        "org.freedesktop.machine1.Image",
+        .fallback_vtables = BUS_FALLBACK_VTABLES({image_vtable, image_object_find}),
+        .node_enumerator = image_node_enumerator,
+};
index a918b77d38e21ee6dc7bbeabb16f5fedc7176778..d785c10817dfaf590fd0fd444625062655b8fe8b 100644 (file)
@@ -1,15 +1,13 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
+#include "bus-object.h"
 #include "machined.h"
 
-extern const sd_bus_vtable image_vtable[];
+extern const BusObjectImplementation image_object;
 
 char *image_bus_path(const char *name);
 
-int image_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error);
-int image_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error);
-
 int bus_image_method_remove(sd_bus_message *message, void *userdata, sd_bus_error *error);
 int bus_image_method_rename(sd_bus_message *message, void *userdata, sd_bus_error *error);
 int bus_image_method_clone(sd_bus_message *message, void *userdata, sd_bus_error *error);
index a2990452af17973de8844844f13bdb5c575ac0b9..73ef5949bf366f6e5ad4ed10120ac4afb251f809 100644 (file)
 
 #include "alloc-util.h"
 #include "bus-common-errors.h"
+#include "bus-get-properties.h"
 #include "bus-internal.h"
 #include "bus-label.h"
+#include "bus-locator.h"
 #include "bus-polkit.h"
-#include "bus-util.h"
 #include "copy.h"
 #include "env-file.h"
 #include "env-util.h"
 #include "fd-util.h"
+#include "fileio.h"
 #include "format-util.h"
 #include "fs-util.h"
 #include "in-addr-util.h"
@@ -399,12 +401,10 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s
 
                 pair[1] = safe_close(pair[1]);
 
-                f = fdopen(pair[0], "r");
+                f = take_fdopen(&pair[0], "r");
                 if (!f)
                         return -errno;
 
-                pair[0] = -1;
-
                 r = load_env_file_pairs(f, "/etc/os-release", &l);
                 if (r < 0)
                         return r;
@@ -553,14 +553,7 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu
 
         getty = strjoina("container-getty@", p, ".service");
 
-        r = sd_bus_call_method(
-                        container_bus,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "StartUnit",
-                        error, NULL,
-                        "ss", getty, "replace");
+        r = bus_call_method(container_bus, bus_systemd_mgr, "StartUnit", error, NULL, "ss", getty, "replace");
         if (r < 0)
                 return r;
 
@@ -669,13 +662,7 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu
 
         container_bus = allocated_bus ?: m->manager->bus;
 
-        r = sd_bus_message_new_method_call(
-                        container_bus,
-                        &tm,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "StartTransientUnit");
+        r = bus_message_new_method_call(container_bus, &tm, bus_systemd_mgr, "StartTransientUnit");
         if (r < 0)
                 return r;
 
@@ -1322,35 +1309,7 @@ int bus_machine_method_get_uid_shift(sd_bus_message *message, void *userdata, sd
         return sd_bus_reply_method_return(message, "u", (uint32_t) shift);
 }
 
-const sd_bus_vtable machine_vtable[] = {
-        SD_BUS_VTABLE_START(0),
-        SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Machine, name), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("Id", "ay", bus_property_get_id128, offsetof(Machine, id), SD_BUS_VTABLE_PROPERTY_CONST),
-        BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Machine, timestamp), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Machine, service), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("Unit", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
-        SD_BUS_PROPERTY("Leader", "u", NULL, offsetof(Machine, leader), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Machine, class), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(Machine, root_directory), SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("NetworkInterfaces", "ai", property_get_netif, 0, SD_BUS_VTABLE_PROPERTY_CONST),
-        SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0),
-        SD_BUS_METHOD("Terminate", NULL, NULL, bus_machine_method_terminate, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("Kill", "si", NULL, bus_machine_method_kill, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("GetAddresses", NULL, "a(iay)", bus_machine_method_get_addresses, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_machine_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("GetUIDShift", NULL, "u", bus_machine_method_get_uid_shift, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("OpenPTY", NULL, "hs", bus_machine_method_open_pty, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("OpenLogin", NULL, "hs", bus_machine_method_open_login, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("OpenShell", "ssasas", "hs", bus_machine_method_open_shell, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("BindMount", "ssbb", NULL, bus_machine_method_bind_mount, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("CopyFrom", "ss", NULL, bus_machine_method_copy, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("CopyTo", "ss", NULL, bus_machine_method_copy, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("OpenRootDirectory", NULL, "h", bus_machine_method_open_root_directory, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_VTABLE_END
-};
-
-int machine_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
+static int machine_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
         Manager *m = userdata;
         Machine *machine;
         int r;
@@ -1414,7 +1373,7 @@ char *machine_bus_path(Machine *m) {
         return strjoin("/org/freedesktop/machine1/machine/", e);
 }
 
-int machine_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
+static int machine_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
         _cleanup_strv_free_ char **l = NULL;
         Machine *machine = NULL;
         Manager *m = userdata;
@@ -1442,6 +1401,115 @@ int machine_node_enumerator(sd_bus *bus, const char *path, void *userdata, char
         return 1;
 }
 
+static const sd_bus_vtable machine_vtable[] = {
+        SD_BUS_VTABLE_START(0),
+        SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Machine, name), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("Id", "ay", bus_property_get_id128, offsetof(Machine, id), SD_BUS_VTABLE_PROPERTY_CONST),
+        BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Machine, timestamp), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Machine, service), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("Unit", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
+        SD_BUS_PROPERTY("Leader", "u", NULL, offsetof(Machine, leader), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Machine, class), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(Machine, root_directory), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("NetworkInterfaces", "ai", property_get_netif, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0),
+
+        SD_BUS_METHOD("Terminate",
+                      NULL,
+                      NULL,
+                      bus_machine_method_terminate,
+                      SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("Kill",
+                                 "si",
+                                 SD_BUS_PARAM(who)
+                                 SD_BUS_PARAM(signal),
+                                 NULL,,
+                                 bus_machine_method_kill,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("GetAddresses",
+                                 NULL,,
+                                 "a(iay)",
+                                 SD_BUS_PARAM(addresses),
+                                 bus_machine_method_get_addresses,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("GetOSRelease",
+                                 NULL,,
+                                 "a{ss}",
+                                 SD_BUS_PARAM(fields),
+                                 bus_machine_method_get_os_release,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("GetUIDShift",
+                                 NULL,,
+                                 "u",
+                                 SD_BUS_PARAM(shift),
+                                 bus_machine_method_get_uid_shift,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("OpenPTY",
+                                 NULL,,
+                                 "hs",
+                                 SD_BUS_PARAM(pty)
+                                 SD_BUS_PARAM(pty_path),
+                                 bus_machine_method_open_pty,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("OpenLogin",
+                                 NULL,,
+                                 "hs",
+                                 SD_BUS_PARAM(pty)
+                                 SD_BUS_PARAM(pty_path),
+                                 bus_machine_method_open_login,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("OpenShell",
+                                 "ssasas",
+                                 SD_BUS_PARAM(user)
+                                 SD_BUS_PARAM(path)
+                                 SD_BUS_PARAM(args)
+                                 SD_BUS_PARAM(environment),
+                                 "hs",
+                                 SD_BUS_PARAM(pty)
+                                 SD_BUS_PARAM(pty_path),
+                                 bus_machine_method_open_shell,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("BindMount",
+                                 "ssbb",
+                                 SD_BUS_PARAM(source)
+                                 SD_BUS_PARAM(destination)
+                                 SD_BUS_PARAM(read_only)
+                                 SD_BUS_PARAM(mkdir),
+                                 NULL,,
+                                 bus_machine_method_bind_mount,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("CopyFrom",
+                                 "ss",
+                                 SD_BUS_PARAM(source)
+                                 SD_BUS_PARAM(destination),
+                                 NULL,,
+                                 bus_machine_method_copy,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("CopyTo",
+                                 "ss",
+                                 SD_BUS_PARAM(source)
+                                 SD_BUS_PARAM(destination),
+                                 NULL,,
+                                 bus_machine_method_copy,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("OpenRootDirectory",
+                                 NULL,,
+                                 "h",
+                                 SD_BUS_PARAM(fd),
+                                 bus_machine_method_open_root_directory,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+
+        SD_BUS_VTABLE_END
+};
+
+const BusObjectImplementation machine_object = {
+        "/org/freedesktop/machine1/machine",
+        "org.freedesktop.machine1.Machine",
+        .fallback_vtables = BUS_FALLBACK_VTABLES({machine_vtable, machine_object_find}),
+        .node_enumerator = machine_node_enumerator,
+};
+
 int machine_send_signal(Machine *m, bool new_machine) {
         _cleanup_free_ char *p = NULL;
 
index d9f3c59cead7417f17a7c4f5b17628c69eee424c..7080092bcf78cdfc731a079e5487b87bc942037f 100644 (file)
@@ -3,13 +3,12 @@
 
 #include "sd-bus.h"
 
+#include "bus-util.h"
 #include "machine.h"
 
-extern const sd_bus_vtable machine_vtable[];
+extern const BusObjectImplementation machine_object;
 
 char *machine_bus_path(Machine *s);
-int machine_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error);
-int machine_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error);
 
 int bus_machine_method_unregister(sd_bus_message *message, void *userdata, sd_bus_error *error);
 int bus_machine_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error);
index 8154c42ed774b8ea46fc1dfcb57711f61f1b9e9c..ace84edbb4ecd6a5c3996b18e5c21137b1895702 100644 (file)
@@ -746,6 +746,143 @@ int machine_get_uid_shift(Machine *m, uid_t *ret) {
         return 0;
 }
 
+static int machine_owns_uid_internal(
+                Machine *machine,
+                const char *map_file, /* "uid_map" or "gid_map" */
+                uid_t uid,
+                uid_t *ret_internal_uid) {
+
+        _cleanup_fclose_ FILE *f = NULL;
+        const char *p;
+
+        /* This is a generic implementation for both uids and gids, under the assumptions they have the same types and semantics. */
+        assert_cc(sizeof(uid_t) == sizeof(gid_t));
+
+        assert(machine);
+
+        /* Checks if the specified host UID is owned by the machine, and returns the UID it maps to
+         * internally in the machine */
+
+        if (machine->class != MACHINE_CONTAINER)
+                goto negative;
+
+        p = procfs_file_alloca(machine->leader, map_file);
+        f = fopen(p, "re");
+        if (!f) {
+                log_debug_errno(errno, "Failed to open %s, ignoring.", p);
+                goto negative;
+        }
+
+        for (;;) {
+                uid_t uid_base, uid_shift, uid_range, converted;
+                int k;
+
+                errno = 0;
+                k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT, &uid_base, &uid_shift, &uid_range);
+                if (k < 0 && feof(f))
+                        break;
+                if (k != 3) {
+                        if (ferror(f))
+                                return errno_or_else(EIO);
+
+                        return -EIO;
+                }
+
+                /* The private user namespace is disabled, ignoring. */
+                if (uid_shift == 0)
+                        continue;
+
+                if (uid < uid_shift || uid >= uid_shift + uid_range)
+                        continue;
+
+                converted = (uid - uid_shift + uid_base);
+                if (!uid_is_valid(converted))
+                        return -EINVAL;
+
+                if (ret_internal_uid)
+                        *ret_internal_uid = converted;
+
+                return true;
+        }
+
+negative:
+        if (ret_internal_uid)
+                *ret_internal_uid = UID_INVALID;
+
+        return false;
+}
+
+int machine_owns_uid(Machine *machine, uid_t uid, uid_t *ret_internal_uid) {
+        return machine_owns_uid_internal(machine, "uid_map", uid, ret_internal_uid);
+}
+
+int machine_owns_gid(Machine *machine, gid_t gid, gid_t *ret_internal_gid) {
+        return machine_owns_uid_internal(machine, "gid_map", (uid_t) gid, (uid_t*) ret_internal_gid);
+}
+
+static int machine_translate_uid_internal(
+                Machine *machine,
+                const char *map_file, /* "uid_map" or "gid_map" */
+                uid_t uid,
+                uid_t *ret_host_uid) {
+
+        _cleanup_fclose_ FILE *f = NULL;
+        const char *p;
+
+        /* This is a generic implementation for both uids and gids, under the assumptions they have the same types and semantics. */
+        assert_cc(sizeof(uid_t) == sizeof(gid_t));
+
+        assert(machine);
+        assert(uid_is_valid(uid));
+
+        if (machine->class != MACHINE_CONTAINER)
+                return -ESRCH;
+
+        /* Translates a machine UID into a host UID */
+
+        p = procfs_file_alloca(machine->leader, map_file);
+        f = fopen(p, "re");
+        if (!f)
+                return -errno;
+
+        for (;;) {
+                uid_t uid_base, uid_shift, uid_range, converted;
+                int k;
+
+                errno = 0;
+                k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT, &uid_base, &uid_shift, &uid_range);
+                if (k < 0 && feof(f))
+                        break;
+                if (k != 3) {
+                        if (ferror(f))
+                                return errno_or_else(EIO);
+
+                        return -EIO;
+                }
+
+                if (uid < uid_base || uid >= uid_base + uid_range)
+                        continue;
+
+                converted = uid - uid_base + uid_shift;
+                if (!uid_is_valid(converted))
+                        return -EINVAL;
+
+                if (ret_host_uid)
+                        *ret_host_uid = converted;
+                return 0;
+        }
+
+        return -ESRCH;
+}
+
+int machine_translate_uid(Machine *machine, gid_t uid, gid_t *ret_host_uid) {
+        return machine_translate_uid_internal(machine, "uid_map", uid, ret_host_uid);
+}
+
+int machine_translate_gid(Machine *machine, gid_t gid, gid_t *ret_host_gid) {
+        return machine_translate_uid_internal(machine, "gid_map", (uid_t) gid, (uid_t*) ret_host_gid);
+}
+
 static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
         [MACHINE_CONTAINER] = "container",
         [MACHINE_VM] = "vm",
index 0a39e610529f9b6172ea61b933265133805a3fc4..634c5fc648854aeddeeace7bb8ca987f486fb2e7 100644 (file)
@@ -94,3 +94,9 @@ int machine_openpt(Machine *m, int flags, char **ret_slave);
 int machine_open_terminal(Machine *m, const char *path, int mode);
 
 int machine_get_uid_shift(Machine *m, uid_t *ret);
+
+int machine_owns_uid(Machine *m, uid_t host_uid, uid_t *ret_internal_uid);
+int machine_owns_gid(Machine *m, gid_t host_gid, gid_t *ret_internal_gid);
+
+int machine_translate_uid(Machine *m, uid_t internal_uid, uid_t *ret_host_uid);
+int machine_translate_gid(Machine *m, gid_t internal_gid, gid_t *ret_host_gid);
index 61234582a3405e50b3700bb7926f256c86e66d77..841eeae8827b476cd564b71f747fe252efa3919e 100644 (file)
 #include "alloc-util.h"
 #include "bus-common-errors.h"
 #include "bus-error.h"
+#include "bus-locator.h"
+#include "bus-map-properties.h"
+#include "bus-print-properties.h"
 #include "bus-unit-procs.h"
 #include "bus-unit-util.h"
-#include "bus-util.h"
 #include "bus-wait-for-jobs.h"
 #include "cgroup-show.h"
 #include "cgroup-util.h"
@@ -108,14 +110,7 @@ static int call_get_os_release(sd_bus *bus, const char *method, const char *name
                 awaited_args++;
         query_res = newa0(const char *, awaited_args);
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.machine1",
-                        "/org/freedesktop/machine1",
-                        "org.freedesktop.machine1.Manager",
-                        method,
-                        &error,
-                        &reply, "s", name);
+        r = bus_call_method(bus, bus_machine_mgr, method, &error, &reply, "s", name);
         if (r < 0)
                 return log_debug_errno(r, "Failed to call '%s()': %s", method, bus_error_message(&error, r));
 
@@ -179,14 +174,7 @@ static int call_get_addresses(
         assert(prefix);
         assert(prefix2);
 
-        r = sd_bus_call_method(bus,
-                               "org.freedesktop.machine1",
-                               "/org/freedesktop/machine1",
-                               "org.freedesktop.machine1.Manager",
-                               "GetMachineAddresses",
-                               NULL,
-                               &reply,
-                               "s", name);
+        r = bus_call_method(bus, bus_machine_mgr, "GetMachineAddresses", NULL, &reply, "s", name);
         if (r < 0)
                 return log_debug_errno(r, "Could not get addresses: %s", bus_error_message(&error, r));
 
@@ -249,7 +237,7 @@ static int show_table(Table *table, const char *word) {
         if (table_get_rows(table) > 1 || OUTPUT_MODE_IS_JSON(arg_output)) {
                 r = table_set_sort(table, (size_t) 0, (size_t) -1);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to sort table: %m");
+                        return table_log_sort_error(r);
 
                 table_set_header(table, arg_legend);
 
@@ -258,7 +246,7 @@ static int show_table(Table *table, const char *word) {
                 else
                         r = table_print(table, NULL);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to show table: %m");
+                        return table_log_print_error(r);
         }
 
         if (arg_legend) {
@@ -283,14 +271,7 @@ static int list_machines(int argc, char *argv[], void *userdata) {
 
         (void) pager_open(arg_pager_flags);
 
-        r = sd_bus_call_method(bus,
-                               "org.freedesktop.machine1",
-                               "/org/freedesktop/machine1",
-                               "org.freedesktop.machine1.Manager",
-                               "ListMachines",
-                               &error,
-                               &reply,
-                               NULL);
+        r = bus_call_method(bus, bus_machine_mgr, "ListMachines", &error, &reply, NULL);
         if (r < 0)
                 return log_error_errno(r, "Could not get machines: %s", bus_error_message(&error, r));
 
@@ -369,14 +350,7 @@ static int list_images(int argc, char *argv[], void *userdata) {
 
         (void) pager_open(arg_pager_flags);
 
-        r = sd_bus_call_method(bus,
-                               "org.freedesktop.machine1",
-                               "/org/freedesktop/machine1",
-                               "org.freedesktop.machine1.Manager",
-                               "ListImages",
-                               &error,
-                               &reply,
-                               NULL);
+        r = bus_call_method(bus, bus_machine_mgr, "ListImages", &error, &reply, NULL);
         if (r < 0)
                 return log_error_errno(r, "Could not get images: %s", bus_error_message(&error, r));
 
@@ -493,14 +467,7 @@ static int print_uid_shift(sd_bus *bus, const char *name) {
         assert(bus);
         assert(name);
 
-        r = sd_bus_call_method(bus,
-                               "org.freedesktop.machine1",
-                               "/org/freedesktop/machine1",
-                               "org.freedesktop.machine1.Manager",
-                               "GetMachineUIDShift",
-                               &error,
-                               &reply,
-                               "s", name);
+        r = bus_call_method(bus, bus_machine_mgr, "GetMachineUIDShift", &error, &reply, "s", name);
         if (r < 0)
                 return log_debug_errno(r, "Failed to query UID/GID shift: %s", bus_error_message(&error, r));
 
@@ -751,14 +718,7 @@ static int show_machine(int argc, char *argv[], void *userdata) {
         for (int i = 1; i < argc; i++) {
                 const char *path = NULL;
 
-                r = sd_bus_call_method(bus,
-                                       "org.freedesktop.machine1",
-                                       "/org/freedesktop/machine1",
-                                       "org.freedesktop.machine1.Manager",
-                                       "GetMachine",
-                                       &error,
-                                       &reply,
-                                       "s", argv[i]);
+                r = bus_call_method(bus, bus_machine_mgr, "GetMachine", &error, &reply, "s", argv[i]);
                 if (r < 0)
                         return log_error_errno(r, "Could not get path to machine: %s", bus_error_message(&error, -r));
 
@@ -780,13 +740,7 @@ static int print_image_hostname(sd_bus *bus, const char *name) {
         const char *hn;
         int r;
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.machine1",
-                        "/org/freedesktop/machine1",
-                        "org.freedesktop.machine1.Manager",
-                        "GetImageHostname",
-                        NULL, &reply, "s", name);
+        r = bus_call_method(bus, bus_machine_mgr, "GetImageHostname", NULL, &reply, "s", name);
         if (r < 0)
                 return r;
 
@@ -807,13 +761,7 @@ static int print_image_machine_id(sd_bus *bus, const char *name) {
         size_t size;
         int r;
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.machine1",
-                        "/org/freedesktop/machine1",
-                        "org.freedesktop.machine1.Manager",
-                        "GetImageMachineID",
-                        NULL, &reply, "s", name);
+        r = bus_call_method(bus, bus_machine_mgr, "GetImageMachineID", NULL, &reply, "s", name);
         if (r < 0)
                 return r;
 
@@ -834,13 +782,7 @@ static int print_image_machine_info(sd_bus *bus, const char *name) {
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
         int r;
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.machine1",
-                        "/org/freedesktop/machine1",
-                        "org.freedesktop.machine1.Manager",
-                        "GetImageMachineInfo",
-                        NULL, &reply, "s", name);
+        r = bus_call_method(bus, bus_machine_mgr, "GetImageMachineInfo", NULL, &reply, "s", name);
         if (r < 0)
                 return r;
 
@@ -1093,15 +1035,7 @@ static int show_image(int argc, char *argv[], void *userdata) {
         for (int i = 1; i < argc; i++) {
                 const char *path = NULL;
 
-                r = sd_bus_call_method(
-                                bus,
-                                "org.freedesktop.machine1",
-                                "/org/freedesktop/machine1",
-                                "org.freedesktop.machine1.Manager",
-                                "GetImage",
-                                &error,
-                                &reply,
-                                "s", argv[i]);
+                r = bus_call_method(bus, bus_machine_mgr, "GetImage", &error, &reply, "s", argv[i]);
                 if (r < 0)
                         return log_error_errno(r, "Could not get path to image: %s", bus_error_message(&error, -r));
 
@@ -1131,11 +1065,9 @@ static int kill_machine(int argc, char *argv[], void *userdata) {
                 arg_kill_who = "all";
 
         for (int i = 1; i < argc; i++) {
-                r = sd_bus_call_method(
+                r = bus_call_method(
                                 bus,
-                                "org.freedesktop.machine1",
-                                "/org/freedesktop/machine1",
-                                "org.freedesktop.machine1.Manager",
+                                bus_machine_mgr,
                                 "KillMachine",
                                 &error,
                                 NULL,
@@ -1171,15 +1103,7 @@ static int terminate_machine(int argc, char *argv[], void *userdata) {
         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
 
         for (int i = 1; i < argc; i++) {
-                r = sd_bus_call_method(
-                                bus,
-                                "org.freedesktop.machine1",
-                                "/org/freedesktop/machine1",
-                                "org.freedesktop.machine1.Manager",
-                                "TerminateMachine",
-                                &error,
-                                NULL,
-                                "s", argv[i]);
+                r = bus_call_method(bus, bus_machine_mgr, "TerminateMachine", &error, NULL, "s", argv[i]);
                 if (r < 0)
                         return log_error_errno(r, "Could not terminate machine: %s", bus_error_message(&error, -r));
         }
@@ -1213,12 +1137,10 @@ static int copy_files(int argc, char *argv[], void *userdata) {
                 host_path = abs_host_path;
         }
 
-        r = sd_bus_message_new_method_call(
+        r = bus_message_new_method_call(
                         bus,
                         &m,
-                        "org.freedesktop.machine1",
-                        "/org/freedesktop/machine1",
-                        "org.freedesktop.machine1.Manager",
+                        bus_machine_mgr,
                         copy_from ? "CopyFromMachine" : "CopyToMachine");
         if (r < 0)
                 return bus_log_create_error(r);
@@ -1249,11 +1171,9 @@ static int bind_mount(int argc, char *argv[], void *userdata) {
 
         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
 
-        r = sd_bus_call_method(
+        r = bus_call_method(
                         bus,
-                        "org.freedesktop.machine1",
-                        "/org/freedesktop/machine1",
-                        "org.freedesktop.machine1.Manager",
+                        bus_machine_mgr,
                         "BindMountMachine",
                         &error,
                         NULL,
@@ -1425,15 +1345,7 @@ static int login_machine(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return log_error_errno(r, "Failed to request machine removal match: %m");
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.machine1",
-                        "/org/freedesktop/machine1",
-                        "org.freedesktop.machine1.Manager",
-                        "OpenMachineLogin",
-                        &error,
-                        &reply,
-                        "s", machine);
+        r = bus_call_method(bus, bus_machine_mgr, "OpenMachineLogin", &error, &reply, "s", machine);
         if (r < 0)
                 return log_error_errno(r, "Failed to get login PTY: %s", bus_error_message(&error, -r));
 
@@ -1498,13 +1410,7 @@ static int shell_machine(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return log_error_errno(r, "Failed to request machine removal match: %m");
 
-        r = sd_bus_message_new_method_call(
-                        bus,
-                        &m,
-                        "org.freedesktop.machine1",
-                        "/org/freedesktop/machine1",
-                        "org.freedesktop.machine1.Manager",
-                        "OpenMachineShell");
+        r = bus_message_new_method_call(bus, &m, bus_machine_mgr, "OpenMachineShell");
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -1545,13 +1451,7 @@ static int remove_image(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 *m = NULL;
 
-                r = sd_bus_message_new_method_call(
-                                bus,
-                                &m,
-                                "org.freedesktop.machine1",
-                                "/org/freedesktop/machine1",
-                                "org.freedesktop.machine1.Manager",
-                                "RemoveImage");
+                r = bus_message_new_method_call(bus, &m, bus_machine_mgr, "RemoveImage");
                 if (r < 0)
                         return bus_log_create_error(r);
 
@@ -1577,11 +1477,9 @@ static int rename_image(int argc, char *argv[], void *userdata) {
 
         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
 
-        r = sd_bus_call_method(
+        r = bus_call_method(
                         bus,
-                        "org.freedesktop.machine1",
-                        "/org/freedesktop/machine1",
-                        "org.freedesktop.machine1.Manager",
+                        bus_machine_mgr,
                         "RenameImage",
                         &error,
                         NULL,
@@ -1602,13 +1500,7 @@ static int clone_image(int argc, char *argv[], void *userdata) {
 
         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
 
-        r = sd_bus_message_new_method_call(
-                        bus,
-                        &m,
-                        "org.freedesktop.machine1",
-                        "/org/freedesktop/machine1",
-                        "org.freedesktop.machine1.Manager",
-                        "CloneImage");
+        r = bus_message_new_method_call(bus, &m, bus_machine_mgr, "CloneImage");
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -1641,15 +1533,7 @@ static int read_only_image(int argc, char *argv[], void *userdata) {
 
         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.machine1",
-                        "/org/freedesktop/machine1",
-                        "org.freedesktop.machine1.Manager",
-                        "MarkImageReadOnly",
-                        &error,
-                        NULL,
-                        "sb", argv[1], b);
+        r = bus_call_method(bus, bus_machine_mgr, "MarkImageReadOnly", &error, NULL, "sb", argv[1], b);
         if (r < 0)
                 return log_error_errno(r, "Could not mark image read-only: %s", bus_error_message(&error, -r));
 
@@ -1663,15 +1547,7 @@ static int image_exists(sd_bus *bus, const char *name) {
         assert(bus);
         assert(name);
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.machine1",
-                        "/org/freedesktop/machine1",
-                        "org.freedesktop.machine1.Manager",
-                        "GetImage",
-                        &error,
-                        NULL,
-                        "s", name);
+        r = bus_call_method(bus, bus_machine_mgr, "GetImage", &error, NULL, "s", name);
         if (r < 0) {
                 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_IMAGE))
                         return 0;
@@ -1933,12 +1809,10 @@ 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_match_signal_async(
+        r = bus_match_signal_async(
                         bus,
                         &slot_job_removed,
-                        "org.freedesktop.import1",
-                        "/org/freedesktop/import1",
-                        "org.freedesktop.import1.Manager",
+                        bus_import_mgr,
                         "TransferRemoved",
                         match_transfer_removed, NULL, &path);
         if (r < 0)
@@ -2022,13 +1896,7 @@ static int import_tar(int argc, char *argv[], void *userdata) {
                         return log_error_errno(errno, "Failed to open %s: %m", path);
         }
 
-        r = sd_bus_message_new_method_call(
-                        bus,
-                        &m,
-                        "org.freedesktop.import1",
-                        "/org/freedesktop/import1",
-                        "org.freedesktop.import1.Manager",
-                        "ImportTar");
+        r = bus_message_new_method_call(bus, &m, bus_import_mgr, "ImportTar");
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -2089,13 +1957,7 @@ static int import_raw(int argc, char *argv[], void *userdata) {
                         return log_error_errno(errno, "Failed to open %s: %m", path);
         }
 
-        r = sd_bus_message_new_method_call(
-                        bus,
-                        &m,
-                        "org.freedesktop.import1",
-                        "/org/freedesktop/import1",
-                        "org.freedesktop.import1.Manager",
-                        "ImportRaw");
+        r = bus_message_new_method_call(bus, &m, bus_import_mgr, "ImportRaw");
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -2150,13 +2012,7 @@ static int import_fs(int argc, char *argv[], void *userdata) {
                         return log_error_errno(errno, "Failed to open directory '%s': %m", path);
         }
 
-        r = sd_bus_message_new_method_call(
-                        bus,
-                        &m,
-                        "org.freedesktop.import1",
-                        "/org/freedesktop/import1",
-                        "org.freedesktop.import1.Manager",
-                        "ImportFileSystem");
+        r = bus_message_new_method_call(bus, &m, bus_import_mgr, "ImportFileSystem");
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -2215,13 +2071,7 @@ static int export_tar(int argc, char *argv[], void *userdata) {
                         return log_error_errno(errno, "Failed to open %s: %m", path);
         }
 
-        r = sd_bus_message_new_method_call(
-                        bus,
-                        &m,
-                        "org.freedesktop.import1",
-                        "/org/freedesktop/import1",
-                        "org.freedesktop.import1.Manager",
-                        "ExportTar");
+        r = bus_message_new_method_call(bus, &m, bus_import_mgr, "ExportTar");
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -2264,13 +2114,7 @@ static int export_raw(int argc, char *argv[], void *userdata) {
                         return log_error_errno(errno, "Failed to open %s: %m", path);
         }
 
-        r = sd_bus_message_new_method_call(
-                        bus,
-                        &m,
-                        "org.freedesktop.import1",
-                        "/org/freedesktop/import1",
-                        "org.freedesktop.import1.Manager",
-                        "ExportRaw");
+        r = bus_message_new_method_call(bus, &m, bus_import_mgr, "ExportRaw");
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -2326,13 +2170,7 @@ static int pull_tar(int argc, char *argv[], void *userdata) {
                 }
         }
 
-        r = sd_bus_message_new_method_call(
-                        bus,
-                        &m,
-                        "org.freedesktop.import1",
-                        "/org/freedesktop/import1",
-                        "org.freedesktop.import1.Manager",
-                        "PullTar");
+        r = bus_message_new_method_call(bus, &m, bus_import_mgr, "PullTar");
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -2389,13 +2227,7 @@ static int pull_raw(int argc, char *argv[], void *userdata) {
                 }
         }
 
-        r = sd_bus_message_new_method_call(
-                        bus,
-                        &m,
-                        "org.freedesktop.import1",
-                        "/org/freedesktop/import1",
-                        "org.freedesktop.import1.Manager",
-                        "PullRaw");
+        r = bus_message_new_method_call(bus, &m, bus_import_mgr, "PullRaw");
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -2438,14 +2270,7 @@ static int list_transfers(int argc, char *argv[], void *userdata) {
 
         (void) pager_open(arg_pager_flags);
 
-        r = sd_bus_call_method(bus,
-                               "org.freedesktop.import1",
-                               "/org/freedesktop/import1",
-                               "org.freedesktop.import1.Manager",
-                               "ListTransfers",
-                               &error,
-                               &reply,
-                               NULL);
+        r = bus_call_method(bus, bus_import_mgr, "ListTransfers", &error, &reply, NULL);
         if (r < 0)
                 return log_error_errno(r, "Could not get transfers: %s", bus_error_message(&error, -r));
 
@@ -2542,15 +2367,7 @@ static int cancel_transfer(int argc, char *argv[], void *userdata) {
                 if (r < 0)
                         return log_error_errno(r, "Failed to parse transfer id: %s", argv[i]);
 
-                r = sd_bus_call_method(
-                                bus,
-                                "org.freedesktop.import1",
-                                "/org/freedesktop/import1",
-                                "org.freedesktop.import1.Manager",
-                                "CancelTransfer",
-                                &error,
-                                NULL,
-                                "u", id);
+                r = bus_call_method(bus, bus_import_mgr, "CancelTransfer", &error, NULL, "u", id);
                 if (r < 0)
                         return log_error_errno(r, "Could not cancel transfer: %s", bus_error_message(&error, -r));
         }
@@ -2577,26 +2394,10 @@ static int set_limit(int argc, char *argv[], void *userdata) {
         if (argc > 2)
                 /* With two arguments changes the quota limit of the
                  * specified image */
-                r = sd_bus_call_method(
-                                bus,
-                                "org.freedesktop.machine1",
-                                "/org/freedesktop/machine1",
-                                "org.freedesktop.machine1.Manager",
-                                "SetImageLimit",
-                                &error,
-                                NULL,
-                                "st", argv[1], limit);
+                r = bus_call_method(bus, bus_machine_mgr, "SetImageLimit", &error, NULL, "st", argv[1], limit);
         else
                 /* With one argument changes the pool quota limit */
-                r = sd_bus_call_method(
-                                bus,
-                                "org.freedesktop.machine1",
-                                "/org/freedesktop/machine1",
-                                "org.freedesktop.machine1.Manager",
-                                "SetPoolLimit",
-                                &error,
-                                NULL,
-                                "t", limit);
+                r = bus_call_method(bus, bus_machine_mgr, "SetPoolLimit", &error, NULL, "t", limit);
 
         if (r < 0)
                 return log_error_errno(r, "Could not set limit: %s", bus_error_message(&error, r));
@@ -2616,13 +2417,7 @@ static int clean_images(int argc, char *argv[], void *userdata) {
 
         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
 
-        r = sd_bus_message_new_method_call(
-                        bus,
-                        &m,
-                        "org.freedesktop.machine1",
-                        "/org/freedesktop/machine1",
-                        "org.freedesktop.machine1.Manager",
-                        "CleanPool");
+        r = bus_message_new_method_call(bus, &m, bus_machine_mgr, "CleanPool");
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -3089,9 +2884,7 @@ static int run(int argc, char *argv[]) {
         int r;
 
         setlocale(LC_ALL, "");
-        log_show_color(true);
-        log_parse_environment();
-        log_open();
+        log_setup_cli();
 
         /* The journal merging logic potentially needs a lot of fds. */
         (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE);
@@ -3104,7 +2897,7 @@ static int run(int argc, char *argv[]) {
 
         r = bus_connect_transport(arg_transport, arg_host, false, &bus);
         if (r < 0)
-                return log_error_errno(r, "Failed to create bus connection: %m");
+                return bus_log_connect_error(r);
 
         (void) sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
 
index 6a404805eaf2c770e480e88ebc4a3131ff26344e..c44bb94d8adceae546f6b1b14c5dd9f6240e016c 100644 (file)
@@ -3,6 +3,7 @@
 #include "machined.h"
 #include "nscd-flush.h"
 #include "strv.h"
+#include "user-util.h"
 
 static int on_nscd_cache_flush_event(sd_event_source *s, void *userdata) {
         /* Let's ask glibc's nscd daemon to flush its caches. We request this for the three database machines may show
@@ -34,3 +35,72 @@ int manager_enqueue_nscd_cache_flush(Manager *m) {
 
         return 0;
 }
+
+int manager_find_machine_for_uid(Manager *m, uid_t uid, Machine **ret_machine, uid_t *ret_internal_uid) {
+        Machine *machine;
+        Iterator i;
+        int r;
+
+        assert(m);
+        assert(uid_is_valid(uid));
+
+        /* Finds the machine for the specified host UID and returns it along with the UID translated into the
+         * internal UID inside the machine */
+
+        HASHMAP_FOREACH(machine, m->machines, i) {
+                uid_t converted;
+
+                r = machine_owns_uid(machine, uid, &converted);
+                if (r < 0)
+                        return r;
+                if (r) {
+                        if (ret_machine)
+                                *ret_machine = machine;
+
+                        if (ret_internal_uid)
+                                *ret_internal_uid = converted;
+
+                        return true;
+                }
+        }
+
+        if (ret_machine)
+                *ret_machine = NULL;
+        if (ret_internal_uid)
+                *ret_internal_uid = UID_INVALID;
+
+        return false;
+}
+
+int manager_find_machine_for_gid(Manager *m, gid_t gid, Machine **ret_machine, gid_t *ret_internal_gid) {
+        Machine *machine;
+        Iterator i;
+        int r;
+
+        assert(m);
+        assert(gid_is_valid(gid));
+
+        HASHMAP_FOREACH(machine, m->machines, i) {
+                gid_t converted;
+
+                r = machine_owns_gid(machine, gid, &converted);
+                if (r < 0)
+                        return r;
+                if (r) {
+                        if (ret_machine)
+                                *ret_machine = machine;
+
+                        if (ret_internal_gid)
+                                *ret_internal_gid = converted;
+
+                        return true;
+                }
+        }
+
+        if (ret_machine)
+                *ret_machine = NULL;
+        if (ret_internal_gid)
+                *ret_internal_gid = GID_INVALID;
+
+        return false;
+}
index 07b09050731777a656da0e54ab6f1ddec783695b..467f16b72a4113200141d179b00ca68ea086d0df 100644 (file)
@@ -8,8 +8,9 @@
 #include "alloc-util.h"
 #include "btrfs-util.h"
 #include "bus-common-errors.h"
+#include "bus-get-properties.h"
+#include "bus-locator.h"
 #include "bus-polkit.h"
-#include "bus-util.h"
 #include "cgroup-util.h"
 #include "errno-util.h"
 #include "fd-util.h"
@@ -620,12 +621,10 @@ static int clean_pool_done(Operation *operation, int ret, sd_bus_error *error) {
         if (lseek(operation->extra_fd, 0, SEEK_SET) == (off_t) -1)
                 return -errno;
 
-        f = fdopen(operation->extra_fd, "r");
+        f = take_fdopen(&operation->extra_fd, "r");
         if (!f)
                 return -errno;
 
-        operation->extra_fd = -1;
-
         /* The resulting temporary file starts with a boolean value that indicates success or not. */
         errno = 0;
         n = fread(&success, 1, sizeof(success), f);
@@ -884,11 +883,11 @@ static int method_set_image_limit(sd_bus_message *message, void *userdata, sd_bu
 }
 
 static int method_map_from_machine_user(sd_bus_message *message, void *userdata, sd_bus_error *error) {
-        _cleanup_fclose_ FILE *f = NULL;
         Manager *m = userdata;
-        const char *name, *p;
+        const char *name;
         Machine *machine;
         uint32_t uid;
+        uid_t converted;
         int r;
 
         r = sd_bus_message_read(message, "su", &name, &uid);
@@ -905,44 +904,20 @@ static int method_map_from_machine_user(sd_bus_message *message, void *userdata,
         if (machine->class != MACHINE_CONTAINER)
                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Not supported for non-container machines.");
 
-        p = procfs_file_alloca(machine->leader, "uid_map");
-        f = fopen(p, "re");
-        if (!f)
-                return -errno;
-
-        for (;;) {
-                uid_t uid_base, uid_shift, uid_range, converted;
-                int k;
-
-                errno = 0;
-                k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT, &uid_base, &uid_shift, &uid_range);
-                if (k < 0 && feof(f))
-                        break;
-                if (k != 3) {
-                        if (ferror(f))
-                                return errno_or_else(EIO);
-
-                        return -EIO;
-                }
-
-                if (uid < uid_base || uid >= uid_base + uid_range)
-                        continue;
-
-                converted = uid - uid_base + uid_shift;
-                if (!uid_is_valid(converted))
-                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid user ID " UID_FMT, uid);
-
-                return sd_bus_reply_method_return(message, "u", (uint32_t) converted);
-        }
+        r = machine_translate_uid(machine, uid, &converted);
+        if (r == -ESRCH)
+                return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER_MAPPING, "Machine '%s' has no matching user mappings.", name);
+        if (r < 0)
+                return r;
 
-        return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER_MAPPING, "Machine '%s' has no matching user mappings.", name);
+        return sd_bus_reply_method_return(message, "u", (uint32_t) converted);
 }
 
 static int method_map_to_machine_user(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        _cleanup_free_ char *o = NULL;
         Manager *m = userdata;
         Machine *machine;
-        uid_t uid;
-        Iterator i;
+        uid_t uid, converted;
         int r;
 
         r = sd_bus_message_read(message, "u", &uid);
@@ -953,63 +928,24 @@ static int method_map_to_machine_user(sd_bus_message *message, void *userdata, s
         if (uid < 0x10000)
                 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER_MAPPING, "User " UID_FMT " belongs to host UID range", uid);
 
-        HASHMAP_FOREACH(machine, m->machines, i) {
-                _cleanup_fclose_ FILE *f = NULL;
-                char p[STRLEN("/proc//uid_map") + DECIMAL_STR_MAX(pid_t) + 1];
-
-                if (machine->class != MACHINE_CONTAINER)
-                        continue;
-
-                xsprintf(p, "/proc/" UID_FMT "/uid_map", machine->leader);
-                f = fopen(p, "re");
-                if (!f) {
-                        log_warning_errno(errno, "Failed to open %s, ignoring,", p);
-                        continue;
-                }
-
-                for (;;) {
-                        _cleanup_free_ char *o = NULL;
-                        uid_t uid_base, uid_shift, uid_range, converted;
-                        int k;
-
-                        errno = 0;
-                        k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT, &uid_base, &uid_shift, &uid_range);
-                        if (k < 0 && feof(f))
-                                break;
-                        if (k != 3) {
-                                if (ferror(f))
-                                        return errno_or_else(EIO);
-
-                                return -EIO;
-                        }
-
-                        /* The private user namespace is disabled, ignoring. */
-                        if (uid_shift == 0)
-                                continue;
-
-                        if (uid < uid_shift || uid >= uid_shift + uid_range)
-                                continue;
-
-                        converted = (uid - uid_shift + uid_base);
-                        if (!uid_is_valid(converted))
-                                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid user ID " UID_FMT, uid);
-
-                        o = machine_bus_path(machine);
-                        if (!o)
-                                return -ENOMEM;
+        r = manager_find_machine_for_uid(m, uid, &machine, &converted);
+        if (r < 0)
+                return r;
+        if (!r)
+                return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER_MAPPING, "No matching user mapping for " UID_FMT ".", uid);
 
-                        return sd_bus_reply_method_return(message, "sou", machine->name, o, (uint32_t) converted);
-                }
-        }
+        o = machine_bus_path(machine);
+        if (!o)
+                return -ENOMEM;
 
-        return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER_MAPPING, "No matching user mapping for " UID_FMT ".", uid);
+        return sd_bus_reply_method_return(message, "sou", machine->name, o, (uint32_t) converted);
 }
 
-static int method_map_from_machine_group(sd_bus_message *message, void *groupdata, sd_bus_error *error) {
-        _cleanup_fclose_ FILE *f = NULL;
-        Manager *m = groupdata;
-        const char *name, *p;
+static int method_map_from_machine_group(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        Manager *m = userdata;
+        const char *name;
         Machine *machine;
+        gid_t converted;
         uint32_t gid;
         int r;
 
@@ -1027,44 +963,20 @@ static int method_map_from_machine_group(sd_bus_message *message, void *groupdat
         if (machine->class != MACHINE_CONTAINER)
                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Not supported for non-container machines.");
 
-        p = procfs_file_alloca(machine->leader, "gid_map");
-        f = fopen(p, "re");
-        if (!f)
-                return -errno;
-
-        for (;;) {
-                gid_t gid_base, gid_shift, gid_range, converted;
-                int k;
-
-                errno = 0;
-                k = fscanf(f, GID_FMT " " GID_FMT " " GID_FMT, &gid_base, &gid_shift, &gid_range);
-                if (k < 0 && feof(f))
-                        break;
-                if (k != 3) {
-                        if (ferror(f))
-                                return errno_or_else(EIO);
-
-                        return -EIO;
-                }
-
-                if (gid < gid_base || gid >= gid_base + gid_range)
-                        continue;
-
-                converted = gid - gid_base + gid_shift;
-                if (!gid_is_valid(converted))
-                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid group ID " GID_FMT, gid);
-
-                return sd_bus_reply_method_return(message, "u", (uint32_t) converted);
-        }
+        r = machine_translate_gid(machine, gid, &converted);
+        if (r == -ESRCH)
+                return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER_MAPPING, "Machine '%s' has no matching group mappings.", name);
+        if (r < 0)
+                return r;
 
-        return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_GROUP_MAPPING, "Machine '%s' has no matching group mappings.", name);
+        return sd_bus_reply_method_return(message, "u", (uint32_t) converted);
 }
 
-static int method_map_to_machine_group(sd_bus_message *message, void *groupdata, sd_bus_error *error) {
-        Manager *m = groupdata;
+static int method_map_to_machine_group(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        _cleanup_free_ char *o = NULL;
+        Manager *m = userdata;
         Machine *machine;
-        gid_t gid;
-        Iterator i;
+        gid_t gid, converted;
         int r;
 
         r = sd_bus_message_read(message, "u", &gid);
@@ -1075,105 +987,342 @@ static int method_map_to_machine_group(sd_bus_message *message, void *groupdata,
         if (gid < 0x10000)
                 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_GROUP_MAPPING, "Group " GID_FMT " belongs to host GID range", gid);
 
-        HASHMAP_FOREACH(machine, m->machines, i) {
-                _cleanup_fclose_ FILE *f = NULL;
-                char p[STRLEN("/proc//gid_map") + DECIMAL_STR_MAX(pid_t) + 1];
-
-                if (machine->class != MACHINE_CONTAINER)
-                        continue;
-
-                xsprintf(p, "/proc/" GID_FMT "/gid_map", machine->leader);
-                f = fopen(p, "re");
-                if (!f) {
-                        log_warning_errno(errno, "Failed to open %s, ignoring,", p);
-                        continue;
-                }
-
-                for (;;) {
-                        _cleanup_free_ char *o = NULL;
-                        gid_t gid_base, gid_shift, gid_range, converted;
-                        int k;
-
-                        errno = 0;
-                        k = fscanf(f, GID_FMT " " GID_FMT " " GID_FMT, &gid_base, &gid_shift, &gid_range);
-                        if (k < 0 && feof(f))
-                                break;
-                        if (k != 3) {
-                                if (ferror(f))
-                                        return errno_or_else(EIO);
-
-                                return -EIO;
-                        }
-
-                        /* The private user namespace is disabled, ignoring. */
-                        if (gid_shift == 0)
-                                continue;
-
-                        if (gid < gid_shift || gid >= gid_shift + gid_range)
-                                continue;
-
-                        converted = (gid - gid_shift + gid_base);
-                        if (!gid_is_valid(converted))
-                                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid group ID " GID_FMT, gid);
-
-                        o = machine_bus_path(machine);
-                        if (!o)
-                                return -ENOMEM;
+        r = manager_find_machine_for_gid(m, gid, &machine, &converted);
+        if (r < 0)
+                return r;
+        if (!r)
+                return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_GROUP_MAPPING, "No matching group mapping for " GID_FMT ".", gid);
 
-                        return sd_bus_reply_method_return(message, "sou", machine->name, o, (uint32_t) converted);
-                }
-        }
+        o = machine_bus_path(machine);
+        if (!o)
+                return -ENOMEM;
 
-        return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_GROUP_MAPPING, "No matching group mapping for " GID_FMT ".", gid);
+        return sd_bus_reply_method_return(message, "sou", machine->name, o, (uint32_t) converted);
 }
 
 const sd_bus_vtable manager_vtable[] = {
         SD_BUS_VTABLE_START(0),
+
         SD_BUS_PROPERTY("PoolPath", "s", property_get_pool_path, 0, 0),
         SD_BUS_PROPERTY("PoolUsage", "t", property_get_pool_usage, 0, 0),
         SD_BUS_PROPERTY("PoolLimit", "t", property_get_pool_limit, 0, 0),
-        SD_BUS_METHOD("GetMachine", "s", "o", method_get_machine, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("GetImage", "s", "o", method_get_image, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("GetMachineByPID", "u", "o", method_get_machine_by_pid, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("ListMachines", NULL, "a(ssso)", method_list_machines, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("ListImages", NULL, "a(ssbttto)", method_list_images, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("CreateMachine", "sayssusa(sv)", "o", method_create_machine, 0),
-        SD_BUS_METHOD("CreateMachineWithNetwork", "sayssusaia(sv)", "o", method_create_machine_with_network, 0),
-        SD_BUS_METHOD("RegisterMachine", "sayssus", "o", method_register_machine, 0),
-        SD_BUS_METHOD("RegisterMachineWithNetwork", "sayssusai", "o", method_register_machine_with_network, 0),
-        SD_BUS_METHOD("UnregisterMachine", "s", NULL, method_unregister_machine, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("TerminateMachine", "s", NULL, method_terminate_machine, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("KillMachine", "ssi", NULL, method_kill_machine, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("GetMachineAddresses", "s", "a(iay)", method_get_machine_addresses, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("GetMachineOSRelease", "s", "a{ss}", method_get_machine_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("OpenMachinePTY", "s", "hs", method_open_machine_pty, 0),
-        SD_BUS_METHOD("OpenMachineLogin", "s", "hs", method_open_machine_login, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("OpenMachineShell", "sssasas", "hs", method_open_machine_shell, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("BindMountMachine", "sssbb", NULL, method_bind_mount_machine, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("CopyFromMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("CopyToMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("OpenMachineRootDirectory", "s", "h", method_open_machine_root_directory, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("GetMachineUIDShift", "s", "u", method_get_machine_uid_shift, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("RemoveImage", "s", NULL, method_remove_image, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("RenameImage", "ss", NULL, method_rename_image, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("CloneImage", "ssb", NULL, method_clone_image, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("MarkImageReadOnly", "sb", NULL, method_mark_image_read_only, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("GetImageHostname", "s", "s", method_get_image_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("GetImageMachineID", "s", "ay", method_get_image_machine_id, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("GetImageMachineInfo", "s", "a{ss}", method_get_image_machine_info, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("GetImageOSRelease", "s", "a{ss}", method_get_image_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetPoolLimit", "t", NULL, method_set_pool_limit, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetImageLimit", "st", NULL, method_set_image_limit, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("CleanPool", "s", "a(st)", method_clean_pool, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("MapFromMachineUser", "su", "u", method_map_from_machine_user, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("MapToMachineUser", "u", "sou", method_map_to_machine_user, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("MapFromMachineGroup", "su", "u", method_map_from_machine_group, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("MapToMachineGroup", "u", "sou", method_map_to_machine_group, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_SIGNAL("MachineNew", "so", 0),
-        SD_BUS_SIGNAL("MachineRemoved", "so", 0),
+
+        SD_BUS_METHOD_WITH_NAMES("GetMachine",
+                                 "s",
+                                 SD_BUS_PARAM(name),
+                                 "o",
+                                 SD_BUS_PARAM(machine),
+                                 method_get_machine,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("GetImage",
+                                 "s",
+                                 SD_BUS_PARAM(name),
+                                 "o",
+                                 SD_BUS_PARAM(image),
+                                 method_get_image,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("GetMachineByPID",
+                                 "u",
+                                 SD_BUS_PARAM(pid),
+                                 "o",
+                                 SD_BUS_PARAM(machine),
+                                 method_get_machine_by_pid,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("ListMachines",
+                                 NULL,,
+                                 "a(ssso)",
+                                 SD_BUS_PARAM(machines),
+                                 method_list_machines,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("ListImages",
+                                 NULL,,
+                                 "a(ssbttto)",
+                                 SD_BUS_PARAM(images),
+                                 method_list_images,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("CreateMachine",
+                                 "sayssusa(sv)",
+                                 SD_BUS_PARAM(name)
+                                 SD_BUS_PARAM(id)
+                                 SD_BUS_PARAM(service)
+                                 SD_BUS_PARAM(class)
+                                 SD_BUS_PARAM(leader)
+                                 SD_BUS_PARAM(root_directory)
+                                 SD_BUS_PARAM(scope_properties),
+                                 "o",
+                                 SD_BUS_PARAM(path),
+                                 method_create_machine, 0),
+        SD_BUS_METHOD_WITH_NAMES("CreateMachineWithNetwork",
+                                 "sayssusaia(sv)",
+                                 SD_BUS_PARAM(name)
+                                 SD_BUS_PARAM(id)
+                                 SD_BUS_PARAM(service)
+                                 SD_BUS_PARAM(class)
+                                 SD_BUS_PARAM(leader)
+                                 SD_BUS_PARAM(root_directory)
+                                 SD_BUS_PARAM(ifindices)
+                                 SD_BUS_PARAM(scope_properties),
+                                 "o",
+                                 SD_BUS_PARAM(path),
+                                 method_create_machine_with_network, 0),
+        SD_BUS_METHOD_WITH_NAMES("RegisterMachine",
+                                 "sayssus",
+                                 SD_BUS_PARAM(name)
+                                 SD_BUS_PARAM(id)
+                                 SD_BUS_PARAM(service)
+                                 SD_BUS_PARAM(class)
+                                 SD_BUS_PARAM(leader)
+                                 SD_BUS_PARAM(root_directory),
+                                 "o",
+                                 SD_BUS_PARAM(path),
+                                 method_register_machine, 0),
+        SD_BUS_METHOD_WITH_NAMES("RegisterMachineWithNetwork",
+                                 "sayssusai",
+                                 SD_BUS_PARAM(name)
+                                 SD_BUS_PARAM(id)
+                                 SD_BUS_PARAM(service)
+                                 SD_BUS_PARAM(class)
+                                 SD_BUS_PARAM(leader)
+                                 SD_BUS_PARAM(root_directory)
+                                 SD_BUS_PARAM(ifindices),
+                                 "o",
+                                 SD_BUS_PARAM(path),
+                                 method_register_machine_with_network, 0),
+        SD_BUS_METHOD_WITH_NAMES("UnregisterMachine",
+                                 "s",
+                                 SD_BUS_PARAM(name),
+                                 NULL,,
+                                 method_unregister_machine,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("TerminateMachine",
+                                 "s",
+                                 SD_BUS_PARAM(id),
+                                 NULL,,
+                                 method_terminate_machine,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("KillMachine",
+                                 "ssi",
+                                 SD_BUS_PARAM(name)
+                                 SD_BUS_PARAM(who)
+                                 SD_BUS_PARAM(signal),
+                                 NULL,,
+                                 method_kill_machine,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("GetMachineAddresses",
+                                 "s",
+                                 SD_BUS_PARAM(name),
+                                 "a(iay)",
+                                 SD_BUS_PARAM(addresses),
+                                 method_get_machine_addresses,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("GetMachineOSRelease",
+                                 "s",
+                                 SD_BUS_PARAM(name),
+                                 "a{ss}",
+                                 SD_BUS_PARAM(fields),
+                                 method_get_machine_os_release,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("OpenMachinePTY",
+                                 "s",
+                                 SD_BUS_PARAM(name),
+                                 "hs",
+                                 SD_BUS_PARAM(pty)
+                                 SD_BUS_PARAM(pty_path),
+                                 method_open_machine_pty,
+                                 0),
+        SD_BUS_METHOD_WITH_NAMES("OpenMachineLogin",
+                                 "s",
+                                 SD_BUS_PARAM(name),
+                                 "hs",
+                                 SD_BUS_PARAM(pty)
+                                 SD_BUS_PARAM(pty_path),
+                                 method_open_machine_login,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("OpenMachineShell",
+                                 "sssasas",
+                                 SD_BUS_PARAM(name)
+                                 SD_BUS_PARAM(user)
+                                 SD_BUS_PARAM(path)
+                                 SD_BUS_PARAM(args)
+                                 SD_BUS_PARAM(environment),
+                                 "hs",
+                                 SD_BUS_PARAM(pty)
+                                 SD_BUS_PARAM(pty_path),
+                                 method_open_machine_shell,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("BindMountMachine",
+                                 "sssbb",
+                                 SD_BUS_PARAM(name)
+                                 SD_BUS_PARAM(source)
+                                 SD_BUS_PARAM(destination)
+                                 SD_BUS_PARAM(read_only)
+                                 SD_BUS_PARAM(mkdir),
+                                 NULL,,
+                                 method_bind_mount_machine,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("CopyFromMachine",
+                                 "sss",
+                                 SD_BUS_PARAM(name)
+                                 SD_BUS_PARAM(source)
+                                 SD_BUS_PARAM(destination),
+                                 NULL,,
+                                 method_copy_machine,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("CopyToMachine",
+                                 "sss",
+                                 SD_BUS_PARAM(name)
+                                 SD_BUS_PARAM(source)
+                                 SD_BUS_PARAM(destination),
+                                 NULL,,
+                                 method_copy_machine,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("OpenMachineRootDirectory",
+                                 "s",
+                                 SD_BUS_PARAM(name),
+                                 "h",
+                                 SD_BUS_PARAM(fd),
+                                 method_open_machine_root_directory,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("GetMachineUIDShift",
+                                 "s",
+                                 SD_BUS_PARAM(name),
+                                 "u",
+                                 SD_BUS_PARAM(shift),
+                                 method_get_machine_uid_shift,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("RemoveImage",
+                                 "s",
+                                 SD_BUS_PARAM(name),
+                                 NULL,,
+                                 method_remove_image,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("RenameImage",
+                                 "ss",
+                                 SD_BUS_PARAM(name)
+                                 SD_BUS_PARAM(new_name),
+                                 NULL,,
+                                 method_rename_image,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("CloneImage",
+                                 "ssb",
+                                 SD_BUS_PARAM(name)
+                                 SD_BUS_PARAM(new_name)
+                                 SD_BUS_PARAM(read_only),
+                                 NULL,,
+                                 method_clone_image,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("MarkImageReadOnly",
+                                 "sb",
+                                 SD_BUS_PARAM(name)
+                                 SD_BUS_PARAM(read_only),
+                                 NULL,,
+                                 method_mark_image_read_only,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("GetImageHostname",
+                                 "s",
+                                 SD_BUS_PARAM(name),
+                                 "s",
+                                 SD_BUS_PARAM(hostname),
+                                 method_get_image_hostname,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("GetImageMachineID",
+                                 "s",
+                                 SD_BUS_PARAM(name),
+                                 "ay",
+                                 SD_BUS_PARAM(id),
+                                 method_get_image_machine_id,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("GetImageMachineInfo",
+                                 "s",
+                                 SD_BUS_PARAM(name),
+                                 "a{ss}",
+                                 SD_BUS_PARAM(machine_info),
+                                 method_get_image_machine_info,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("GetImageOSRelease",
+                                 "s",
+                                 SD_BUS_PARAM(name),
+                                 "a{ss}",
+                                 SD_BUS_PARAM(os_release),
+                                 method_get_image_os_release,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("SetPoolLimit",
+                                 "t",
+                                 SD_BUS_PARAM(size),
+                                 NULL,,
+                                 method_set_pool_limit,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("SetImageLimit",
+                                 "st",
+                                 SD_BUS_PARAM(name)
+                                 SD_BUS_PARAM(size),
+                                 NULL,,
+                                 method_set_image_limit,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("CleanPool",
+                                 "s",
+                                 SD_BUS_PARAM(mode),
+                                 "a(st)",
+                                 SD_BUS_PARAM(images),
+                                 method_clean_pool,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("MapFromMachineUser",
+                                 "su",
+                                 SD_BUS_PARAM(name)
+                                 SD_BUS_PARAM(uid_inner),
+                                 "u",
+                                 SD_BUS_PARAM(uid_outer),
+                                 method_map_from_machine_user,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("MapToMachineUser",
+                                 "u",
+                                 SD_BUS_PARAM(uid_outer),
+                                 "sou",
+                                 SD_BUS_PARAM(machine_name)
+                                 SD_BUS_PARAM(machine_path)
+                                 SD_BUS_PARAM(uid_inner),
+                                 method_map_to_machine_user,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("MapFromMachineGroup",
+                                 "su",
+                                 SD_BUS_PARAM(name)
+                                 SD_BUS_PARAM(gid_inner),
+                                 "u",
+                                 SD_BUS_PARAM(gid_outer),
+                                 method_map_from_machine_group,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("MapToMachineGroup",
+                                 "u",
+                                 SD_BUS_PARAM(gid_outer),
+                                 "sou",
+                                 SD_BUS_PARAM(machine_name)
+                                 SD_BUS_PARAM(machine_path)
+                                 SD_BUS_PARAM(gid_inner),
+                                 method_map_to_machine_group,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+
+        SD_BUS_SIGNAL_WITH_NAMES("MachineNew",
+                                 "so",
+                                 SD_BUS_PARAM(machine)
+                                 SD_BUS_PARAM(path),
+                                 0),
+        SD_BUS_SIGNAL_WITH_NAMES("MachineRemoved",
+                                 "so",
+                                 SD_BUS_PARAM(machine)
+                                 SD_BUS_PARAM(path),
+                                 0),
+
         SD_BUS_VTABLE_END
 };
 
+const BusObjectImplementation manager_object = {
+        "/org/freedesktop/machine1",
+        "org.freedesktop.machine1.Manager",
+        .vtables = BUS_VTABLES(manager_vtable),
+        .children = BUS_IMPLEMENTATIONS( &machine_object,
+                                         &image_object ),
+};
+
 int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         const char *path, *result, *unit;
         Manager *m = userdata;
@@ -1303,16 +1452,7 @@ int manager_unref_unit(
         assert(m);
         assert(unit);
 
-        return sd_bus_call_method(
-                        m->bus,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "UnrefUnit",
-                        error,
-                        NULL,
-                        "s",
-                        unit);
+        return bus_call_method(m->bus, bus_systemd_mgr, "UnrefUnit", error, NULL, "s", unit);
 }
 
 int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job) {
@@ -1322,15 +1462,7 @@ int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, c
         assert(manager);
         assert(unit);
 
-        r = sd_bus_call_method(
-                        manager->bus,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "StopUnit",
-                        error,
-                        &reply,
-                        "ss", unit, "fail");
+        r = bus_call_method(manager->bus, bus_systemd_mgr, "StopUnit", error, &reply, "ss", unit, "fail");
         if (r < 0) {
                 if (sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) ||
                     sd_bus_error_has_name(error, BUS_ERROR_LOAD_FAILED)) {
@@ -1367,15 +1499,7 @@ int manager_kill_unit(Manager *manager, const char *unit, int signo, sd_bus_erro
         assert(manager);
         assert(unit);
 
-        return sd_bus_call_method(
-                        manager->bus,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "KillUnit",
-                        error,
-                        NULL,
-                        "ssi", unit, "all", signo);
+        return bus_call_method(manager->bus, bus_systemd_mgr, "KillUnit", error, NULL, "ssi", unit, "all", signo);
 }
 
 int manager_unit_is_active(Manager *manager, const char *unit) {
diff --git a/src/machine/machined-varlink.c b/src/machine/machined-varlink.c
new file mode 100644 (file)
index 0000000..058ee5c
--- /dev/null
@@ -0,0 +1,404 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "format-util.h"
+#include "machined-varlink.h"
+#include "mkdir.h"
+#include "user-util.h"
+#include "varlink.h"
+
+typedef struct LookupParameters {
+        const char *user_name;
+        const char *group_name;
+        union {
+                uid_t uid;
+                gid_t gid;
+        };
+        const char *service;
+} LookupParameters;
+
+static int build_user_json(const char *user_name, uid_t uid, const char *real_name, JsonVariant **ret) {
+        assert(user_name);
+        assert(uid_is_valid(uid));
+        assert(ret);
+
+        return json_build(ret, JSON_BUILD_OBJECT(
+                                   JSON_BUILD_PAIR("record", JSON_BUILD_OBJECT(
+                                       JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(user_name)),
+                                       JSON_BUILD_PAIR("uid", JSON_BUILD_UNSIGNED(uid)),
+                                       JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(GID_NOBODY)),
+                                       JSON_BUILD_PAIR_CONDITION(!isempty(real_name), "realName", JSON_BUILD_STRING(real_name)),
+                                       JSON_BUILD_PAIR("homeDirectory", JSON_BUILD_STRING("/")),
+                                       JSON_BUILD_PAIR("shell", JSON_BUILD_STRING(NOLOGIN)),
+                                       JSON_BUILD_PAIR("locked", JSON_BUILD_BOOLEAN(true)),
+                                       JSON_BUILD_PAIR("service", JSON_BUILD_STRING("io.systemd.Machine")),
+                                       JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("container"))))));
+}
+
+static bool user_match_lookup_parameters(LookupParameters *p, const char *name, uid_t uid) {
+        assert(p);
+
+        if (p->user_name && !streq(name, p->user_name))
+                return false;
+
+        if (uid_is_valid(p->uid) && uid != p->uid)
+                return false;
+
+        return true;
+}
+
+static int user_lookup_uid(Manager *m, uid_t uid, char **ret_name, char **ret_real_name) {
+        _cleanup_free_ char *n = NULL, *rn = NULL;
+        uid_t converted_uid;
+        Machine *machine;
+        int r;
+
+        assert(m);
+        assert(uid_is_valid(uid));
+        assert(ret_name);
+        assert(ret_real_name);
+
+        if (uid < 0x10000) /* Host UID range */
+                return -ESRCH;
+
+        r = manager_find_machine_for_uid(m, uid, &machine, &converted_uid);
+        if (r < 0)
+                return r;
+        if (!r)
+                return -ESRCH;
+
+        if (asprintf(&n, "vu-%s-" UID_FMT, machine->name, converted_uid) < 0)
+                return -ENOMEM;
+
+        /* Don't synthesize invalid user/group names (too long...) */
+        if (!valid_user_group_name(n, 0))
+                return -ESRCH;
+
+        if (asprintf(&rn, "UID " UID_FMT " of Container %s", converted_uid, machine->name) < 0)
+                return -ENOMEM;
+
+        /* Don't synthesize invalid real names either, but since this field doesn't matter much, simply invalidate things */
+        if (!valid_gecos(rn))
+                rn = mfree(rn);
+
+        *ret_name = TAKE_PTR(n);
+        *ret_real_name = TAKE_PTR(rn);
+        return 0;
+}
+
+static int user_lookup_name(Manager *m, const char *name, uid_t *ret_uid, char **ret_real_name) {
+        _cleanup_free_ char *mn = NULL, *rn = NULL;
+        uid_t uid, converted_uid;
+        Machine *machine;
+        const char *e, *d;
+        int r;
+
+        assert(m);
+
+        if (!valid_user_group_name(name, 0))
+                return -ESRCH;
+
+        e = startswith(name, "vu-");
+        if (!e)
+                return -ESRCH;
+
+        d = strrchr(e, '-');
+        if (!d)
+                return -ESRCH;
+
+        if (parse_uid(d + 1, &uid) < 0)
+                return -ESRCH;
+
+        mn = strndup(e, d - e);
+        if (!mn)
+                return -ENOMEM;
+
+        machine = hashmap_get(m->machines, mn);
+        if (!machine)
+                return -ESRCH;
+
+        if (machine->class != MACHINE_CONTAINER)
+                return -ESRCH;
+
+        r = machine_translate_uid(machine, uid, &converted_uid);
+        if (r < 0)
+                return r;
+
+        if (asprintf(&rn, "UID " UID_FMT " of Container %s", uid, machine->name) < 0)
+                return -ENOMEM;
+        if (!valid_gecos(rn))
+                rn = mfree(rn);
+
+        *ret_uid = converted_uid;
+        *ret_real_name = TAKE_PTR(rn);
+        return 0;
+}
+
+static int vl_method_get_user_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+
+        static const JsonDispatch dispatch_table[] = {
+                { "uid",      JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid,      offsetof(LookupParameters, uid),       0         },
+                { "userName", JSON_VARIANT_STRING,   json_dispatch_const_string, offsetof(LookupParameters, user_name), JSON_SAFE },
+                { "service",  JSON_VARIANT_STRING,   json_dispatch_const_string, offsetof(LookupParameters, service),   0         },
+                {}
+        };
+
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+        LookupParameters p = {
+                .uid = UID_INVALID,
+        };
+        _cleanup_free_ char *found_name = NULL, *found_real_name = NULL;
+        uid_t found_uid = UID_INVALID, uid;
+        Manager *m = userdata;
+        const char *un;
+        int r;
+
+        assert(parameters);
+        assert(m);
+
+        r = json_dispatch(parameters, dispatch_table, NULL, 0, &p);
+        if (r < 0)
+                return r;
+
+        if (!streq_ptr(p.service, "io.systemd.Machine"))
+                return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
+
+        if (uid_is_valid(p.uid))
+                r = user_lookup_uid(m, p.uid, &found_name, &found_real_name);
+        else if (p.user_name)
+                r = user_lookup_name(m, p.user_name, &found_uid, &found_real_name);
+        else
+                return varlink_error(link, "io.systemd.UserDatabase.EnumerationNotSupported", NULL);
+        if (r == -ESRCH)
+                return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+        if (r < 0)
+                return r;
+
+        uid = uid_is_valid(found_uid) ? found_uid : p.uid;
+        un = found_name ?: p.user_name;
+
+        if (!user_match_lookup_parameters(&p, un, uid))
+                return varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
+
+        r = build_user_json(un, uid, found_real_name, &v);
+        if (r < 0)
+                return r;
+
+        return varlink_reply(link, v);
+}
+
+static int build_group_json(const char *group_name, gid_t gid, JsonVariant **ret) {
+        assert(group_name);
+        assert(gid_is_valid(gid));
+        assert(ret);
+
+        return json_build(ret, JSON_BUILD_OBJECT(
+                                   JSON_BUILD_PAIR("record", JSON_BUILD_OBJECT(
+                                       JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(group_name)),
+                                       JSON_BUILD_PAIR("gid", JSON_BUILD_UNSIGNED(gid)),
+                                       JSON_BUILD_PAIR("service", JSON_BUILD_STRING("io.systemd.Machine")),
+                                       JSON_BUILD_PAIR("disposition", JSON_BUILD_STRING("container"))))));
+    }
+
+static bool group_match_lookup_parameters(LookupParameters *p, const char *name, gid_t gid) {
+        assert(p);
+
+        if (p->group_name && !streq(name, p->group_name))
+                return false;
+
+        if (gid_is_valid(p->gid) && gid != p->gid)
+                return false;
+
+        return true;
+}
+
+static int group_lookup_gid(Manager *m, gid_t gid, char **ret_name) {
+        _cleanup_free_ char *n = NULL;
+        gid_t converted_gid;
+        Machine *machine;
+        int r;
+
+        assert(m);
+        assert(gid_is_valid(gid));
+        assert(ret_name);
+
+        if (gid < 0x10000) /* Host GID range */
+                return -ESRCH;
+
+        r = manager_find_machine_for_gid(m, gid, &machine, &converted_gid);
+        if (r < 0)
+                return r;
+        if (!r)
+                return -ESRCH;
+
+        if (asprintf(&n, "vg-%s-" GID_FMT, machine->name, converted_gid) < 0)
+                return -ENOMEM;
+
+        if (!valid_user_group_name(n, 0))
+                return -ESRCH;
+
+        *ret_name = TAKE_PTR(n);
+        return 0;
+}
+
+static int group_lookup_name(Manager *m, const char *name, gid_t *ret_gid) {
+        _cleanup_free_ char *mn = NULL;
+        gid_t gid, converted_gid;
+        Machine *machine;
+        const char *e, *d;
+        int r;
+
+        assert(m);
+
+        if (!valid_user_group_name(name, 0))
+                return -ESRCH;
+
+        e = startswith(name, "vg-");
+        if (!e)
+                return -ESRCH;
+
+        d = strrchr(e, '-');
+        if (!d)
+                return -ESRCH;
+
+        if (parse_gid(d + 1, &gid) < 0)
+                return -ESRCH;
+
+        mn = strndup(e, d - e);
+        if (!mn)
+                return -ENOMEM;
+
+        machine = hashmap_get(m->machines, mn);
+        if (!machine)
+                return -ESRCH;
+
+        if (machine->class != MACHINE_CONTAINER)
+                return -ESRCH;
+
+        r = machine_translate_gid(machine, gid, &converted_gid);
+        if (r < 0)
+                return r;
+
+        *ret_gid = converted_gid;
+        return 0;
+}
+
+static int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+
+        static const JsonDispatch dispatch_table[] = {
+                { "gid",       JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid,      offsetof(LookupParameters, gid),        0         },
+                { "groupName", JSON_VARIANT_STRING,   json_dispatch_const_string, offsetof(LookupParameters, group_name), JSON_SAFE },
+                { "service",   JSON_VARIANT_STRING,   json_dispatch_const_string, offsetof(LookupParameters, service),    0         },
+                {}
+        };
+
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+        LookupParameters p = {
+                .gid = GID_INVALID,
+        };
+        _cleanup_free_ char *found_name = NULL;
+        uid_t found_gid = GID_INVALID, gid;
+        Manager *m = userdata;
+        const char *gn;
+        int r;
+
+        assert(parameters);
+        assert(m);
+
+        r = json_dispatch(parameters, dispatch_table, NULL, 0, &p);
+        if (r < 0)
+                return r;
+
+        if (!streq_ptr(p.service, "io.systemd.Machine"))
+                return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
+
+        if (gid_is_valid(p.gid))
+                r = group_lookup_gid(m, p.gid, &found_name);
+        else if (p.group_name)
+                r = group_lookup_name(m, p.group_name, (uid_t*) &found_gid);
+        else
+                return varlink_error(link, "io.systemd.UserDatabase.EnumerationNotSupported", NULL);
+        if (r == -ESRCH)
+                return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+        if (r < 0)
+                return r;
+
+        gid = gid_is_valid(found_gid) ? found_gid : p.gid;
+        gn = found_name ?: p.group_name;
+
+        if (!group_match_lookup_parameters(&p, gn, gid))
+                return varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
+
+        r = build_group_json(gn, gid, &v);
+        if (r < 0)
+                return r;
+
+        return varlink_reply(link, v);
+}
+
+static int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+
+        static const JsonDispatch dispatch_table[] = {
+                { "userName",  JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, user_name),  JSON_SAFE },
+                { "groupName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, group_name), JSON_SAFE },
+                { "service",   JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service),    0         },
+                {}
+        };
+
+        LookupParameters p = {};
+        int r;
+
+        assert(parameters);
+
+        r = json_dispatch(parameters, dispatch_table, NULL, 0, &p);
+        if (r < 0)
+                return r;
+
+        if (!streq_ptr(p.service, "io.systemd.Machine"))
+                return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
+
+        /* We don't support auxiliary groups for machines. */
+        return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
+}
+
+int manager_varlink_init(Manager *m) {
+        _cleanup_(varlink_server_unrefp) VarlinkServer *s = NULL;
+        int r;
+
+        assert(m);
+
+        if (m->varlink_server)
+                return 0;
+
+        r = varlink_server_new(&s, VARLINK_SERVER_ACCOUNT_UID);
+        if (r < 0)
+                return log_error_errno(r, "Failed to allocate varlink server object: %m");
+
+        varlink_server_set_userdata(s, m);
+
+        r = varlink_server_bind_method_many(
+                        s,
+                        "io.systemd.UserDatabase.GetUserRecord",  vl_method_get_user_record,
+                        "io.systemd.UserDatabase.GetGroupRecord", vl_method_get_group_record,
+                        "io.systemd.UserDatabase.GetMemberships", vl_method_get_memberships);
+        if (r < 0)
+                return log_error_errno(r, "Failed to register varlink methods: %m");
+
+        (void) mkdir_p("/run/systemd/userdb", 0755);
+
+        r = varlink_server_listen_address(s, "/run/systemd/userdb/io.systemd.Machine", 0666);
+        if (r < 0)
+                return log_error_errno(r, "Failed to bind to varlink socket: %m");
+
+        r = varlink_server_attach_event(s, m->event, SD_EVENT_PRIORITY_NORMAL);
+        if (r < 0)
+                return log_error_errno(r, "Failed to attach varlink connection to event loop: %m");
+
+        m->varlink_server = TAKE_PTR(s);
+        return 0;
+}
+
+void manager_varlink_done(Manager *m) {
+        assert(m);
+
+        m->varlink_server = varlink_server_unref(m->varlink_server);
+}
diff --git a/src/machine/machined-varlink.h b/src/machine/machined-varlink.h
new file mode 100644 (file)
index 0000000..42e1c3f
--- /dev/null
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "machined.h"
+
+int manager_varlink_init(Manager *m);
+void manager_varlink_done(Manager *m);
index ace2131c2deaa512bb9960d73277dd2093b0fe3c..a884f3de9bda00370fbe580e9c8d1f69f8221c05 100644 (file)
@@ -10,6 +10,8 @@
 
 #include "alloc-util.h"
 #include "bus-error.h"
+#include "bus-locator.h"
+#include "bus-log-control-api.h"
 #include "bus-polkit.h"
 #include "cgroup-util.h"
 #include "dirent-util.h"
 #include "hostname-util.h"
 #include "label.h"
 #include "machine-image.h"
+#include "machined-varlink.h"
 #include "machined.h"
 #include "main-func.h"
 #include "process-util.h"
+#include "service-util.h"
 #include "signal-util.h"
 #include "special.h"
 
@@ -83,6 +87,8 @@ static Manager* manager_unref(Manager *m) {
 
         bus_verify_polkit_async_registry_free(m->polkit_registry);
 
+        manager_varlink_done(m);
+
         sd_bus_flush_close_unref(m->bus);
         sd_event_unref(m->event);
 
@@ -187,45 +193,15 @@ static int manager_connect_bus(Manager *m) {
         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/machine1", "org.freedesktop.machine1.Manager", manager_vtable, m);
-        if (r < 0)
-                return log_error_errno(r, "Failed to add manager object vtable: %m");
-
-        r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/machine1/machine", "org.freedesktop.machine1.Machine", machine_vtable, machine_object_find, m);
-        if (r < 0)
-                return log_error_errno(r, "Failed to add machine object vtable: %m");
-
-        r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/machine1/machine", machine_node_enumerator, m);
+        r = bus_add_implementation(m->bus, &manager_object, m);
         if (r < 0)
-                return log_error_errno(r, "Failed to add machine enumerator: %m");
-
-        r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/machine1/image", "org.freedesktop.machine1.Image", image_vtable, image_object_find, m);
-        if (r < 0)
-                return log_error_errno(r, "Failed to add image object vtable: %m");
+                return r;
 
-        r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/machine1/image", image_node_enumerator, m);
-        if (r < 0)
-                return log_error_errno(r, "Failed to add image enumerator: %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);
+        r = bus_match_signal_async(m->bus, NULL, bus_systemd_mgr, "JobRemoved", match_job_removed, NULL, m);
         if (r < 0)
                 return log_error_errno(r, "Failed to add 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);
+        r = bus_match_signal_async(m->bus, NULL, bus_systemd_mgr, "UnitRemoved", match_unit_removed, NULL, m);
         if (r < 0)
                 return log_error_errno(r, "Failed to request match for UnitRemoved: %m");
 
@@ -240,29 +216,18 @@ static int manager_connect_bus(Manager *m) {
         if (r < 0)
                 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);
+        r = bus_match_signal_async(m->bus, NULL, bus_systemd_mgr, "Reloading", match_reloading, NULL, m);
         if (r < 0)
                 return log_error_errno(r, "Failed to request match for Reloading: %m");
 
-        r = sd_bus_call_method_async(
-                        m->bus,
-                        NULL,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "Subscribe",
-                        NULL, NULL,
-                        NULL);
+        r = bus_call_method_async(m->bus, NULL, bus_systemd_mgr, "Subscribe", NULL, NULL, NULL);
         if (r < 0)
                 return log_error_errno(r, "Failed to enable subscription: %m");
 
+        r = bus_log_control_api_register(m->bus);
+        if (r < 0)
+                return r;
+
         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 request name: %m");
@@ -310,6 +275,11 @@ static int manager_startup(Manager *m) {
         if (r < 0)
                 return r;
 
+        /* Set up Varlink service */
+        r = manager_varlink_init(m);
+        if (r < 0)
+                return r;
+
         /* Deserialize state */
         manager_enumerate_machines(m);
 
@@ -329,6 +299,9 @@ static bool check_idle(void *userdata) {
         if (m->operations)
                 return false;
 
+        if (varlink_server_current_connections(m->varlink_server) > 0)
+                return false;
+
         manager_gc(m, true);
 
         return hashmap_isempty(m->machines);
@@ -352,12 +325,15 @@ static int run(int argc, char *argv[]) {
         log_set_facility(LOG_AUTH);
         log_setup_service();
 
-        umask(0022);
+        r = service_parse_argv("systemd-machined.service",
+                               "Manage registrations of local VMs and containers.",
+                               BUS_IMPLEMENTATIONS(&manager_object,
+                                                   &log_control_object),
+                               argc, argv);
+        if (r <= 0)
+                return r;
 
-        if (argc != 1) {
-                log_error("This program takes no arguments.");
-                return -EINVAL;
-        }
+        umask(0022);
 
         /* Always create the directories people can create inotify watches in. Note that some applications might check
          * for the existence of /run/systemd/machines/ to determine whether machined is available, so please always
index 205d90f83dfd646ec4306265924097fae499f653..c83017acb897034f51595534c44acda30c4f8d93 100644 (file)
@@ -6,15 +6,15 @@
 #include "sd-bus.h"
 #include "sd-event.h"
 
-#include "hashmap.h"
-#include "list.h"
-
 typedef struct Manager Manager;
 
+#include "hashmap.h"
 #include "image-dbus.h"
+#include "list.h"
 #include "machine-dbus.h"
 #include "machine.h"
 #include "operation.h"
+#include "varlink.h"
 
 struct Manager {
         sd_event *event;
@@ -37,12 +37,14 @@ struct Manager {
         unsigned n_operations;
 
         sd_event_source *nscd_cache_flush_event;
+
+        VarlinkServer *varlink_server;
 };
 
 int manager_add_machine(Manager *m, const char *name, Machine **_machine);
 int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine);
 
-extern const sd_bus_vtable manager_vtable[];
+extern const BusObjectImplementation manager_object;
 
 int match_reloading(sd_bus_message *message, void *userdata, sd_bus_error *error);
 int match_unit_removed(sd_bus_message *message, void *userdata, sd_bus_error *error);
@@ -56,3 +58,6 @@ int manager_unit_is_active(Manager *manager, const char *unit);
 int manager_job_is_active(Manager *manager, const char *path);
 
 int manager_enqueue_nscd_cache_flush(Manager *m);
+
+int manager_find_machine_for_uid(Manager *m, uid_t host_uid, Machine **ret_machine, uid_t *ret_internal_uid);
+int manager_find_machine_for_gid(Manager *m, gid_t host_gid, Machine **ret_machine, gid_t *ret_internal_gid);
index bc670714b45f19e8d317c1ec6073380c7a1f4d60..291630ece988b445daa600cb8e728a63985a744f 100644 (file)
@@ -6,14 +6,16 @@ systemd_machined_sources = files('''
 '''.split())
 
 libmachine_core_sources = files('''
+        image-dbus.c
+        image-dbus.h
+        machine-dbus.c
+        machine-dbus.h
         machine.c
         machine.h
-        machined-dbus.c
         machined-core.c
-        machine-dbus.c
-        machine-dbus.h
-        image-dbus.c
-        image-dbus.h
+        machined-dbus.c
+        machined-varlink.c
+        machined-varlink.h
         operation.c
         operation.h
 '''.split())
index 982e0a285e386e97b1a539413ba335e55001253f..22da189684fb3e45d716f04baa326330129623ae 100644 (file)
@@ -6,8 +6,8 @@
 #include "sd-device.h"
 
 #include "bus-error.h"
+#include "bus-locator.h"
 #include "bus-unit-util.h"
-#include "bus-util.h"
 #include "bus-wait-for-jobs.h"
 #include "device-util.h"
 #include "dirent-util.h"
@@ -364,7 +364,7 @@ static int parse_argv(int argc, char *argv[]) {
                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                                "At most two arguments required.");
 
-                if (arg_mount_type && (fstype_is_api_vfs(arg_mount_type) || fstype_is_network(arg_mount_type))) {
+                if (arg_mount_type && !fstype_is_blockdev_backed(arg_mount_type)) {
                         arg_mount_what = strdup(argv[optind]);
                         if (!arg_mount_what)
                                 return log_oom();
@@ -551,13 +551,7 @@ static int start_transient_mount(
         if (r < 0)
                 return log_error_errno(r, "Failed to make mount unit name: %m");
 
-        r = sd_bus_message_new_method_call(
-                        bus,
-                        &m,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "StartTransientUnit");
+        r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "StartTransientUnit");
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -638,13 +632,7 @@ static int start_transient_automount(
         if (r < 0)
                 return log_error_errno(r, "Failed to make mount unit name: %m");
 
-        r = sd_bus_message_new_method_call(
-                        bus,
-                        &m,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "StartTransientUnit");
+        r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "StartTransientUnit");
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -854,13 +842,7 @@ static int stop_mount(
         if (r < 0)
                 return log_error_errno(r, "Failed to make %s unit name from path %s: %m", suffix + 1, where);
 
-        r = sd_bus_message_new_method_call(
-                        bus,
-                        &m,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "StopUnit");
+        r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "StopUnit");
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -954,10 +936,9 @@ static int umount_by_device(sd_bus *bus, const char *what) {
         if (r < 0)
                 return log_device_error_errno(d, r, "Failed to get device property: %m");
 
-        if (!streq(v, "filesystem")) {
-                log_device_error(d, "%s does not contain a known file system.", what);
-                return -EINVAL;
-        }
+        if (!streq(v, "filesystem"))
+                return log_device_error_errno(d, SYNTHETIC_ERRNO(EINVAL),
+                                              "%s does not contain a known file system.", what);
 
         if (sd_device_get_property_value(d, "SYSTEMD_MOUNT_WHERE", &v) >= 0)
                 r2 = stop_mounts(bus, v);
@@ -1293,10 +1274,9 @@ static int discover_loop_backing_file(void) {
         if (r < 0)
                 return log_error_errno(r, "Failed to get device from device number: %m");
 
-        if (sd_device_get_property_value(d, "ID_FS_USAGE", &v) < 0 || !streq(v, "filesystem")) {
-                log_device_error(d, "%s does not contain a known file system.", arg_mount_what);
-                return -EINVAL;
-        }
+        if (sd_device_get_property_value(d, "ID_FS_USAGE", &v) < 0 || !streq(v, "filesystem"))
+                return log_device_error_errno(d, SYNTHETIC_ERRNO(EINVAL),
+                                              "%s does not contain a known file system.", arg_mount_what);
 
         r = acquire_mount_type(d);
         if (r < 0)
@@ -1454,7 +1434,7 @@ static int list_devices(void) {
 
         r = table_print(table, NULL);
         if (r < 0)
-                return log_error_errno(r, "Failed to print table: %m");
+                return table_log_print_error(r);
 
         return 0;
 }
@@ -1476,12 +1456,12 @@ static int run(int argc, char* argv[]) {
 
         r = bus_connect_transport_systemd(arg_transport, arg_host, arg_user, &bus);
         if (r < 0)
-                return log_error_errno(r, "Failed to create bus connection: %m");
+                return bus_log_connect_error(r);
 
         if (arg_action == ACTION_UMOUNT)
                 return action_umount(bus, argc, argv);
 
-        if ((!arg_mount_type || !fstype_is_network(arg_mount_type))
+        if ((!arg_mount_type || fstype_is_blockdev_backed(arg_mount_type))
             && !path_is_normalized(arg_mount_what)) {
                 log_error("Path contains non-normalized components: %s", arg_mount_what);
                 return -EINVAL;
index c1c02cfda162b06069e88747a13864c06e76a348..b3a88d991035677fbfc9d364b566f10e2edf908d 100644 (file)
@@ -61,6 +61,8 @@ sources = files('''
         networkd-conf.h
         networkd-dhcp-common.c
         networkd-dhcp-common.h
+        networkd-dhcp-server-bus.c
+        networkd-dhcp-server-bus.h
         networkd-dhcp-server.c
         networkd-dhcp-server.h
         networkd-dhcp4.c
@@ -103,26 +105,52 @@ sources = files('''
         networkd-routing-policy-rule.h
         networkd-speed-meter.c
         networkd-speed-meter.h
+        networkd-sriov.c
+        networkd-sriov.h
         networkd-util.c
         networkd-util.h
         networkd-wifi.c
         networkd-wifi.h
+        tc/cake.c
+        tc/cake.h
         tc/codel.c
         tc/codel.h
+        tc/drr.c
+        tc/drr.h
+        tc/ets.c
+        tc/ets.h
+        tc/fifo.c
+        tc/fifo.h
         tc/fq.c
         tc/fq.h
         tc/fq-codel.c
         tc/fq-codel.h
+        tc/gred.c
+        tc/gred.h
+        tc/hhf.c
+        tc/hhf.h
+        tc/htb.c
+        tc/htb.h
         tc/netem.c
         tc/netem.h
+        tc/pie.c
+        tc/pie.h
         tc/qdisc.c
         tc/qdisc.h
+        tc/qfq.c
+        tc/qfq.h
+        tc/sfb.c
+        tc/sfb.h
         tc/sfq.c
         tc/sfq.h
         tc/tbf.c
         tc/tbf.h
         tc/tc-util.c
         tc/tc-util.h
+        tc/tc.c
+        tc/tc.h
+        tc/tclass.c
+        tc/tclass.h
         tc/teql.c
         tc/teql.h
 '''.split())
index 8df39e35843f625af265483a35811bdf014a8bae..b5cf162704433ef0bbff9148a7d3617e1c6688c4 100644 (file)
@@ -2,13 +2,13 @@
 
 #include "alloc-util.h"
 #include "bond.h"
+#include "bond-util.h"
 #include "conf-parser.h"
 #include "ether-addr-util.h"
 #include "extract-word.h"
 #include "netlink-util.h"
 #include "networkd-manager.h"
 #include "string-table.h"
-#include "string-util.h"
 
 /*
  * Number of seconds between instances where the bonding
 #define GRATUITOUS_ARP_MAX        255
 #define GRATUITOUS_ARP_DEFAULT    1
 
-static const char* const bond_mode_table[_NETDEV_BOND_MODE_MAX] = {
-        [NETDEV_BOND_MODE_BALANCE_RR] = "balance-rr",
-        [NETDEV_BOND_MODE_ACTIVE_BACKUP] = "active-backup",
-        [NETDEV_BOND_MODE_BALANCE_XOR] = "balance-xor",
-        [NETDEV_BOND_MODE_BROADCAST] = "broadcast",
-        [NETDEV_BOND_MODE_802_3AD] = "802.3ad",
-        [NETDEV_BOND_MODE_BALANCE_TLB] = "balance-tlb",
-        [NETDEV_BOND_MODE_BALANCE_ALB] = "balance-alb",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(bond_mode, BondMode);
 DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_mode, bond_mode, BondMode, "Failed to parse bond mode");
-
-static const char* const bond_xmit_hash_policy_table[_NETDEV_BOND_XMIT_HASH_POLICY_MAX] = {
-        [NETDEV_BOND_XMIT_HASH_POLICY_LAYER2] = "layer2",
-        [NETDEV_BOND_XMIT_HASH_POLICY_LAYER34] = "layer3+4",
-        [NETDEV_BOND_XMIT_HASH_POLICY_LAYER23] = "layer2+3",
-        [NETDEV_BOND_XMIT_HASH_POLICY_ENCAP23] = "encap2+3",
-        [NETDEV_BOND_XMIT_HASH_POLICY_ENCAP34] = "encap3+4",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(bond_xmit_hash_policy, BondXmitHashPolicy);
 DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_xmit_hash_policy,
                          bond_xmit_hash_policy,
                          BondXmitHashPolicy,
-                         "Failed to parse bond transmit hash policy")
-
-static const char* const bond_lacp_rate_table[_NETDEV_BOND_LACP_RATE_MAX] = {
-        [NETDEV_BOND_LACP_RATE_SLOW] = "slow",
-        [NETDEV_BOND_LACP_RATE_FAST] = "fast",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(bond_lacp_rate, BondLacpRate);
-DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_lacp_rate, bond_lacp_rate, BondLacpRate, "Failed to parse bond lacp rate")
-
-static const char* const bond_ad_select_table[_NETDEV_BOND_AD_SELECT_MAX] = {
-        [NETDEV_BOND_AD_SELECT_STABLE] = "stable",
-        [NETDEV_BOND_AD_SELECT_BANDWIDTH] = "bandwidth",
-        [NETDEV_BOND_AD_SELECT_COUNT] = "count",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(bond_ad_select, BondAdSelect);
+                         "Failed to parse bond transmit hash policy");
+DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_lacp_rate, bond_lacp_rate, BondLacpRate, "Failed to parse bond lacp rate");
 DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_ad_select, bond_ad_select, BondAdSelect, "Failed to parse bond AD select");
-
-static const char* const bond_fail_over_mac_table[_NETDEV_BOND_FAIL_OVER_MAC_MAX] = {
-        [NETDEV_BOND_FAIL_OVER_MAC_NONE] = "none",
-        [NETDEV_BOND_FAIL_OVER_MAC_ACTIVE] = "active",
-        [NETDEV_BOND_FAIL_OVER_MAC_FOLLOW] = "follow",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(bond_fail_over_mac, BondFailOverMac);
 DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_fail_over_mac, bond_fail_over_mac, BondFailOverMac, "Failed to parse bond fail over MAC");
-
-static const char *const bond_arp_validate_table[_NETDEV_BOND_ARP_VALIDATE_MAX] = {
-        [NETDEV_BOND_ARP_VALIDATE_NONE] = "none",
-        [NETDEV_BOND_ARP_VALIDATE_ACTIVE]= "active",
-        [NETDEV_BOND_ARP_VALIDATE_BACKUP]= "backup",
-        [NETDEV_BOND_ARP_VALIDATE_ALL]= "all",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(bond_arp_validate, BondArpValidate);
 DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_arp_validate, bond_arp_validate, BondArpValidate, "Failed to parse bond arp validate");
-
-static const char *const bond_arp_all_targets_table[_NETDEV_BOND_ARP_ALL_TARGETS_MAX] = {
-        [NETDEV_BOND_ARP_ALL_TARGETS_ANY] = "any",
-        [NETDEV_BOND_ARP_ALL_TARGETS_ALL] = "all",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(bond_arp_all_targets, BondArpAllTargets);
 DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_arp_all_targets, bond_arp_all_targets, BondArpAllTargets, "Failed to parse bond Arp all targets");
-
-static const char *const bond_primary_reselect_table[_NETDEV_BOND_PRIMARY_RESELECT_MAX] = {
-        [NETDEV_BOND_PRIMARY_RESELECT_ALWAYS] = "always",
-        [NETDEV_BOND_PRIMARY_RESELECT_BETTER]= "better",
-        [NETDEV_BOND_PRIMARY_RESELECT_FAILURE]= "failure",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(bond_primary_reselect, BondPrimaryReselect);
 DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_primary_reselect, bond_primary_reselect, BondPrimaryReselect, "Failed to parse bond primary reselect");
 
 static int netdev_bond_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) {
@@ -356,7 +288,7 @@ int link_set_bond(Link *link) {
         r = netlink_call_async(link->manager->rtnl, NULL, req, link_set_bond_handler,
                                link_netlink_destroy_callback, link);
         if (r < 0)
-                return log_link_error_errno(link, r,  "Could not send rtnetlink message: %m");
+                return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
 
         link_ref(link);
 
@@ -388,14 +320,16 @@ int config_parse_arp_ip_target_address(
                 return 0;
         }
 
-        for (;;) {
+        for (const char *p = rvalue;;) {
                 _cleanup_free_ char *n = NULL;
                 union in_addr_union ip;
 
-                r = extract_first_word(&rvalue, &n, NULL, 0);
+                r = extract_first_word(&p, &n, NULL, 0);
+                if (r == -ENOMEM)
+                        return log_oom();
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
-                                   "Failed to parse Bond ARP ip target address, ignoring assignment: %s",
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to parse Bond ARP IP target address, ignoring assignment: %s",
                                    rvalue);
                         return 0;
                 }
@@ -404,8 +338,8 @@ int config_parse_arp_ip_target_address(
 
                 r = in_addr_from_string(AF_INET, n, &ip);
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
-                                   "Bond ARP ip target address is invalid, ignoring assignment: %s", n);
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Bond ARP IP target address is invalid, ignoring assignment: %s", n);
                         continue;
                 }
 
@@ -415,7 +349,7 @@ int config_parse_arp_ip_target_address(
 
                 if (ordered_set_size(b->arp_ip_targets) >= NETDEV_BOND_ARP_TARGETS_MAX) {
                         log_syntax(unit, LOG_WARNING, filename, line, 0,
-                                   "Too many ARP ip targets are specified. The maximum number is %d. Ignoring assignment: %s",
+                                   "Too many ARP IP targets are specified. The maximum number is %d. Ignoring assignment: %s",
                                    NETDEV_BOND_ARP_TARGETS_MAX, n);
                         continue;
                 }
@@ -423,10 +357,10 @@ int config_parse_arp_ip_target_address(
                 r = ordered_set_put(b->arp_ip_targets, UINT32_TO_PTR(ip.in.s_addr));
                 if (r == -EEXIST)
                         log_syntax(unit, LOG_WARNING, filename, line, r,
-                                   "Bond ARP ip target address is duplicated, ignoring assignment: %s", n);
+                                   "Bond ARP IP target address is duplicated, ignoring assignment: %s", n);
                 if (r < 0)
-                        log_syntax(unit, LOG_ERR, filename, line, r,
-                                   "Failed to store bond ARP ip target address '%s', ignoring assignment: %m", n);
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to store bond ARP IP target address '%s', ignoring assignment: %m", n);
         }
 }
 
@@ -452,13 +386,13 @@ int config_parse_ad_actor_sys_prio(
 
         r = safe_atou16(rvalue, &v);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to parse actor system priority '%s', ignoring: %m", rvalue);
                 return 0;
         }
 
         if (v == 0) {
-                log_syntax(unit, LOG_ERR, filename, line, 0,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "Failed to parse actor system priority '%s'. Range is [1,65535], ignoring.",
                            rvalue);
                 return 0;
@@ -491,13 +425,13 @@ int config_parse_ad_user_port_key(
 
         r = safe_atou16(rvalue, &v);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to parse user port key '%s', ignoring: %m", rvalue);
                 return 0;
         }
 
         if (v > 1023) {
-                log_syntax(unit, LOG_ERR, filename, line, 0,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "Failed to parse user port key '%s'. Range is [0…1023], ignoring.", rvalue);
                 return 0;
         }
@@ -529,13 +463,13 @@ int config_parse_ad_actor_system(
 
         r = ether_addr_from_string(rvalue, &n);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Not a valid MAC address %s. Ignoring assignment: %m",
                            rvalue);
                 return 0;
         }
         if (ether_addr_is_null(&n) || (n.ether_addr_octet[0] & 0x01)) {
-                log_syntax(unit, LOG_ERR, filename, line, 0,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "Not a valid MAC address %s, can not be null or multicast. Ignoring assignment.",
                            rvalue);
                 return 0;
index 28796a3a8be1d40ff75893c64c4a2b9442830b49..64b2dd04a2bc56e27e381e29022a08c0564ef673 100644 (file)
@@ -4,85 +4,11 @@
 #include <netinet/in.h>
 #include <linux/if_bonding.h>
 
-#include "in-addr-util.h"
+#include "bond-util.h"
+#include "macro.h"
 #include "netdev.h"
 #include "ordered-set.h"
 
-/*
- * Maximum number of targets supported by the kernel for a single
- * bond netdev.
- */
-#define NETDEV_BOND_ARP_TARGETS_MAX 16
-
-typedef enum BondMode {
-        NETDEV_BOND_MODE_BALANCE_RR    = BOND_MODE_ROUNDROBIN,
-        NETDEV_BOND_MODE_ACTIVE_BACKUP = BOND_MODE_ACTIVEBACKUP,
-        NETDEV_BOND_MODE_BALANCE_XOR   = BOND_MODE_XOR,
-        NETDEV_BOND_MODE_BROADCAST     = BOND_MODE_BROADCAST,
-        NETDEV_BOND_MODE_802_3AD       = BOND_MODE_8023AD,
-        NETDEV_BOND_MODE_BALANCE_TLB   = BOND_MODE_TLB,
-        NETDEV_BOND_MODE_BALANCE_ALB   = BOND_MODE_ALB,
-        _NETDEV_BOND_MODE_MAX,
-        _NETDEV_BOND_MODE_INVALID      = -1
-} BondMode;
-
-typedef enum BondXmitHashPolicy {
-        NETDEV_BOND_XMIT_HASH_POLICY_LAYER2   = BOND_XMIT_POLICY_LAYER2,
-        NETDEV_BOND_XMIT_HASH_POLICY_LAYER34  = BOND_XMIT_POLICY_LAYER34,
-        NETDEV_BOND_XMIT_HASH_POLICY_LAYER23  = BOND_XMIT_POLICY_LAYER23,
-        NETDEV_BOND_XMIT_HASH_POLICY_ENCAP23  = BOND_XMIT_POLICY_ENCAP23,
-        NETDEV_BOND_XMIT_HASH_POLICY_ENCAP34  = BOND_XMIT_POLICY_ENCAP34,
-        _NETDEV_BOND_XMIT_HASH_POLICY_MAX,
-        _NETDEV_BOND_XMIT_HASH_POLICY_INVALID = -1
-} BondXmitHashPolicy;
-
-typedef enum BondLacpRate {
-        NETDEV_BOND_LACP_RATE_SLOW,
-        NETDEV_BOND_LACP_RATE_FAST,
-        _NETDEV_BOND_LACP_RATE_MAX,
-        _NETDEV_BOND_LACP_RATE_INVALID = -1,
-} BondLacpRate;
-
-typedef enum BondAdSelect {
-        NETDEV_BOND_AD_SELECT_STABLE,
-        NETDEV_BOND_AD_SELECT_BANDWIDTH,
-        NETDEV_BOND_AD_SELECT_COUNT,
-        _NETDEV_BOND_AD_SELECT_MAX,
-        _NETDEV_BOND_AD_SELECT_INVALID = -1,
-} BondAdSelect;
-
-typedef enum BondFailOverMac {
-        NETDEV_BOND_FAIL_OVER_MAC_NONE,
-        NETDEV_BOND_FAIL_OVER_MAC_ACTIVE,
-        NETDEV_BOND_FAIL_OVER_MAC_FOLLOW,
-        _NETDEV_BOND_FAIL_OVER_MAC_MAX,
-        _NETDEV_BOND_FAIL_OVER_MAC_INVALID = -1,
-} BondFailOverMac;
-
-typedef enum BondArpValidate {
-        NETDEV_BOND_ARP_VALIDATE_NONE,
-        NETDEV_BOND_ARP_VALIDATE_ACTIVE,
-        NETDEV_BOND_ARP_VALIDATE_BACKUP,
-        NETDEV_BOND_ARP_VALIDATE_ALL,
-        _NETDEV_BOND_ARP_VALIDATE_MAX,
-        _NETDEV_BOND_ARP_VALIDATE_INVALID = -1,
-} BondArpValidate;
-
-typedef enum BondArpAllTargets {
-        NETDEV_BOND_ARP_ALL_TARGETS_ANY,
-        NETDEV_BOND_ARP_ALL_TARGETS_ALL,
-        _NETDEV_BOND_ARP_ALL_TARGETS_MAX,
-        _NETDEV_BOND_ARP_ALL_TARGETS_INVALID = -1,
-} BondArpAllTargets;
-
-typedef enum BondPrimaryReselect {
-        NETDEV_BOND_PRIMARY_RESELECT_ALWAYS,
-        NETDEV_BOND_PRIMARY_RESELECT_BETTER,
-        NETDEV_BOND_PRIMARY_RESELECT_FAILURE,
-        _NETDEV_BOND_PRIMARY_RESELECT_MAX,
-        _NETDEV_BOND_PRIMARY_RESELECT_INVALID = -1,
-} BondPrimaryReselect;
-
 typedef struct Bond {
         NetDev meta;
 
@@ -122,30 +48,6 @@ extern const NetDevVTable bond_vtable;
 
 int link_set_bond(Link *link);
 
-const char *bond_mode_to_string(BondMode d) _const_;
-BondMode bond_mode_from_string(const char *d) _pure_;
-
-const char *bond_xmit_hash_policy_to_string(BondXmitHashPolicy d) _const_;
-BondXmitHashPolicy bond_xmit_hash_policy_from_string(const char *d) _pure_;
-
-const char *bond_lacp_rate_to_string(BondLacpRate d) _const_;
-BondLacpRate bond_lacp_rate_from_string(const char *d) _pure_;
-
-const char *bond_fail_over_mac_to_string(BondFailOverMac d) _const_;
-BondFailOverMac bond_fail_over_mac_from_string(const char *d) _pure_;
-
-const char *bond_ad_select_to_string(BondAdSelect d) _const_;
-BondAdSelect bond_ad_select_from_string(const char *d) _pure_;
-
-const char *bond_arp_validate_to_string(BondArpValidate d) _const_;
-BondArpValidate bond_arp_validate_from_string(const char *d) _pure_;
-
-const char *bond_arp_all_targets_to_string(BondArpAllTargets d) _const_;
-BondArpAllTargets bond_arp_all_targets_from_string(const char *d) _pure_;
-
-const char *bond_primary_reselect_to_string(BondPrimaryReselect d) _const_;
-BondPrimaryReselect bond_primary_reselect_from_string(const char *d) _pure_;
-
 CONFIG_PARSER_PROTOTYPE(config_parse_bond_mode);
 CONFIG_PARSER_PROTOTYPE(config_parse_bond_xmit_hash_policy);
 CONFIG_PARSER_PROTOTYPE(config_parse_bond_lacp_rate);
index 6b8f9944612ea39765f4a3ead0ceee9813d6099f..70240661e87b6e302895e8ad1610a2d142ae2602 100644 (file)
@@ -126,6 +126,12 @@ static int netdev_bridge_post_create(NetDev *netdev, Link *link, sd_netlink_mess
                         return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_VLAN_FILTERING attribute: %m");
         }
 
+        if (b->vlan_protocol >= 0) {
+                r = sd_netlink_message_append_u16(req, IFLA_BR_VLAN_PROTOCOL, b->vlan_protocol);
+                if (r < 0)
+                        return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_VLAN_PROTOCOL attribute: %m");
+        }
+
         if (b->stp >= 0) {
                 r = sd_netlink_message_append_u32(req, IFLA_BR_STP_STATE, b->stp);
                 if (r < 0)
@@ -212,7 +218,7 @@ int link_set_bridge(Link *link) {
                         return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_FAST_LEAVE attribute: %m");
         }
 
-        if (link->network->allow_port_to_be_root >=  0) {
+        if (link->network->allow_port_to_be_root >= 0) {
                 r = sd_netlink_message_append_u8(req, IFLA_BRPORT_PROTECT, link->network->allow_port_to_be_root);
                 if (r < 0)
                         return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_PROTECT attribute: %m");
@@ -320,13 +326,13 @@ int config_parse_bridge_igmp_version(
 
         r = safe_atou8(rvalue, &u);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to parse bridge's multicast IGMP version number '%s', ignoring assignment: %m",
                            rvalue);
                 return 0;
         }
         if (!IN_SET(u, 2, 3)) {
-                log_syntax(unit, LOG_ERR, filename, line, 0,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "Invalid bridge's multicast IGMP version number '%s', ignoring assignment.", rvalue);
                 return 0;
         }
@@ -346,6 +352,7 @@ static void bridge_init(NetDev *n) {
         b->mcast_querier = -1;
         b->mcast_snooping = -1;
         b->vlan_filtering = -1;
+        b->vlan_protocol = -1;
         b->stp = -1;
         b->default_pvid = VLANID_INVALID;
         b->forward_delay = USEC_INFINITY;
index b0a728e5a40df76fab3c2e94c87e1fbd6ced4ac1..ed4f484c946457966dc010fc862e1543bfb18e84 100644 (file)
@@ -13,6 +13,7 @@ typedef struct Bridge {
         int mcast_querier;
         int mcast_snooping;
         int vlan_filtering;
+        int vlan_protocol;
         int stp;
         uint16_t priority;
         uint16_t group_fwd_mask;
index bc0dc185d3a444121995d4629d2c9b680e1a3754..832cf57deabb741f9a0725379085691409816562 100644 (file)
@@ -167,14 +167,14 @@ int config_parse_ip_protocol(
         else {
                 r = safe_atou(rvalue, &protocol);
                 if (r < 0)
-                        log_syntax(unit, LOG_ERR, filename, line, r,
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
                                    "Failed to parse IP protocol '%s' for FooOverUDP tunnel, "
                                    "ignoring assignment: %m", rvalue);
                 return 0;
         }
 
         if (protocol > UINT8_MAX) {
-                log_syntax(unit, LOG_ERR, filename, line, 0,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "IP protocol '%s' for FooOverUDP tunnel out of range, "
                            "ignoring assignment: %m", rvalue);
                 return 0;
@@ -212,7 +212,7 @@ int config_parse_fou_tunnel_address(
 
         r = in_addr_from_string_auto(rvalue, f, addr);
         if (r < 0)
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "FooOverUDP tunnel '%s' address is invalid, ignoring assignment: %s",
                            lvalue, rvalue);
 
index b960840a543ea10f1eccec902c9b4906a2d0e2a3..b4f865a538a22a2e3b83e92e35bb2c44512e8dec 100644 (file)
@@ -185,12 +185,12 @@ int config_parse_geneve_vni(const char *unit,
 
         r = safe_atou32(rvalue, &f);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse Geneve VNI '%s'.", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse Geneve VNI '%s'.", rvalue);
                 return 0;
         }
 
         if (f > GENEVE_VID_MAX){
-                log_syntax(unit, LOG_ERR, filename, line, r, "Geneve VNI out is of range '%s'.", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, 0, "Geneve VNI out is of range '%s'.", rvalue);
                 return 0;
         }
 
@@ -220,13 +220,13 @@ int config_parse_geneve_address(const char *unit,
 
         r = in_addr_from_string_auto(rvalue, &f, &buffer);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "geneve '%s' address is invalid, ignoring assignment: %s", lvalue, rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, r, "geneve '%s' address is invalid, ignoring assignment: %s", lvalue, rvalue);
                 return 0;
         }
 
         r = in_addr_is_multicast(f, &buffer);
         if (r > 0) {
-                log_syntax(unit, LOG_ERR, filename, line, 0, "geneve invalid multicast '%s' address, ignoring assignment: %s", lvalue, rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, 0, "geneve invalid multicast '%s' address, ignoring assignment: %s", lvalue, rvalue);
                 return 0;
         }
 
@@ -257,12 +257,12 @@ int config_parse_geneve_flow_label(const char *unit,
 
         r = safe_atou32(rvalue, &f);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse Geneve flow label '%s'.", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse Geneve flow label '%s'.", rvalue);
                 return 0;
         }
 
         if (f & ~GENEVE_FLOW_LABEL_MAX_MASK) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "Geneve flow label '%s' not valid. Flow label range should be [0-1048575].", rvalue);
                 return 0;
         }
@@ -296,13 +296,13 @@ int config_parse_geneve_ttl(const char *unit,
         else {
                 r = safe_atou(rvalue, &f);
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
                                    "Failed to parse Geneve TTL '%s', ignoring assignment: %m", rvalue);
                         return 0;
                 }
 
                 if (f > 255) {
-                        log_syntax(unit, LOG_ERR, filename, line, 0,
+                        log_syntax(unit, LOG_WARNING, filename, line, 0,
                                    "Invalid Geneve TTL '%s'. TTL must be <= 255. Ignoring assignment.", rvalue);
                         return 0;
                 }
index 1d87cfa865a13f853a35e491e4beea3ad87e7ea1..bd14f625deaa0a95842f75abd203c2a32dcd1d81 100644 (file)
@@ -4,25 +4,11 @@
 
 #include "conf-parser.h"
 #include "ipvlan.h"
+#include "ipvlan-util.h"
 #include "networkd-link.h"
-#include "string-table.h"
+#include "string-util.h"
 
-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) {
index 171407b7c4bfb17c3cd526c3ec2aed7bde5e8e26..8e658184ffce2c7fc3e7a06fadac38de32032687 100644 (file)
@@ -4,24 +4,9 @@
 #include <netinet/in.h>
 #include <linux/if_link.h>
 
+#include "ipvlan-util.h"
 #include "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;
 
@@ -34,12 +19,6 @@ DEFINE_NETDEV_CAST(IPVTAP, IPVlan);
 extern const NetDevVTable ipvlan_vtable;
 extern const NetDevVTable ipvtap_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_;
-
 CONFIG_PARSER_PROTOTYPE(config_parse_ipvlan_mode);
 CONFIG_PARSER_PROTOTYPE(config_parse_ipvlan_flags);
 
index 91788c3681938fc28eb296cdeb9313203310c322..0d670cb2a5d0754fbd293e55c8ca447d937f1b8d 100644 (file)
@@ -459,7 +459,7 @@ int config_parse_l2tp_tunnel_address(
         else
                 r = in_addr_from_string(t->family, rvalue, addr);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Invalid L2TP Tunnel address specified in %s='%s', ignoring assignment: %m", lvalue, rvalue);
                 return 0;
         }
@@ -489,13 +489,13 @@ int config_parse_l2tp_tunnel_id(
 
         r = safe_atou32(rvalue, &k);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to parse L2TP tunnel id. Ignoring assignment: %s", rvalue);
                 return 0;
         }
 
         if (k == 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "Invalid L2TP tunnel id. Ignoring assignment: %s", rvalue);
                 return 0;
         }
@@ -530,17 +530,17 @@ int config_parse_l2tp_session_id(
 
         r = l2tp_session_new_static(t, filename, section_line, &session);
         if (r < 0)
-                return r;
+                return log_oom();
 
         r = safe_atou32(rvalue, &k);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to parse L2TP session id. Ignoring assignment: %s", rvalue);
                 return 0;
         }
 
         if (k == 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "Invalid L2TP session id. Ignoring assignment: %s", rvalue);
                 return 0;
         }
@@ -579,11 +579,11 @@ int config_parse_l2tp_session_l2spec(
 
         r = l2tp_session_new_static(t, filename, section_line, &session);
         if (r < 0)
-                return r;
+                return log_oom();
 
         spec = l2tp_l2spec_type_from_string(rvalue);
         if (spec < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, 0,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "Failed to parse layer2 specific header type. Ignoring assignment: %s", rvalue);
                 return 0;
         }
@@ -618,10 +618,10 @@ int config_parse_l2tp_session_name(
 
         r = l2tp_session_new_static(t, filename, section_line, &session);
         if (r < 0)
-                return r;
+                return log_oom();
 
         if (!ifname_valid(rvalue)) {
-                log_syntax(unit, LOG_ERR, filename, line, 0,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "Failed to parse L2TP tunnel session name. Ignoring assignment: %s", rvalue);
                 return 0;
         }
index 8f7559e9ae7a8ad21ba23f9d4fac997171d86e99..2ffa5ec8c695a32c5ee2072d48389f3f7d95460c 100644 (file)
@@ -577,7 +577,7 @@ int config_parse_macsec_port(
         else if (streq(section, "MACsecReceiveChannel")) {
                 r = macsec_receive_channel_new_static(s, filename, section_line, &c);
                 if (r < 0)
-                        return r;
+                        return log_oom();
 
                 dest = &c->sci.port;
         } else {
@@ -585,14 +585,14 @@ int config_parse_macsec_port(
 
                 r = macsec_receive_association_new_static(s, filename, section_line, &b);
                 if (r < 0)
-                        return r;
+                        return log_oom();
 
                 dest = &b->sci.port;
         }
 
         r = parse_ip_port(rvalue, &port);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to parse port '%s' for secure channel identifier. Ignoring assignment: %m",
                            rvalue);
                 return 0;
@@ -634,11 +634,11 @@ int config_parse_macsec_hw_address(
         else
                 r = macsec_receive_association_new_static(s, filename, section_line, &b);
         if (r < 0)
-                return r;
+                return log_oom();
 
         r = ether_addr_from_string(rvalue, b ? &b->sci.mac : &c->sci.mac);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to parse MAC address for secure channel identifier. "
                            "Ignoring assignment: %s", rvalue);
                 return 0;
@@ -679,18 +679,18 @@ int config_parse_macsec_packet_number(
         else
                 r = macsec_receive_association_new_static(s, filename, section_line, &b);
         if (r < 0)
-                return r;
+                return log_oom();
 
         dest = a ? &a->sa.packet_number : &b->sa.packet_number;
 
         r = safe_atou32(rvalue, &val);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to parse packet number. Ignoring assignment: %s", rvalue);
                 return 0;
         }
         if (streq(section, "MACsecTransmitAssociation") && val == 0) {
-                log_syntax(unit, LOG_ERR, filename, line, 0,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "Invalid packet number. Ignoring assignment: %s", rvalue);
                 return 0;
         }
@@ -735,19 +735,19 @@ int config_parse_macsec_key(
         else
                 r = macsec_receive_association_new_static(s, filename, section_line, &b);
         if (r < 0)
-                return r;
+                return log_oom();
 
         dest = a ? &a->sa : &b->sa;
 
         r = unhexmem_full(rvalue, strlen(rvalue), true, &p, &l);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse key. Ignoring assignment: %m");
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse key. Ignoring assignment: %m");
                 return 0;
         }
 
         if (l != 16) {
                 /* See DEFAULT_SAK_LEN in drivers/net/macsec.c */
-                log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid key length (%zu). Ignoring assignment", l);
+                log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid key length (%zu). Ignoring assignment", l);
                 return 0;
         }
 
@@ -791,7 +791,7 @@ int config_parse_macsec_key_file(
         else
                 r = macsec_receive_association_new_static(s, filename, section_line, &b);
         if (r < 0)
-                return r;
+                return log_oom();
 
         dest = a ? &a->sa.key_file : &b->sa.key_file;
 
@@ -845,15 +845,15 @@ int config_parse_macsec_key_id(
         else
                 r = macsec_receive_association_new_static(s, filename, section_line, &b);
         if (r < 0)
-                return r;
+                return log_oom();
 
         r = unhexmem(rvalue, strlen(rvalue), &p, &l);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse KeyId \"%s\": %m", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse KeyId \"%s\": %m", rvalue);
                 return 0;
         }
         if (l > MACSEC_KEYID_LEN)
-                return log_syntax(unit, LOG_ERR, filename, line, 0,
+                return log_syntax(unit, LOG_WARNING, filename, line, 0,
                                   "Specified KeyId is larger then the allowed maximum (%zu > %u), ignoring: %s",
                                   l, MACSEC_KEYID_LEN, rvalue);
 
@@ -896,7 +896,7 @@ int config_parse_macsec_sa_activate(
         else
                 r = macsec_receive_association_new_static(s, filename, section_line, &b);
         if (r < 0)
-                return r;
+                return log_oom();
 
         dest = a ? &a->sa.activate : &b->sa.activate;
 
@@ -905,7 +905,7 @@ int config_parse_macsec_sa_activate(
         else {
                 r = parse_boolean(rvalue);
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
                                    "Failed to parse activation mode of %s security association. "
                                    "Ignoring assignment: %s",
                                    streq(section, "MACsecTransmitAssociation") ? "transmit" : "receive",
@@ -945,18 +945,20 @@ int config_parse_macsec_use_for_encoding(
 
         r = macsec_transmit_association_new_static(s, filename, section_line, &a);
         if (r < 0)
-                return r;
+                return log_oom();
 
-        if (isempty(rvalue))
-                r = -1;
-        else {
-                r = parse_boolean(rvalue);
-                if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
-                                   "Failed to parse %s= setting. Ignoring assignment: %s",
-                                   lvalue, rvalue);
-                        return 0;
-                }
+        if (isempty(rvalue)) {
+                a->sa.use_for_encoding = -1;
+                TAKE_PTR(a);
+                return 0;
+        }
+
+        r = parse_boolean(rvalue);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse %s= setting. Ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
         }
 
         a->sa.use_for_encoding = r;
@@ -981,7 +983,10 @@ static int macsec_read_key_file(NetDev *netdev, SecurityAssociation *sa) {
 
         (void) warn_file_is_world_accessible(sa->key_file, NULL, NULL, 0);
 
-        r = read_full_file_full(AT_FDCWD, sa->key_file, READ_FULL_FILE_SECURE | READ_FULL_FILE_UNHEX, (char **) &key, &key_len);
+        r = read_full_file_full(
+                        AT_FDCWD, sa->key_file,
+                        READ_FULL_FILE_SECURE | READ_FULL_FILE_UNHEX | READ_FULL_FILE_WARN_WORLD_READABLE | READ_FULL_FILE_CONNECT_SOCKET,
+                        (char **) &key, &key_len);
         if (r < 0)
                 return log_netdev_error_errno(netdev, r,
                                               "Failed to read key from '%s', ignoring: %m",
index dbe25e9e34fb103e9cee6e060004e5335171d5d3..41391788b083f53bc3a81a168017d739e9fa9b17 100644 (file)
@@ -4,16 +4,8 @@
 
 #include "conf-parser.h"
 #include "macvlan.h"
-#include "string-table.h"
+#include "macvlan-util.h"
 
-static const char* const macvlan_mode_table[_NETDEV_MACVLAN_MODE_MAX] = {
-        [NETDEV_MACVLAN_MODE_PRIVATE] = "private",
-        [NETDEV_MACVLAN_MODE_VEPA] = "vepa",
-        [NETDEV_MACVLAN_MODE_BRIDGE] = "bridge",
-        [NETDEV_MACVLAN_MODE_PASSTHRU] = "passthru",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(macvlan_mode, MacVlanMode);
 DEFINE_CONFIG_PARSE_ENUM(config_parse_macvlan_mode, macvlan_mode, MacVlanMode, "Failed to parse macvlan mode");
 
 static int netdev_macvlan_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *req) {
@@ -31,6 +23,29 @@ static int netdev_macvlan_fill_message_create(NetDev *netdev, Link *link, sd_net
 
         assert(m);
 
+        if (m->mode == NETDEV_MACVLAN_MODE_SOURCE && !set_isempty(m->match_source_mac)) {
+                Iterator i;
+                const struct ether_addr *mac_addr;
+
+                r = sd_netlink_message_append_u32(req, IFLA_MACVLAN_MACADDR_MODE, MACVLAN_MACADDR_SET);
+                if (r < 0)
+                        return log_netdev_error_errno(netdev, r, "Could not append IFLA_MACVLAN_MACADDR_MODE attribute: %m");
+
+                r = sd_netlink_message_open_container(req, IFLA_MACVLAN_MACADDR_DATA);
+                if (r < 0)
+                        return log_netdev_error_errno(netdev, r, "Could not open IFLA_MACVLAN_MACADDR_DATA container: %m");
+
+                SET_FOREACH(mac_addr, m->match_source_mac, i) {
+                        r = sd_netlink_message_append_ether_addr(req, IFLA_MACVLAN_MACADDR, mac_addr);
+                        if (r < 0)
+                                return log_netdev_error_errno(netdev, r, "Could not append IFLA_MACVLAN_MACADDR attribute: %m");
+                }
+
+                r = sd_netlink_message_close_container(req);
+                if (r < 0)
+                        return log_netdev_error_errno(netdev, r, "Could not close IFLA_MACVLAN_MACADDR_DATA container: %m");
+        }
+
         if (m->mode != _NETDEV_MACVLAN_MODE_INVALID) {
                 r = sd_netlink_message_append_u32(req, IFLA_MACVLAN_MODE, m->mode);
                 if (r < 0)
@@ -40,6 +55,21 @@ static int netdev_macvlan_fill_message_create(NetDev *netdev, Link *link, sd_net
         return 0;
 }
 
+static void macvlan_done(NetDev *n) {
+        MacVlan *m;
+
+        assert(n);
+
+        if (n->kind == NETDEV_KIND_MACVLAN)
+                m = MACVLAN(n);
+        else
+                m = MACVTAP(n);
+
+        assert(m);
+
+        set_free_free(m->match_source_mac);
+}
+
 static void macvlan_init(NetDev *n) {
         MacVlan *m;
 
@@ -58,6 +88,7 @@ static void macvlan_init(NetDev *n) {
 const NetDevVTable macvtap_vtable = {
         .object_size = sizeof(MacVlan),
         .init = macvlan_init,
+        .done = macvlan_done,
         .sections = NETDEV_COMMON_SECTIONS "MACVTAP\0",
         .fill_message_create = netdev_macvlan_fill_message_create,
         .create_type = NETDEV_CREATE_STACKED,
@@ -67,6 +98,7 @@ const NetDevVTable macvtap_vtable = {
 const NetDevVTable macvlan_vtable = {
         .object_size = sizeof(MacVlan),
         .init = macvlan_init,
+        .done = macvlan_done,
         .sections = NETDEV_COMMON_SECTIONS "MACVLAN\0",
         .fill_message_create = netdev_macvlan_fill_message_create,
         .create_type = NETDEV_CREATE_STACKED,
index 5d81be32da291e038bc556c7a7912eb09cc04d93..7bc6eef12df2b3a4e14bdf0a15eb1bf5ab33b61e 100644 (file)
@@ -3,21 +3,15 @@
 
 typedef struct MacVlan MacVlan;
 
+#include "macvlan-util.h"
 #include "netdev.h"
-
-typedef enum MacVlanMode {
-        NETDEV_MACVLAN_MODE_PRIVATE = MACVLAN_MODE_PRIVATE,
-        NETDEV_MACVLAN_MODE_VEPA = MACVLAN_MODE_VEPA,
-        NETDEV_MACVLAN_MODE_BRIDGE = MACVLAN_MODE_BRIDGE,
-        NETDEV_MACVLAN_MODE_PASSTHRU = MACVLAN_MODE_PASSTHRU,
-        _NETDEV_MACVLAN_MODE_MAX,
-        _NETDEV_MACVLAN_MODE_INVALID = -1
-} MacVlanMode;
+#include "set.h"
 
 struct MacVlan {
         NetDev meta;
 
         MacVlanMode mode;
+        Set *match_source_mac;
 };
 
 DEFINE_NETDEV_CAST(MACVLAN, MacVlan);
@@ -25,7 +19,4 @@ DEFINE_NETDEV_CAST(MACVTAP, MacVlan);
 extern const NetDevVTable macvlan_vtable;
 extern const NetDevVTable macvtap_vtable;
 
-const char *macvlan_mode_to_string(MacVlanMode d) _const_;
-MacVlanMode macvlan_mode_from_string(const char *d) _pure_;
-
 CONFIG_PARSER_PROTOTYPE(config_parse_macvlan_mode);
index 09a5f4822e03dc39b32e7fe8072ecaf89bc2cc04..0e2a9ce045e3dd7db947c31c8b54b0890d3ae452 100644 (file)
@@ -52,7 +52,9 @@ VLAN.MVRP,                                config_parse_tristate,
 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)
+MACVLAN.SourceMACAddress,                 config_parse_hwaddrs,                      0,                             offsetof(MacVlan, match_source_mac)
 MACVTAP.Mode,                             config_parse_macvlan_mode,                 0,                             offsetof(MacVlan, mode)
+MACVTAP.SourceMACAddress,                 config_parse_hwaddrs,                      0,                             offsetof(MacVlan, match_source_mac)
 IPVLAN.Mode,                              config_parse_ipvlan_mode,                  0,                             offsetof(IPVlan, mode)
 IPVLAN.Flags,                             config_parse_ipvlan_flags,                 0,                             offsetof(IPVlan, flags)
 IPVTAP.Mode,                              config_parse_ipvlan_mode,                  0,                             offsetof(IPVlan, mode)
@@ -206,6 +208,7 @@ Bridge.DefaultPVID,                       config_parse_default_port_vlanid,
 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.VLANProtocol,                      config_parse_vlanprotocol,                 0,                             offsetof(Bridge, vlan_protocol)
 Bridge.STP,                               config_parse_tristate,                     0,                             offsetof(Bridge, stp)
 Bridge.MulticastIGMPVersion,              config_parse_uint8,                        0,                             offsetof(Bridge, igmp_version)
 VRF.TableId,                              config_parse_uint32,                       0,                             offsetof(Vrf, table) /* deprecated */
index 109122c326c9f726ad95d8a30c66a2318fb79759..446a580e2cc91af4502d9b23da6e4625146d2d15 100644 (file)
@@ -2,6 +2,7 @@
 
 #include <net/if.h>
 #include <netinet/in.h>
+#include <unistd.h>
 
 #include "alloc-util.h"
 #include "bond.h"
@@ -24,6 +25,7 @@
 #include "network-internal.h"
 #include "networkd-manager.h"
 #include "nlmon.h"
+#include "path-lookup.h"
 #include "siphash24.h"
 #include "stat-util.h"
 #include "string-table.h"
@@ -135,12 +137,12 @@ int config_parse_netdev_kind(
 
         k = netdev_kind_from_string(rvalue);
         if (k < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse netdev kind, ignoring assignment: %s", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse netdev kind, ignoring assignment: %s", rvalue);
                 return 0;
         }
 
         if (*kind != _NETDEV_KIND_INVALID && *kind != k) {
-                log_syntax(unit, LOG_ERR, filename, line, 0,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "Specified netdev kind is different from the previous value '%s', ignoring assignment: %s",
                            netdev_kind_to_string(*kind), rvalue);
                 return 0;
@@ -362,7 +364,7 @@ static int netdev_enslave(NetDev *netdev, Link *link, link_netlink_message_handl
                 if (r >= 0)
                         callback(netdev->manager->rtnl, m, link);
         } else {
-                /* the netdev is not yet read, save this request for when it is */
+                /* the netdev is not yet ready, save this request for when it is */
                 netdev_join_callback *cb;
 
                 cb = new(netdev_join_callback, 1);
@@ -684,15 +686,18 @@ int netdev_load_one(Manager *manager, const char *filename) {
         };
 
         dropin_dirname = strjoina(basename(filename), ".d");
-        r = config_parse_many(filename, NETWORK_DIRS, dropin_dirname,
-                              NETDEV_COMMON_SECTIONS NETDEV_OTHER_SECTIONS,
-                              config_item_perf_lookup, network_netdev_gperf_lookup,
-                              CONFIG_PARSE_WARN, netdev_raw, NULL);
+        r = config_parse_many(
+                        filename, NETWORK_DIRS, dropin_dirname,
+                        NETDEV_COMMON_SECTIONS NETDEV_OTHER_SECTIONS,
+                        config_item_perf_lookup, network_netdev_gperf_lookup,
+                        CONFIG_PARSE_WARN,
+                        netdev_raw,
+                        NULL);
         if (r < 0)
                 return r;
 
         /* skip out early if configuration does not match the environment */
-        if (!condition_test_list(netdev_raw->conditions, NULL, NULL, NULL)) {
+        if (!condition_test_list(netdev_raw->conditions, environ, NULL, NULL, NULL)) {
                 log_debug("%s: Conditions in the file do not match the system environment, skipping.", filename);
                 return 0;
         }
@@ -724,10 +729,12 @@ int netdev_load_one(Manager *manager, const char *filename) {
         if (NETDEV_VTABLE(netdev)->init)
                 NETDEV_VTABLE(netdev)->init(netdev);
 
-        r = config_parse_many(filename, NETWORK_DIRS, dropin_dirname,
-                              NETDEV_VTABLE(netdev)->sections,
-                              config_item_perf_lookup, network_netdev_gperf_lookup,
-                              CONFIG_PARSE_WARN, netdev, NULL);
+        r = config_parse_many(
+                        filename, NETWORK_DIRS, dropin_dirname,
+                        NETDEV_VTABLE(netdev)->sections,
+                        config_item_perf_lookup, network_netdev_gperf_lookup,
+                        CONFIG_PARSE_WARN,
+                        netdev, NULL);
         if (r < 0)
                 return r;
 
index 72b33ed9e59302f1a7b92e9d845097d410644042..852aa423b61fbe4283a2b2617b436b538aae11ad 100644 (file)
@@ -541,13 +541,13 @@ int config_parse_tunnel_address(const char *unit,
 
         r = in_addr_from_string_auto(rvalue, &f, &buffer);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Tunnel address \"%s\" invalid, ignoring assignment: %m", rvalue);
                 return 0;
         }
 
         if (t->family != AF_UNSPEC && t->family != f) {
-                log_syntax(unit, LOG_ERR, filename, line, 0,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "Tunnel addresses incompatible, ignoring assignment: %s", rvalue);
                 return 0;
         }
@@ -581,7 +581,7 @@ int config_parse_tunnel_key(const char *unit,
         if (r < 0) {
                 r = safe_atou32(rvalue, &k);
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse tunnel key ignoring assignment: %s", rvalue);
+                        log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse tunnel key ignoring assignment: %s", rvalue);
                         return 0;
                 }
         } else
@@ -626,7 +626,7 @@ int config_parse_ipv6_flowlabel(const char* unit,
                         return r;
 
                 if (k > 0xFFFFF)
-                        log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IPv6 flowlabel option, ignoring: %s", rvalue);
+                        log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse IPv6 flowlabel option, ignoring: %s", rvalue);
                 else {
                         *ipv6_flowlabel = htobe32(k) & IP6_FLOWINFO_FLOWLABEL;
                         t->flags &= ~IP6_TNL_F_USE_ORIG_FLOWLABEL;
@@ -659,12 +659,12 @@ int config_parse_encap_limit(const char* unit,
         else {
                 r = safe_atoi(rvalue, &k);
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse Tunnel Encapsulation Limit option, ignoring: %s", rvalue);
+                        log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse Tunnel Encapsulation Limit option, ignoring: %s", rvalue);
                         return 0;
                 }
 
                 if (k > 255 || k < 0)
-                        log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid Tunnel Encapsulation value, ignoring: %d", k);
+                        log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid Tunnel Encapsulation value, ignoring: %d", k);
                 else {
                         t->encap_limit = k;
                         t->flags &= ~IP6_TNL_F_IGN_ENCAP_LIMIT;
@@ -696,11 +696,11 @@ int config_parse_6rd_prefix(const char* unit,
 
         r = in_addr_prefix_from_string(rvalue, AF_INET6, &p, &l);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse 6rd prefix \"%s\", ignoring: %m", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse 6rd prefix \"%s\", ignoring: %m", rvalue);
                 return 0;
         }
         if (l == 0) {
-                log_syntax(unit, LOG_ERR, filename, line, 0, "6rd prefix length of \"%s\" must be greater than zero, ignoring", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, 0, "6rd prefix length of \"%s\" must be greater than zero, ignoring", rvalue);
                 return 0;
         }
 
index ace3c5d2ed390216135c8d5652b789f0a9043939..373ff789aaeae0885bbb739fc22313661b73a06a 100644 (file)
@@ -38,7 +38,14 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netli
                         return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_ID attribute: %m");
         }
 
-        if (in_addr_is_null(v->remote_family, &v->remote) == 0) {
+        if (in_addr_is_null(v->group_family, &v->group) == 0) {
+                if (v->group_family == AF_INET)
+                        r = sd_netlink_message_append_in_addr(m, IFLA_VXLAN_GROUP, &v->group.in);
+                else
+                        r = sd_netlink_message_append_in6_addr(m, IFLA_VXLAN_GROUP6, &v->group.in6);
+                if (r < 0)
+                        return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_GROUP attribute: %m");
+        } else if (in_addr_is_null(v->remote_family, &v->remote) == 0) {
                 if (v->remote_family == AF_INET)
                         r = sd_netlink_message_append_in_addr(m, IFLA_VXLAN_GROUP, &v->remote.in);
                 else
@@ -189,7 +196,7 @@ int config_parse_vxlan_address(const char *unit,
 
         r = in_addr_from_string_auto(rvalue, &f, &buffer);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "vxlan '%s' address is invalid, ignoring assignment: %s", lvalue, rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, r, "vxlan '%s' address is invalid, ignoring assignment: %s", lvalue, rvalue);
                 return 0;
         }
 
@@ -197,14 +204,14 @@ int config_parse_vxlan_address(const char *unit,
 
         if (streq(lvalue, "Group")) {
                 if (r <= 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, 0, "vxlan %s invalid multicast address, ignoring assignment: %s", lvalue, rvalue);
+                        log_syntax(unit, LOG_WARNING, filename, line, 0, "vxlan %s invalid multicast address, ignoring assignment: %s", lvalue, rvalue);
                         return 0;
                 }
 
                 v->group_family = f;
         } else {
                 if (r > 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, 0, "vxlan %s cannot be a multicast address, ignoring assignment: %s", lvalue, rvalue);
+                        log_syntax(unit, LOG_WARNING, filename, line, 0, "vxlan %s cannot be a multicast address, ignoring assignment: %s", lvalue, rvalue);
                         return 0;
                 }
 
@@ -240,7 +247,7 @@ int config_parse_port_range(const char *unit,
 
         r = parse_ip_port_range(rvalue, &low, &high);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to parse VXLAN port range '%s'. Port should be greater than 0 and less than 65535.", rvalue);
                 return 0;
         }
@@ -272,12 +279,12 @@ int config_parse_flow_label(const char *unit,
 
         r = safe_atou(rvalue, &f);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse VXLAN flow label '%s'.", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse VXLAN flow label '%s'.", rvalue);
                 return 0;
         }
 
         if (f & ~VXLAN_FLOW_LABEL_MAX_MASK) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "VXLAN flow label '%s' not valid. Flow label range should be [0-1048575].", rvalue);
                 return 0;
         }
@@ -311,13 +318,13 @@ int config_parse_vxlan_ttl(const char *unit,
         else {
                 r = safe_atou(rvalue, &f);
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
                                    "Failed to parse VXLAN TTL '%s', ignoring assignment: %m", rvalue);
                         return 0;
                 }
 
                 if (f > 255) {
-                        log_syntax(unit, LOG_ERR, filename, line, 0,
+                        log_syntax(unit, LOG_WARNING, filename, line, 0,
                                    "Invalid VXLAN TTL '%s'. TTL must be <= 255. Ignoring assignment.", rvalue);
                         return 0;
                 }
@@ -348,6 +355,11 @@ static int netdev_vxlan_verify(NetDev *netdev, const char *filename) {
         if (!v->dest_port && v->generic_protocol_extension)
                 v->dest_port = 4790;
 
+        if (in_addr_is_null(v->group_family, &v->group) == 0 && in_addr_is_null(v->remote_family, &v->remote) == 0)
+                return log_netdev_warning_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
+                                                "%s: VXLAN both 'Group=' and 'Remote=' cannot be specified. Ignoring.",
+                                                filename);
+
         return 0;
 }
 
index 713cdaa8847ad8eec72e40db81479c572fed311b..6812b07bff5e4b3d19162f8a3ff40c84f6be1676 100644 (file)
@@ -348,14 +348,7 @@ static int wireguard_resolve_handler(sd_resolve_query *q,
         if (ret != 0) {
                 log_netdev_error(netdev, "Failed to resolve host '%s:%s': %s", peer->endpoint_host, peer->endpoint_port, gai_strerror(ret));
 
-                r = set_ensure_allocated(&w->peers_with_failed_endpoint, NULL);
-                if (r < 0) {
-                        log_oom();
-                        peer->section->invalid = true;
-                        goto resolve_next;
-                }
-
-                r = set_put(w->peers_with_failed_endpoint, peer);
+                r = set_ensure_put(&w->peers_with_failed_endpoint, NULL, peer);
                 if (r < 0) {
                         log_netdev_error(netdev, "Failed to save a peer, dropping the peer: %m");
                         peer->section->invalid = true;
@@ -466,7 +459,7 @@ int config_parse_wireguard_listen_port(
 
         r = parse_ip_port(rvalue, s);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Invalid port specification, ignoring assignment: %s", rvalue);
                 return 0;
         }
@@ -501,10 +494,10 @@ static int wireguard_decode_key_and_warn(
 
         r = unbase64mem_full(rvalue, strlen(rvalue), true, &key, &len);
         if (r < 0)
-                return log_syntax(unit, LOG_ERR, filename, line, r,
+                return log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to decode wireguard key provided by %s=, ignoring assignment: %m", lvalue);
         if (len != WG_KEY_LEN)
-                return log_syntax(unit, LOG_ERR, filename, line, 0,
+                return log_syntax(unit, LOG_WARNING, filename, line, SYNTHETIC_ERRNO(EINVAL),
                            "Wireguard key provided by %s= has invalid length (%zu bytes), ignoring assignment.",
                            lvalue, len);
 
@@ -580,7 +573,7 @@ int config_parse_wireguard_preshared_key(
                 void *data,
                 void *userdata) {
 
-        _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
+        WireguardPeer *peer;
         Wireguard *w;
         int r;
 
@@ -590,13 +583,9 @@ int config_parse_wireguard_preshared_key(
 
         r = wireguard_peer_new_static(w, filename, section_line, &peer);
         if (r < 0)
-                return r;
-
-        r = wireguard_decode_key_and_warn(rvalue, peer->preshared_key, unit, filename, line, lvalue);
-        if (r < 0)
-                return r;
+                return log_oom();
 
-        TAKE_PTR(peer);
+        (void) wireguard_decode_key_and_warn(rvalue, peer->preshared_key, unit, filename, line, lvalue);
         return 0;
 }
 
@@ -623,7 +612,7 @@ int config_parse_wireguard_preshared_key_file(
 
         r = wireguard_peer_new_static(w, filename, section_line, &peer);
         if (r < 0)
-                return r;
+                return log_oom();
 
         if (isempty(rvalue)) {
                 peer->preshared_key_file = mfree(peer->preshared_key_file);
@@ -665,11 +654,11 @@ int config_parse_wireguard_public_key(
 
         r = wireguard_peer_new_static(w, filename, section_line, &peer);
         if (r < 0)
-                return r;
+                return log_oom();
 
         r = wireguard_decode_key_and_warn(rvalue, peer->public_key, unit, filename, line, lvalue);
         if (r < 0)
-                return r;
+                return 0;
 
         TAKE_PTR(peer);
         return 0;
@@ -702,25 +691,25 @@ int config_parse_wireguard_allowed_ips(
 
         r = wireguard_peer_new_static(w, filename, section_line, &peer);
         if (r < 0)
-                return r;
+                return log_oom();
 
-        for (;;) {
+        for (const char *p = rvalue;;) {
                 _cleanup_free_ char *word = NULL;
 
-                r = extract_first_word(&rvalue, &word, "," WHITESPACE, 0);
+                r = extract_first_word(&p, &word, "," WHITESPACE, 0);
                 if (r == 0)
                         break;
                 if (r == -ENOMEM)
                         return log_oom();
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
+                        log_syntax(unit, LOG_WARNING, 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,
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
                                    "Network address is invalid, ignoring assignment: %s", word);
                         continue;
                 }
@@ -766,15 +755,11 @@ int config_parse_wireguard_endpoint(
         w = WIREGUARD(data);
         assert(w);
 
-        r = wireguard_peer_new_static(w, filename, section_line, &peer);
-        if (r < 0)
-                return r;
-
         if (rvalue[0] == '[') {
                 begin = &rvalue[1];
                 end = strchr(rvalue, ']');
                 if (!end) {
-                        log_syntax(unit, LOG_ERR, filename, line, 0,
+                        log_syntax(unit, LOG_WARNING, filename, line, 0,
                                    "Unable to find matching brace of endpoint, ignoring assignment: %s",
                                    rvalue);
                         return 0;
@@ -782,7 +767,7 @@ int config_parse_wireguard_endpoint(
                 len = end - begin;
                 ++end;
                 if (*end != ':' || !*(end + 1)) {
-                        log_syntax(unit, LOG_ERR, filename, line, 0,
+                        log_syntax(unit, LOG_WARNING, filename, line, 0,
                                    "Unable to find port of endpoint, ignoring assignment: %s",
                                    rvalue);
                         return 0;
@@ -792,7 +777,7 @@ int config_parse_wireguard_endpoint(
                 begin = rvalue;
                 end = strrchr(rvalue, ':');
                 if (!end || !*(end + 1)) {
-                        log_syntax(unit, LOG_ERR, filename, line, 0,
+                        log_syntax(unit, LOG_WARNING, filename, line, 0,
                                    "Unable to find port of endpoint, ignoring assignment: %s",
                                    rvalue);
                         return 0;
@@ -801,23 +786,23 @@ int config_parse_wireguard_endpoint(
                 ++end;
         }
 
-        r = free_and_strndup(&peer->endpoint_host, begin, len);
+        r = wireguard_peer_new_static(w, filename, section_line, &peer);
         if (r < 0)
                 return log_oom();
 
-        r = free_and_strdup(&peer->endpoint_port, end);
+        r = free_and_strndup(&peer->endpoint_host, begin, len);
         if (r < 0)
                 return log_oom();
 
-        r = set_ensure_allocated(&w->peers_with_unresolved_endpoint, NULL);
+        r = free_and_strdup(&peer->endpoint_port, end);
         if (r < 0)
                 return log_oom();
 
-        r = set_put(w->peers_with_unresolved_endpoint, peer);
+        r = set_ensure_put(&w->peers_with_unresolved_endpoint, NULL, peer);
         if (r < 0)
-                return r;
+                return log_oom();
+        TAKE_PTR(peer); /* The peer may already have been in the hash map, that is fine too. */
 
-        TAKE_PTR(peer);
         return 0;
 }
 
@@ -833,7 +818,7 @@ int config_parse_wireguard_keepalive(
                 void *data,
                 void *userdata) {
 
-        _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL;
+        WireguardPeer *peer;
         uint16_t keepalive = 0;
         Wireguard *w;
         int r;
@@ -846,23 +831,21 @@ int config_parse_wireguard_keepalive(
 
         r = wireguard_peer_new_static(w, filename, section_line, &peer);
         if (r < 0)
-                return r;
+                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",
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to parse \"%s\" as keepalive interval (range 0–65535), ignoring assignment: %m",
                                    rvalue);
                         return 0;
                 }
         }
 
         peer->persistent_keepalive_interval = keepalive;
-
-        TAKE_PTR(peer);
         return 0;
 }
 
@@ -905,7 +888,10 @@ static int wireguard_read_key_file(const char *filename, uint8_t dest[static WG_
 
         (void) warn_file_is_world_accessible(filename, NULL, NULL, 0);
 
-        r = read_full_file_full(AT_FDCWD, filename, READ_FULL_FILE_SECURE | READ_FULL_FILE_UNBASE64, &key, &key_len);
+        r = read_full_file_full(
+                        AT_FDCWD, filename,
+                        READ_FULL_FILE_SECURE | READ_FULL_FILE_UNBASE64 | READ_FULL_FILE_WARN_WORLD_READABLE | READ_FULL_FILE_CONNECT_SOCKET,
+                        &key, &key_len);
         if (r < 0)
                 return r;
 
index a9390b8689a27bb78c49de6885adccbf28bb7c33..48182e61ddfe803003b1601ec0f8d9c6be991867 100644 (file)
@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
+#include <arpa/inet.h>
 #include <getopt.h>
 #include <linux/if_addrlabel.h>
 #include <net/if.h>
@@ -7,18 +8,23 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
+#include <linux/if_bridge.h>
+#include <linux/if_tunnel.h>
 
 #include "sd-bus.h"
 #include "sd-device.h"
+#include "sd-dhcp-client.h"
 #include "sd-hwdb.h"
 #include "sd-lldp.h"
 #include "sd-netlink.h"
 #include "sd-network.h"
 
 #include "alloc-util.h"
+#include "bond-util.h"
+#include "bridge-util.h"
 #include "bus-common-errors.h"
 #include "bus-error.h"
-#include "bus-util.h"
+#include "bus-locator.h"
 #include "device-util.h"
 #include "escape.h"
 #include "ether-addr-util.h"
 #include "fd-util.h"
 #include "format-table.h"
 #include "format-util.h"
+#include "geneve-util.h"
 #include "glob-util.h"
 #include "hwdb-util.h"
+#include "ipvlan-util.h"
 #include "local-addresses.h"
 #include "locale-util.h"
 #include "logs-show.h"
 #include "macro.h"
+#include "macvlan-util.h"
 #include "main-func.h"
 #include "netlink-util.h"
 #include "network-internal.h"
@@ -110,6 +119,14 @@ typedef struct VxLanInfo {
 
         uint16_t dest_port;
 
+        uint8_t proxy;
+        uint8_t learning;
+        uint8_t inerit;
+        uint8_t rsc;
+        uint8_t l2miss;
+        uint8_t l3miss;
+        uint8_t tos;
+        uint8_t ttl;
 } VxLanInfo;
 
 typedef struct LinkInfo {
@@ -120,11 +137,14 @@ typedef struct LinkInfo {
         unsigned short iftype;
         struct ether_addr mac_address;
         struct ether_addr permanent_mac_address;
+        uint32_t master;
         uint32_t mtu;
         uint32_t min_mtu;
         uint32_t max_mtu;
         uint32_t tx_queues;
         uint32_t rx_queues;
+        uint8_t addr_gen_mode;
+        char *qdisc;
         char **alternative_names;
 
         union {
@@ -141,12 +161,44 @@ typedef struct LinkInfo {
         uint32_t max_age;
         uint32_t ageing_time;
         uint32_t stp_state;
+        uint32_t cost;
         uint16_t priority;
         uint8_t mcast_igmp_version;
+        uint8_t port_state;
 
         /* vxlan info */
         VxLanInfo vxlan_info;
 
+        /* vlan info */
+        uint16_t vlan_id;
+
+        /* tunnel info */
+        uint8_t ttl;
+        uint8_t tos;
+        uint8_t inherit;
+        uint8_t df;
+        uint8_t csum;
+        uint8_t csum6_tx;
+        uint8_t csum6_rx;
+        uint16_t tunnel_port;
+        uint32_t vni;
+        uint32_t label;
+        union in_addr_union local;
+        union in_addr_union remote;
+
+        /* bonding info */
+        uint8_t mode;
+        uint32_t miimon;
+        uint32_t updelay;
+        uint32_t downdelay;
+
+        /* macvlan and macvtap info */
+        uint32_t macvlan_mode;
+
+        /* ipvlan info */
+        uint16_t ipvlan_mode;
+        uint16_t ipvlan_flags;
+
         /* ethtool info */
         int autonegotiation;
         uint64_t speed;
@@ -167,6 +219,8 @@ typedef struct LinkInfo {
         bool has_bitrates:1;
         bool has_ethtool_link_info:1;
         bool has_wlan_link_info:1;
+        bool has_tunnel_ipv4:1;
+        bool has_ipv6_address_generation_mode:1;
 
         bool needs_freeing:1;
 } LinkInfo;
@@ -179,6 +233,7 @@ static const LinkInfo* link_info_array_free(LinkInfo *array) {
         for (unsigned i = 0; array && array[i].needs_freeing; i++) {
                 sd_device_unref(array[i].sd_device);
                 free(array[i].ssid);
+                free(array[i].qdisc);
                 strv_free(array[i].alternative_names);
         }
 
@@ -211,9 +266,15 @@ static int decode_netdev(sd_netlink_message *m, LinkInfo *info) {
                 (void) sd_netlink_message_read_u32(m, IFLA_BR_MAX_AGE, &info->max_age);
                 (void) sd_netlink_message_read_u32(m, IFLA_BR_AGEING_TIME, &info->ageing_time);
                 (void) sd_netlink_message_read_u32(m, IFLA_BR_STP_STATE, &info->stp_state);
+                (void) sd_netlink_message_read_u32(m, IFLA_BRPORT_COST, &info->cost);
                 (void) sd_netlink_message_read_u16(m, IFLA_BR_PRIORITY, &info->priority);
                 (void) sd_netlink_message_read_u8(m, IFLA_BR_MCAST_IGMP_VERSION, &info->mcast_igmp_version);
-
+                (void) sd_netlink_message_read_u8(m, IFLA_BRPORT_STATE, &info->port_state);
+        } if (streq(received_kind, "bond")) {
+                (void) sd_netlink_message_read_u8(m, IFLA_BOND_MODE, &info->mode);
+                (void) sd_netlink_message_read_u32(m, IFLA_BOND_MIIMON, &info->miimon);
+                (void) sd_netlink_message_read_u32(m, IFLA_BOND_DOWNDELAY, &info->downdelay);
+                (void) sd_netlink_message_read_u32(m, IFLA_BOND_UPDELAY, &info->updelay);
         } else if (streq(received_kind, "vxlan")) {
                 (void) sd_netlink_message_read_u32(m, IFLA_VXLAN_ID, &info->vxlan_info.vni);
 
@@ -237,6 +298,53 @@ static int decode_netdev(sd_netlink_message *m, LinkInfo *info) {
 
                 (void) sd_netlink_message_read_u32(m, IFLA_VXLAN_LINK, &info->vxlan_info.link);
                 (void) sd_netlink_message_read_u16(m, IFLA_VXLAN_PORT, &info->vxlan_info.dest_port);
+                (void) sd_netlink_message_read_u8(m, IFLA_VXLAN_PROXY, &info->vxlan_info.proxy);
+                (void) sd_netlink_message_read_u8(m, IFLA_VXLAN_LEARNING, &info->vxlan_info.learning);
+                (void) sd_netlink_message_read_u8(m, IFLA_VXLAN_RSC, &info->vxlan_info.rsc);
+                (void) sd_netlink_message_read_u8(m, IFLA_VXLAN_L3MISS, &info->vxlan_info.l3miss);
+                (void) sd_netlink_message_read_u8(m, IFLA_VXLAN_L2MISS, &info->vxlan_info.l2miss);
+                (void) sd_netlink_message_read_u8(m, IFLA_VXLAN_TOS, &info->vxlan_info.tos);
+                (void) sd_netlink_message_read_u8(m, IFLA_VXLAN_TTL, &info->vxlan_info.ttl);
+        } else if (streq(received_kind, "vlan"))
+                (void) sd_netlink_message_read_u16(m, IFLA_VLAN_ID, &info->vlan_id);
+        else if (STR_IN_SET(received_kind, "ipip", "sit")) {
+                (void) sd_netlink_message_read_in_addr(m, IFLA_IPTUN_LOCAL, &info->local.in);
+                (void) sd_netlink_message_read_in_addr(m, IFLA_IPTUN_REMOTE, &info->remote.in);
+        } else if (streq(received_kind, "geneve")) {
+                (void) sd_netlink_message_read_u32(m, IFLA_GENEVE_ID, &info->vni);
+
+                r = sd_netlink_message_read_in_addr(m, IFLA_GENEVE_REMOTE, &info->remote.in);
+                if (r >= 0)
+                        info->has_tunnel_ipv4 = true;
+                else
+                        (void) sd_netlink_message_read_in6_addr(m, IFLA_GENEVE_REMOTE6, &info->remote.in6);
+
+                (void) sd_netlink_message_read_u8(m, IFLA_GENEVE_TTL, &info->ttl);
+                (void) sd_netlink_message_read_u8(m, IFLA_GENEVE_TTL_INHERIT, &info->inherit);
+                (void) sd_netlink_message_read_u8(m, IFLA_GENEVE_TOS, &info->tos);
+                (void) sd_netlink_message_read_u8(m, IFLA_GENEVE_DF, &info->df);
+                (void) sd_netlink_message_read_u8(m, IFLA_GENEVE_UDP_CSUM, &info->csum);
+                (void) sd_netlink_message_read_u8(m, IFLA_GENEVE_UDP_ZERO_CSUM6_TX, &info->csum6_tx);
+                (void) sd_netlink_message_read_u8(m, IFLA_GENEVE_UDP_ZERO_CSUM6_RX, &info->csum6_rx);
+                (void) sd_netlink_message_read_u16(m, IFLA_GENEVE_PORT, &info->tunnel_port);
+                (void) sd_netlink_message_read_u32(m, IFLA_GENEVE_LABEL, &info->label);
+        } else if (STR_IN_SET(received_kind, "gre", "gretap", "erspan")) {
+                (void) sd_netlink_message_read_in_addr(m, IFLA_GRE_LOCAL, &info->local.in);
+                (void) sd_netlink_message_read_in_addr(m, IFLA_GRE_REMOTE, &info->remote.in);
+        } else if (STR_IN_SET(received_kind, "ip6gre", "ip6gretap", "ip6erspan")) {
+                (void) sd_netlink_message_read_in6_addr(m, IFLA_GRE_LOCAL, &info->local.in6);
+                (void) sd_netlink_message_read_in6_addr(m, IFLA_GRE_REMOTE, &info->remote.in6);
+        } else if (streq(received_kind, "vti")) {
+                (void) sd_netlink_message_read_in_addr(m, IFLA_VTI_LOCAL, &info->local.in);
+                (void) sd_netlink_message_read_in_addr(m, IFLA_VTI_REMOTE, &info->remote.in);
+        } else if (streq(received_kind, "vti6")) {
+                (void) sd_netlink_message_read_in6_addr(m, IFLA_VTI_LOCAL, &info->local.in6);
+                (void) sd_netlink_message_read_in6_addr(m, IFLA_VTI_REMOTE, &info->remote.in6);
+        } else if (STR_IN_SET(received_kind, "macvlan", "macvtap"))
+                (void) sd_netlink_message_read_u32(m, IFLA_MACVLAN_MODE, &info->macvlan_mode);
+        else if (streq(received_kind, "ipvlan")) {
+                (void) sd_netlink_message_read_u16(m, IFLA_IPVLAN_MODE, &info->ipvlan_mode);
+                (void) sd_netlink_message_read_u16(m, IFLA_IPVLAN_FLAGS, &info->ipvlan_flags);
         }
 
         strncpy(info->netdev_kind, received_kind, IFNAMSIZ);
@@ -249,7 +357,7 @@ static int decode_netdev(sd_netlink_message *m, LinkInfo *info) {
 
 static int decode_link(sd_netlink_message *m, LinkInfo *info, char **patterns, bool matched_patterns[]) {
         _cleanup_strv_free_ char **altnames = NULL;
-        const char *name;
+        const char *name, *qdisc;
         int ifindex, r;
         uint16_t type;
 
@@ -272,7 +380,7 @@ static int decode_link(sd_netlink_message *m, LinkInfo *info, char **patterns, b
                 return r;
 
         r = sd_netlink_message_read_strv(m, IFLA_PROP_LIST, IFLA_ALT_IFNAME, &altnames);
-        if (r < 0 && !IN_SET(r, -EOPNOTSUPP, -ENODATA))
+        if (r < 0 && r != -ENODATA)
                 return r;
 
         if (patterns) {
@@ -333,15 +441,45 @@ static int decode_link(sd_netlink_message *m, LinkInfo *info, char **patterns, b
         else if (sd_netlink_message_read(m, IFLA_STATS, sizeof info->stats, &info->stats) >= 0)
                 info->has_stats = true;
 
+        r = sd_netlink_message_read_string(m, IFLA_QDISC, &qdisc);
+        if (r >= 0) {
+                info->qdisc = strdup(qdisc);
+                if (!info->qdisc)
+                        return log_oom();
+        }
+
+        (void) sd_netlink_message_read_u32(m, IFLA_MASTER, &info->master);
+
+        r = sd_netlink_message_enter_container(m, IFLA_AF_SPEC);
+        if (r >= 0) {
+                r = sd_netlink_message_enter_container(m, AF_INET6);
+                if (r >= 0) {
+                        r = sd_netlink_message_read_u8(m, IFLA_INET6_ADDR_GEN_MODE, &info->addr_gen_mode);
+                        if (r >= 0 && IN_SET(info->addr_gen_mode,
+                                             IN6_ADDR_GEN_MODE_EUI64,
+                                             IN6_ADDR_GEN_MODE_NONE,
+                                             IN6_ADDR_GEN_MODE_STABLE_PRIVACY,
+                                             IN6_ADDR_GEN_MODE_RANDOM))
+                                info->has_ipv6_address_generation_mode = true;
+
+                        (void) sd_netlink_message_exit_container(m);
+                }
+                (void) sd_netlink_message_exit_container(m);
+        }
+
         /* fill kind info */
         (void) decode_netdev(m, info);
 
         return 1;
 }
 
-static int acquire_link_bitrates(sd_bus *bus, LinkInfo *link) {
-        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
-        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+static int link_get_property(
+                sd_bus *bus,
+                const LinkInfo *link,
+                sd_bus_error *error,
+                sd_bus_message **reply,
+                const char *iface,
+                const char *propname) {
         _cleanup_free_ char *path = NULL, *ifindex_str = NULL;
         int r;
 
@@ -352,17 +490,25 @@ static int acquire_link_bitrates(sd_bus *bus, LinkInfo *link) {
         if (r < 0)
                 return r;
 
-        r = sd_bus_call_method(
+        return sd_bus_call_method(
                         bus,
                         "org.freedesktop.network1",
                         path,
                         "org.freedesktop.DBus.Properties",
                         "Get",
-                        &error,
-                        &reply,
+                        error,
+                        reply,
                         "ss",
-                        "org.freedesktop.network1.Link",
-                        "BitRates");
+                        iface,
+                        propname);
+}
+
+static int acquire_link_bitrates(sd_bus *bus, LinkInfo *link) {
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        int r;
+
+        r = link_get_property(bus, link, &error, &reply, "org.freedesktop.network1.Link", "BitRates");
         if (r < 0) {
                 bool quiet = sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_PROPERTY) ||
                              sd_bus_error_has_name(&error, BUS_ERROR_SPEED_METER_INACTIVE);
@@ -573,7 +719,7 @@ static int list_links(int argc, char *argv[], void *userdata) {
 
         r = table_print(table, NULL);
         if (r < 0)
-                return log_error_errno(r, "Failed to print table: %m");
+                return table_log_print_error(r);
 
         if (arg_legend)
                 printf("\n%i links listed.\n", c);
@@ -785,12 +931,13 @@ static int dump_gateways(
 
 static int dump_addresses(
                 sd_netlink *rtnl,
+                sd_dhcp_lease *lease,
                 Table *table,
                 int ifindex) {
 
         _cleanup_free_ struct local_address *local = NULL;
-        _cleanup_free_ char *dhcp4_address = NULL;
         _cleanup_strv_free_ char **buf = NULL;
+        struct in_addr dhcp4_address = {};
         int r, n, i;
 
         assert(rtnl);
@@ -800,7 +947,8 @@ static int dump_addresses(
         if (n <= 0)
                 return n;
 
-        (void) sd_network_link_get_dhcp4_address(ifindex, &dhcp4_address);
+        if (lease)
+                (void) sd_dhcp_lease_get_address(lease, &dhcp4_address);
 
         for (i = 0; i < n; i++) {
                 _cleanup_free_ char *pretty = NULL;
@@ -810,13 +958,19 @@ static int dump_addresses(
                 if (r < 0)
                         return r;
 
-                if (dhcp4_address && streq(pretty, dhcp4_address)) {
-                        _cleanup_free_ char *p = NULL;
+                if (local[i].family == AF_INET && in4_addr_equal(&local[i].address.in, &dhcp4_address)) {
+                        struct in_addr server_address;
+                        char *p, s[INET_ADDRSTRLEN];
 
-                        p = pretty;
-                        pretty = strjoin(pretty , " (DHCP4)");
-                        if (!pretty)
+                        r = sd_dhcp_lease_get_server_identifier(lease, &server_address);
+                        if (r >= 0 && inet_ntop(AF_INET, &server_address, s, sizeof(s)))
+                                p = strjoin(pretty, " (DHCP4 via ", s, ")");
+                        else
+                                p = strjoin(pretty, " (DHCP4)");
+                        if (!p)
                                 return log_oom();
+
+                        free_and_replace(pretty, p);
                 }
 
                 r = strv_extendf(&buf, "%s%s%s",
@@ -910,7 +1064,7 @@ static int dump_address_labels(sd_netlink *rtnl) {
 
         r = table_print(table, NULL);
         if (r < 0)
-                return log_error_errno(r, "Failed to print table: %m");
+                return table_log_print_error(r);
 
         return 0;
 }
@@ -1018,6 +1172,96 @@ static int dump_lldp_neighbors(Table *table, const char *prefix, int ifindex) {
         return dump_list(table, prefix, buf);
 }
 
+static int dump_dhcp_leases(Table *table, const char *prefix, sd_bus *bus, const LinkInfo *link) {
+        _cleanup_strv_free_ char **buf = NULL;
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        int r;
+
+        r = link_get_property(bus, link, &error, &reply, "org.freedesktop.network1.DHCPServer", "Leases");
+        if (r < 0) {
+                bool quiet = sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_PROPERTY);
+
+                log_full_errno(quiet ? LOG_DEBUG : LOG_WARNING,
+                               r, "Failed to query link DHCP leases: %s", bus_error_message(&error, r));
+                return 0;
+        }
+
+        r = sd_bus_message_enter_container(reply, 'v', "a(uayayayayt)");
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        r = sd_bus_message_enter_container(reply, 'a', "(uayayayayt)");
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        while ((r = sd_bus_message_enter_container(reply, 'r', "uayayayayt")) > 0) {
+                _cleanup_free_ char *id = NULL, *ip = NULL;
+                const void *client_id, *addr, *gtw, *hwaddr;
+                size_t client_id_sz, sz;
+                uint64_t expiration;
+                uint32_t family;
+
+                r = sd_bus_message_read(reply, "u", &family);
+                if (r < 0)
+                        return bus_log_parse_error(r);
+
+                r = sd_bus_message_read_array(reply, 'y', &client_id, &client_id_sz);
+                if (r < 0)
+                        return bus_log_parse_error(r);
+
+                r = sd_bus_message_read_array(reply, 'y', &addr, &sz);
+                if (r < 0 || sz != 4)
+                        return bus_log_parse_error(r);
+
+                r = sd_bus_message_read_array(reply, 'y', &gtw, &sz);
+                if (r < 0 || sz != 4)
+                        return bus_log_parse_error(r);
+
+                r = sd_bus_message_read_array(reply, 'y', &hwaddr, &sz);
+                if (r < 0)
+                        return bus_log_parse_error(r);
+
+                r = sd_bus_message_read_basic(reply, 't', &expiration);
+                if (r < 0)
+                        return bus_log_parse_error(r);
+
+                r = sd_dhcp_client_id_to_string(client_id, client_id_sz, &id);
+                if (r < 0)
+                        return bus_log_parse_error(r);
+
+                r = in_addr_to_string(family, addr, &ip);
+                if (r < 0)
+                        return bus_log_parse_error(r);
+
+                r = strv_extendf(&buf, "%s (to %s)", ip, id);
+                if (r < 0)
+                        return log_oom();
+
+                r = sd_bus_message_exit_container(reply);
+                if (r < 0)
+                        return bus_log_parse_error(r);
+        }
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        r = sd_bus_message_exit_container(reply);
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        r = sd_bus_message_exit_container(reply);
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        if (strv_isempty(buf)) {
+                r = strv_extendf(&buf, "none");
+                if (r < 0)
+                        return log_oom();
+        }
+
+        return dump_list(table, prefix, buf);
+}
+
 static int dump_ifindexes(Table *table, const char *prefix, const int *ifindexes) {
         unsigned c;
         int r;
@@ -1138,17 +1382,18 @@ static int show_logs(const LinkInfo *info) {
 }
 
 static int link_status_one(
+                sd_bus *bus,
                 sd_netlink *rtnl,
                 sd_hwdb *hwdb,
                 const LinkInfo *info) {
 
-        _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **search_domains = NULL, **route_domains = NULL;
-        _cleanup_free_ char *setup_state = NULL, *operational_state = NULL, *tz = NULL;
-        _cleanup_free_ char *t = NULL, *network = NULL;
-        const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL;
-        const char *on_color_operational, *off_color_operational,
-                *on_color_setup, *off_color_setup;
+        _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **sip = NULL, **search_domains = NULL, **route_domains = NULL;
+        _cleanup_free_ char *t = NULL, *network = NULL, *iaid = NULL, *duid = NULL,
+                *setup_state = NULL, *operational_state = NULL, *lease_file = NULL;
+        const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL,
+                *on_color_operational, *off_color_operational, *on_color_setup, *off_color_setup;
         _cleanup_free_ int *carrier_bound_to = NULL, *carrier_bound_by = NULL;
+        _cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
         _cleanup_(table_unrefp) Table *table = NULL;
         TableCell *cell;
         int r;
@@ -1168,6 +1413,7 @@ static int link_status_one(
         (void) sd_network_link_get_search_domains(info->ifindex, &search_domains);
         (void) sd_network_link_get_route_domains(info->ifindex, &route_domains);
         (void) sd_network_link_get_ntp(info->ifindex, &ntp);
+        (void) sd_network_link_get_sip(info->ifindex, &sip);
 
         if (info->sd_device) {
                 (void) sd_device_get_property_value(info->sd_device, "ID_NET_LINK_FILE", &link);
@@ -1188,6 +1434,11 @@ static int link_status_one(
         (void) sd_network_link_get_carrier_bound_to(info->ifindex, &carrier_bound_to);
         (void) sd_network_link_get_carrier_bound_by(info->ifindex, &carrier_bound_by);
 
+        if (asprintf(&lease_file, "/run/systemd/netif/leases/%d", info->ifindex) < 0)
+                return log_oom();
+
+        (void) dhcp_lease_load(&lease, lease_file);
+
         table = table_new("dot", "key", "value");
         if (!table)
                 return log_oom();
@@ -1235,6 +1486,7 @@ static int link_status_one(
         if (r < 0)
                 return table_log_add_error(r);
 
+        strv_sort(info->alternative_names);
         r = dump_list(table, "Alternative Names:", info->alternative_names);
         if (r < 0)
                 return r;
@@ -1336,6 +1588,42 @@ static int link_status_one(
                         return table_log_add_error(r);
         }
 
+        if (info->qdisc) {
+                r = table_add_many(table,
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, "QDisc:",
+                                   TABLE_STRING, info->qdisc);
+                if (r < 0)
+                        return table_log_add_error(r);
+        }
+
+        if (info->master > 0) {
+                r = table_add_many(table,
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, "Master:",
+                                   TABLE_IFINDEX, info->master);
+                if (r < 0)
+                        return table_log_add_error(r);
+        }
+
+        if (info->has_ipv6_address_generation_mode) {
+                static const struct {
+                        const char *mode;
+                } mode_table[] = {
+                        { "eui64" },
+                        { "none" },
+                        { "stable-privacy" },
+                        { "random" },
+                };
+
+                r = table_add_many(table,
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, "IPv6 Address Generation Mode:",
+                                   TABLE_STRING, mode_table[info->addr_gen_mode]);
+                if (r < 0)
+                        return table_log_add_error(r);
+        }
+
         if (streq_ptr(info->netdev_kind, "bridge")) {
                 r = table_add_many(table,
                                    TABLE_EMPTY,
@@ -1358,11 +1646,39 @@ static int link_status_one(
                                    TABLE_BOOLEAN, info->stp_state > 0,
                                    TABLE_EMPTY,
                                    TABLE_STRING, "Multicast IGMP Version:",
-                                   TABLE_UINT8, info->mcast_igmp_version);
+                                   TABLE_UINT8, info->mcast_igmp_version,
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, "Cost:",
+                                   TABLE_UINT32, info->cost);
+                if (r < 0)
+                        return table_log_add_error(r);
+
+                if (info->port_state <= BR_STATE_BLOCKING) {
+                        r = table_add_many(table,
+                                           TABLE_EMPTY,
+                                           TABLE_STRING, "Port State:",
+                                           TABLE_STRING, bridge_state_to_string(info->port_state));
+                }
+        } else if (streq_ptr(info->netdev_kind, "bond")) {
+                r = table_add_many(table,
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, "Mode:",
+                                   TABLE_STRING, bond_mode_to_string(info->mode),
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, "Miimon:",
+                                   TABLE_TIMESPAN_MSEC, jiffies_to_usec(info->miimon),
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, "Updelay:",
+                                   TABLE_TIMESPAN_MSEC, jiffies_to_usec(info->updelay),
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, "Downdelay:",
+                                   TABLE_TIMESPAN_MSEC, jiffies_to_usec(info->downdelay));
                 if (r < 0)
                         return table_log_add_error(r);
 
         } else if (streq_ptr(info->netdev_kind, "vxlan")) {
+                char ttl[CONST_MAX(STRLEN("auto") + 1, DECIMAL_STR_MAX(uint8_t))];
+
                 if (info->vxlan_info.vni > 0) {
                         r = table_add_many(table,
                                            TABLE_EMPTY,
@@ -1373,9 +1689,17 @@ static int link_status_one(
                 }
 
                 if (IN_SET(info->vxlan_info.group_family, AF_INET, AF_INET6)) {
+                        const char *p;
+
+                        r = in_addr_is_multicast(info->vxlan_info.group_family, &info->vxlan_info.group);
+                        if (r <= 0)
+                                p = "Remote:";
+                        else
+                                p = "Group:";
+
                         r = table_add_many(table,
                                            TABLE_EMPTY,
-                                           TABLE_STRING, "Group:",
+                                           TABLE_STRING, p,
                                            info->vxlan_info.group_family == AF_INET ? TABLE_IN_ADDR : TABLE_IN6_ADDR,
                                            &info->vxlan_info.group);
                         if (r < 0)
@@ -1409,6 +1733,221 @@ static int link_status_one(
                         if (r < 0)
                                  return table_log_add_error(r);
                 }
+
+                r = table_add_many(table,
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, "Learning:",
+                                   TABLE_BOOLEAN, info->vxlan_info.learning);
+                if (r < 0)
+                        return table_log_add_error(r);
+
+                r = table_add_many(table,
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, "RSC:",
+                                   TABLE_BOOLEAN, info->vxlan_info.rsc);
+                if (r < 0)
+                        return table_log_add_error(r);
+
+                r = table_add_many(table,
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, "L3MISS:",
+                                   TABLE_BOOLEAN, info->vxlan_info.l3miss);
+                if (r < 0)
+                        return table_log_add_error(r);
+
+                r = table_add_many(table,
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, "L2MISS:",
+                                   TABLE_BOOLEAN, info->vxlan_info.l2miss);
+                if (r < 0)
+                        return table_log_add_error(r);
+
+                if (info->vxlan_info.tos > 1) {
+                        r = table_add_many(table,
+                                           TABLE_EMPTY,
+                                           TABLE_STRING, "TOS:",
+                                           TABLE_UINT8, info->vxlan_info.tos);
+                        if (r < 0)
+                                return table_log_add_error(r);
+                }
+
+                if (info->vxlan_info.ttl > 0)
+                        xsprintf(ttl, "%" PRIu8, info->vxlan_info.ttl);
+                else
+                        strcpy(ttl, "auto");
+
+                r = table_add_many(table,
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, "TTL:",
+                                   TABLE_STRING, ttl);
+                if (r < 0)
+                        return table_log_add_error(r);
+        } else if (streq_ptr(info->netdev_kind, "vlan") && info->vlan_id > 0) {
+                r = table_add_many(table,
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, "VLan Id:",
+                                   TABLE_UINT16, info->vlan_id);
+                if (r < 0)
+                        return table_log_add_error(r);
+        } else if (STRPTR_IN_SET(info->netdev_kind, "ipip", "sit", "gre", "gretap", "erspan", "vti")) {
+                if (!in_addr_is_null(AF_INET, &info->local)) {
+                        r = table_add_many(table,
+                                           TABLE_EMPTY,
+                                           TABLE_STRING, "Local:",
+                                           TABLE_IN_ADDR, &info->local);
+                        if (r < 0)
+                                return table_log_add_error(r);
+                }
+
+                if (!in_addr_is_null(AF_INET, &info->remote)) {
+                        r = table_add_many(table,
+                                           TABLE_EMPTY,
+                                           TABLE_STRING, "Remote:",
+                                           TABLE_IN_ADDR, &info->remote);
+                        if (r < 0)
+                                return table_log_add_error(r);
+                }
+        } else if (STRPTR_IN_SET(info->netdev_kind, "ip6gre", "ip6gretap", "ip6erspan", "vti6")) {
+                if (!in_addr_is_null(AF_INET6, &info->local)) {
+                        r = table_add_many(table,
+                                           TABLE_EMPTY,
+                                           TABLE_STRING, "Local:",
+                                           TABLE_IN6_ADDR, &info->local);
+                        if (r < 0)
+                                return table_log_add_error(r);
+                }
+
+                if (!in_addr_is_null(AF_INET6, &info->remote)) {
+                        r = table_add_many(table,
+                                           TABLE_EMPTY,
+                                           TABLE_STRING, "Remote:",
+                                           TABLE_IN6_ADDR, &info->remote);
+                        if (r < 0)
+                                return table_log_add_error(r);
+                }
+        } else if (streq_ptr(info->netdev_kind, "geneve")) {
+                r = table_add_many(table,
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, "VNI:",
+                                   TABLE_UINT32, info->vni);
+                if (r < 0)
+                        return table_log_add_error(r);
+
+                if (info->has_tunnel_ipv4 && !in_addr_is_null(AF_INET, &info->remote)) {
+                        r = table_add_many(table,
+                                           TABLE_EMPTY,
+                                           TABLE_STRING, "Remote:",
+                                           TABLE_IN_ADDR, &info->remote);
+                        if (r < 0)
+                                return table_log_add_error(r);
+                } else if (!in_addr_is_null(AF_INET6, &info->remote)) {
+                        r = table_add_many(table,
+                                           TABLE_EMPTY,
+                                           TABLE_STRING, "Remote:",
+                                           TABLE_IN6_ADDR, &info->remote);
+                        if (r < 0)
+                                return table_log_add_error(r);
+                }
+
+                if (info->ttl > 0) {
+                        r = table_add_many(table,
+                                           TABLE_EMPTY,
+                                           TABLE_STRING, "TTL:",
+                                           TABLE_UINT8, info->ttl);
+                        if (r < 0)
+                                return table_log_add_error(r);
+                }
+
+                if (info->tos > 0) {
+                        r = table_add_many(table,
+                                           TABLE_EMPTY,
+                                           TABLE_STRING, "TOS:",
+                                           TABLE_UINT8, info->tos);
+                        if (r < 0)
+                                return table_log_add_error(r);
+                }
+
+                r = table_add_many(table,
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, "Port:",
+                                   TABLE_UINT16, info->tunnel_port);
+                if (r < 0)
+                        return table_log_add_error(r);
+
+                r = table_add_many(table,
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, "Inherit:",
+                                   TABLE_STRING, geneve_df_to_string(info->inherit));
+                if (r < 0)
+                        return table_log_add_error(r);
+
+                if (info->df > 0) {
+                        r = table_add_many(table,
+                                           TABLE_EMPTY,
+                                           TABLE_STRING, "IPDoNotFragment:",
+                                           TABLE_UINT8, info->df);
+                        if (r < 0)
+                                return table_log_add_error(r);
+                }
+
+                r = table_add_many(table,
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, "UDPChecksum:",
+                                   TABLE_BOOLEAN, info->csum);
+                if (r < 0)
+                        return table_log_add_error(r);
+
+                r = table_add_many(table,
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, "UDP6ZeroChecksumTx:",
+                                   TABLE_BOOLEAN, info->csum6_tx);
+                if (r < 0)
+                        return table_log_add_error(r);
+
+                r = table_add_many(table,
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, "UDP6ZeroChecksumRx:",
+                                   TABLE_BOOLEAN, info->csum6_rx);
+                if (r < 0)
+                        return table_log_add_error(r);
+
+                if (info->label > 0) {
+                        r = table_add_many(table,
+                                           TABLE_EMPTY,
+                                           TABLE_STRING, "FlowLabel:",
+                                           TABLE_UINT32, info->label);
+                        if (r < 0)
+                                return table_log_add_error(r);
+                }
+        } else if (STRPTR_IN_SET(info->netdev_kind, "macvlan", "macvtap")) {
+                r = table_add_many(table,
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, "Mode:",
+                                   TABLE_STRING, macvlan_mode_to_string(info->macvlan_mode));
+                if (r < 0)
+                        return table_log_add_error(r);
+        } else if (streq_ptr(info->netdev_kind, "ipvlan")) {
+                _cleanup_free_ char *p = NULL, *s = NULL;
+
+                if (info->ipvlan_flags & IPVLAN_F_PRIVATE)
+                        p = strdup("private");
+                else if (info->ipvlan_flags & IPVLAN_F_VEPA)
+                        p = strdup("vepa");
+                else
+                        p = strdup("bridge");
+                if (!p)
+                        log_oom();
+
+                s = strjoin(ipvlan_mode_to_string(info->ipvlan_mode), " (", p, ")");
+                if (!s)
+                        return log_oom();
+
+                r = table_add_many(table,
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, "Mode:",
+                                   TABLE_STRING, s);
+                if (r < 0)
+                        return table_log_add_error(r);
         }
 
         if (info->has_wlan_link_info) {
@@ -1498,7 +2037,7 @@ static int link_status_one(
                 }
         }
 
-        r = dump_addresses(rtnl, table, info->ifindex);
+        r = dump_addresses(rtnl, lease, table, info->ifindex);
         if (r < 0)
                 return r;
         r = dump_gateways(rtnl, hwdb, table, info->ifindex);
@@ -1514,6 +2053,9 @@ static int link_status_one(
         if (r < 0)
                 return r;
         r = dump_list(table, "NTP:", ntp);
+        if (r < 0)
+                return r;
+        r = dump_list(table, "SIP:", sip);
         if (r < 0)
                 return r;
         r = dump_ifindexes(table, "Carrier Bound To:", carrier_bound_to);
@@ -1523,12 +2065,53 @@ static int link_status_one(
         if (r < 0)
                 return r;
 
-        (void) sd_network_link_get_timezone(info->ifindex, &tz);
-        if (tz) {
+        if (lease) {
+                const void *client_id;
+                size_t client_id_len;
+                const char *tz;
+
+                r = sd_dhcp_lease_get_timezone(lease, &tz);
+                if (r >= 0) {
+                        r = table_add_many(table,
+                                           TABLE_EMPTY,
+                                           TABLE_STRING, "Time Zone:",
+                                           TABLE_STRING, tz);
+                        if (r < 0)
+                                return table_log_add_error(r);
+                }
+
+                r = sd_dhcp_lease_get_client_id(lease, &client_id, &client_id_len);
+                if (r >= 0) {
+                        _cleanup_free_ char *id = NULL;
+
+                        r = sd_dhcp_client_id_to_string(client_id, client_id_len, &id);
+                        if (r >= 0) {
+                                r = table_add_many(table,
+                                                   TABLE_EMPTY,
+                                                   TABLE_STRING, "DHCP4 Client ID:",
+                                                   TABLE_STRING, id);
+                                if (r < 0)
+                                        return table_log_add_error(r);
+                        }
+                }
+        }
+
+        r = sd_network_link_get_dhcp6_client_iaid_string(info->ifindex, &iaid);
+        if (r >= 0) {
                 r = table_add_many(table,
                                    TABLE_EMPTY,
-                                   TABLE_STRING, "Time Zone:",
-                                   TABLE_STRING, tz);
+                                   TABLE_STRING, "DHCP6 Client IAID:",
+                                   TABLE_STRING, iaid);
+                if (r < 0)
+                        return table_log_add_error(r);
+        }
+
+        r = sd_network_link_get_dhcp6_client_duid_string(info->ifindex, &duid);
+        if (r >= 0) {
+                r = table_add_many(table,
+                                   TABLE_EMPTY,
+                                   TABLE_STRING, "DHCP6 Client DUID:",
+                                   TABLE_STRING, duid);
                 if (r < 0)
                         return table_log_add_error(r);
         }
@@ -1537,13 +2120,17 @@ static int link_status_one(
         if (r < 0)
                 return r;
 
+        r = dump_dhcp_leases(table, "Offered DHCP leases:", bus, info);
+        if (r < 0)
+                return r;
+
         r = dump_statistics(table, info);
         if (r < 0)
                 return r;
 
         r = table_print(table, NULL);
         if (r < 0)
-                return log_error_errno(r, "Failed to print table: %m");
+                return table_log_print_error(r);
 
         return show_logs(info);
 }
@@ -1586,7 +2173,7 @@ static int system_status(sd_netlink *rtnl, sd_hwdb *hwdb) {
         if (r < 0)
                 return table_log_add_error(r);
 
-        r = dump_addresses(rtnl, table, 0);
+        r = dump_addresses(rtnl, NULL, table, 0);
         if (r < 0)
                 return r;
         r = dump_gateways(rtnl, hwdb, table, 0);
@@ -1615,7 +2202,7 @@ static int system_status(sd_netlink *rtnl, sd_hwdb *hwdb) {
 
         r = table_print(table, NULL);
         if (r < 0)
-                return log_error_errno(r, "Failed to print table: %m");
+                return table_log_print_error(r);
 
         return show_logs(NULL);
 }
@@ -1654,7 +2241,7 @@ static int link_status(int argc, char *argv[], void *userdata) {
                 if (i > 0)
                         fputc('\n', stdout);
 
-                link_status_one(rtnl, hwdb, links + i);
+                link_status_one(bus, rtnl, hwdb, links + i);
         }
 
         return 0;
@@ -1835,7 +2422,7 @@ static int link_lldp_status(int argc, char *argv[], void *userdata) {
 
         r = table_print(table, NULL);
         if (r < 0)
-                return log_error_errno(r, "Failed to print table: %m");
+                return table_log_print_error(r);
 
         if (arg_legend) {
                 lldp_capabilities_legend(all);
@@ -1862,6 +2449,69 @@ static int link_delete_send_message(sd_netlink *rtnl, int index) {
         return 0;
 }
 
+static int link_up_down_send_message(sd_netlink *rtnl, char *command, int index) {
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
+        int r;
+
+        assert(rtnl);
+
+        r = sd_rtnl_message_new_link(rtnl, &req, RTM_SETLINK, index);
+        if (r < 0)
+                return rtnl_log_create_error(r);
+
+        if (streq(command, "up"))
+                r = sd_rtnl_message_link_set_flags(req, IFF_UP, IFF_UP);
+        else
+                r = sd_rtnl_message_link_set_flags(req, 0, IFF_UP);
+        if (r < 0)
+                return log_error_errno(r, "Could not set link flags: %m");
+
+        r = sd_netlink_call(rtnl, req, 0, NULL);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+static int link_up_down(int argc, char *argv[], void *userdata) {
+        _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+        _cleanup_set_free_ Set *indexes = NULL;
+        int index, r, i;
+        Iterator j;
+        void *p;
+
+        r = sd_netlink_open(&rtnl);
+        if (r < 0)
+                return log_error_errno(r, "Failed to connect to netlink: %m");
+
+        indexes = set_new(NULL);
+        if (!indexes)
+                return log_oom();
+
+        for (i = 1; i < argc; i++) {
+                index = resolve_interface_or_warn(&rtnl, argv[i]);
+                if (index < 0)
+                        return index;
+
+                r = set_put(indexes, INT_TO_PTR(index));
+                if (r < 0)
+                        return log_oom();
+        }
+
+        SET_FOREACH(p, indexes, j) {
+                index = PTR_TO_INT(p);
+                r = link_up_down_send_message(rtnl, argv[0], index);
+                if (r < 0) {
+                        char ifname[IF_NAMESIZE + 1];
+
+                        return log_error_errno(r, "Failed to %s interface %s: %m",
+                                               argv[1], format_ifname_full(index, ifname, FORMAT_IFNAME_IFINDEX));
+                }
+        }
+
+        return r;
+}
+
 static int link_delete(int argc, char *argv[], void *userdata) {
         _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
         _cleanup_set_free_ Set *indexes = NULL;
@@ -1905,15 +2555,7 @@ static int link_renew_one(sd_bus *bus, int index, const char *name) {
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         int r;
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.network1",
-                        "/org/freedesktop/network1",
-                        "org.freedesktop.network1.Manager",
-                        "RenewLink",
-                        &error,
-                        NULL,
-                        "i", index);
+        r = bus_call_method(bus, bus_network_mgr, "RenewLink", &error, NULL, "i", index);
         if (r < 0)
                 return log_error_errno(r, "Failed to renew dynamic configuration of interface %s: %s",
                                        name, bus_error_message(&error, r));
@@ -1943,6 +2585,40 @@ static int link_renew(int argc, char *argv[], void *userdata) {
         return k;
 }
 
+static int link_force_renew_one(sd_bus *bus, int index, const char *name) {
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        int r;
+
+        r = bus_call_method(bus, bus_network_mgr, "ForceRenewLink", &error, NULL, "i", index);
+        if (r < 0)
+                return log_error_errno(r, "Failed to force renew dynamic configuration of interface %s: %s",
+                                       name, bus_error_message(&error, r));
+
+        return 0;
+}
+
+static int link_force_renew(int argc, char *argv[], void *userdata) {
+        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+        _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+        int index, i, k = 0, r;
+
+        r = sd_bus_open_system(&bus);
+        if (r < 0)
+                return log_error_errno(r, "Failed to connect system bus: %m");
+
+        for (i = 1; i < argc; i++) {
+                index = resolve_interface_or_warn(&rtnl, argv[i]);
+                if (index < 0)
+                        return index;
+
+                r = link_force_renew_one(bus, index, argv[i]);
+                if (r < 0 && k >= 0)
+                        k = r;
+        }
+
+        return k;
+}
+
 static int verb_reload(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;
@@ -1952,13 +2628,7 @@ static int verb_reload(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return log_error_errno(r, "Failed to connect system bus: %m");
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.network1",
-                        "/org/freedesktop/network1",
-                        "org.freedesktop.network1.Manager",
-                        "Reload",
-                        &error, NULL, NULL);
+        r = bus_call_method(bus, bus_network_mgr, "Reload", &error, NULL, NULL);
         if (r < 0)
                 return log_error_errno(r, "Failed to reload network settings: %m");
 
@@ -1994,13 +2664,7 @@ static int verb_reconfigure(int argc, char *argv[], void *userdata) {
 
         SET_FOREACH(p, indexes, j) {
                 index = PTR_TO_INT(p);
-                r = sd_bus_call_method(
-                                bus,
-                                "org.freedesktop.network1",
-                                "/org/freedesktop/network1",
-                                "org.freedesktop.network1.Manager",
-                                "ReconfigureLink",
-                                &error, NULL, "i", index);
+                r = bus_call_method(bus, bus_network_mgr, "ReconfigureLink", &error, NULL, "i", index);
                 if (r < 0) {
                         char ifname[IF_NAMESIZE + 1];
 
@@ -2028,7 +2692,10 @@ static int help(void) {
                "  lldp [PATTERN...]      Show LLDP neighbors\n"
                "  label                  Show current address label entries in the kernel\n"
                "  delete DEVICES...      Delete virtual netdevs\n"
+               "  up DEVICES...          Bring devices up\n"
+               "  down DEVICES...        Bring devices down\n"
                "  renew DEVICES...       Renew dynamic configurations\n"
+               "  forcerenew DEVICES...  Trigger DHCP reconfiguration of all connected clients\n"
                "  reconfigure DEVICES... Reconfigure interfaces\n"
                "  reload                 Reload .network and .netdev files\n"
                "\nOptions:\n"
@@ -2129,7 +2796,10 @@ static int networkctl_main(int argc, char *argv[]) {
                 { "lldp",        VERB_ANY, VERB_ANY, 0,            link_lldp_status    },
                 { "label",       VERB_ANY, VERB_ANY, 0,            list_address_labels },
                 { "delete",      2,        VERB_ANY, 0,            link_delete         },
+                { "up",          2,        VERB_ANY, 0,            link_up_down        },
+                { "down",        2,        VERB_ANY, 0,            link_up_down        },
                 { "renew",       2,        VERB_ANY, 0,            link_renew          },
+                { "forcerenew",  2,        VERB_ANY, 0,            link_force_renew    },
                 { "reconfigure", 2,        VERB_ANY, 0,            verb_reconfigure    },
                 { "reload",      1,        1,        0,            verb_reload         },
                 {}
@@ -2149,9 +2819,7 @@ static void warn_networkd_missing(void) {
 static int run(int argc, char* argv[]) {
         int r;
 
-        log_show_color(true);
-        log_parse_environment();
-        log_open();
+        log_setup_cli();
 
         r = parse_argv(argc, argv);
         if (r <= 0)
index 32b79dd1548107c0f8fd13df148bd93e5254faff..0d53aa90429b333d7ebaa232160498130f2df03a 100644 (file)
@@ -173,11 +173,11 @@ int config_parse_address_label_prefix(const char *unit,
 
         r = address_label_new_static(network, filename, section_line, &n);
         if (r < 0)
-                return r;
+                return log_oom();
 
         r = in_addr_prefix_from_string(rvalue, AF_INET6, &n->in_addr, &n->prefixlen);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "Address label is invalid, ignoring assignment: %s", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Address label is invalid, ignoring assignment: %s", rvalue);
                 return 0;
         }
 
@@ -211,16 +211,16 @@ int config_parse_address_label(
 
         r = address_label_new_static(network, filename, section_line, &n);
         if (r < 0)
-                return r;
+                return log_oom();
 
         r = safe_atou32(rvalue, &k);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address label, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse address label, ignoring: %s", rvalue);
                 return 0;
         }
 
         if (k == 0xffffffffUL) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "Address label is invalid, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, 0, "Address label is invalid, ignoring: %s", rvalue);
                 return 0;
         }
 
index c61e517ea97cad84508b5d6e8c4177f8f78da448..b09d75e6158e7e6e39ba0ce891eaad5aa93ee1ad 100644 (file)
 #define ADDRESSES_PER_LINK_MAX 2048U
 #define STATIC_ADDRESSES_PER_NETWORK_MAX 1024U
 
+int generate_ipv6_eui_64_address(Link *link, struct in6_addr *ret) {
+        assert(link);
+        assert(ret);
+
+        /* see RFC4291 section 2.5.1 */
+        ret->s6_addr[8]  = link->mac.ether_addr_octet[0];
+        ret->s6_addr[8] ^= 1 << 1;
+        ret->s6_addr[9]  = link->mac.ether_addr_octet[1];
+        ret->s6_addr[10] = link->mac.ether_addr_octet[2];
+        ret->s6_addr[11] = 0xff;
+        ret->s6_addr[12] = 0xfe;
+        ret->s6_addr[13] = link->mac.ether_addr_octet[3];
+        ret->s6_addr[14] = link->mac.ether_addr_octet[4];
+        ret->s6_addr[15] = link->mac.ether_addr_octet[5];
+
+        return 0;
+}
+
 int address_new(Address **ret) {
         _cleanup_(address_freep) Address *address = NULL;
 
@@ -107,6 +125,17 @@ void address_free(Address *address) {
         if (address->link && !address->acd) {
                 set_remove(address->link->addresses, address);
                 set_remove(address->link->addresses_foreign, address);
+                set_remove(address->link->static_addresses, address);
+                if (address->link->dhcp_address == address)
+                        address->link->dhcp_address = NULL;
+                if (address->link->dhcp_address_old == address)
+                        address->link->dhcp_address_old = NULL;
+                set_remove(address->link->dhcp6_addresses, address);
+                set_remove(address->link->dhcp6_addresses_old, address);
+                set_remove(address->link->dhcp6_pd_addresses, address);
+                set_remove(address->link->dhcp6_pd_addresses_old, address);
+                set_remove(address->link->ndisc_addresses, address);
+                set_remove(address->link->ndisc_addresses_old, address);
 
                 if (in_addr_equal(AF_INET6, &address->in_addr, (const union in_addr_union *) &address->link->ipv6ll_address))
                         memzero(&address->link->ipv6ll_address, sizeof(struct in6_addr));
@@ -187,7 +216,7 @@ static int address_compare_func(const Address *a1, const Address *a2) {
         }
 }
 
-DEFINE_HASH_OPS(address_hash_ops, Address, address_hash_func, address_compare_func);
+DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(address_hash_ops, Address, address_hash_func, address_compare_func, address_free);
 
 bool address_equal(Address *a1, Address *a2) {
         if (a1 == a2)
@@ -248,11 +277,7 @@ static int address_add_internal(Link *link, Set **addresses,
         /* Consider address tentative until we get the real flags from the kernel */
         address->flags = IFA_F_TENTATIVE;
 
-        r = set_ensure_allocated(addresses, &address_hash_ops);
-        if (r < 0)
-                return r;
-
-        r = set_put(*addresses, address);
+        r = set_ensure_put(addresses, &address_hash_ops, address);
         if (r < 0)
                 return r;
         if (r == 0)
@@ -262,9 +287,7 @@ static int address_add_internal(Link *link, Set **addresses,
 
         if (ret)
                 *ret = address;
-
-        address = NULL;
-
+        TAKE_PTR(address);
         return 0;
 }
 
@@ -284,11 +307,7 @@ int address_add(Link *link, int family, const union in_addr_union *in_addr, unsi
                         return r;
         } else if (r == 0) {
                 /* Take over a foreign address */
-                r = set_ensure_allocated(&link->addresses, &address_hash_ops);
-                if (r < 0)
-                        return r;
-
-                r = set_put(link->addresses, address);
+                r = set_ensure_put(&link->addresses, &address_hash_ops, address);
                 if (r < 0)
                         return r;
 
@@ -336,11 +355,8 @@ int address_update(
         int r;
 
         assert(address);
+        assert(address->link);
         assert(cinfo);
-        assert_return(address->link, 1);
-
-        if (IN_SET(address->link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
-                return 1;
 
         ready = address_is_ready(address);
 
@@ -348,18 +364,27 @@ int address_update(
         address->scope = scope;
         address->cinfo = *cinfo;
 
+        if (IN_SET(address->link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+                return 0;
+
         link_update_operstate(address->link, true);
         link_check_ready(address->link);
 
-        if (!ready &&
-            address_is_ready(address) &&
-            address->family == AF_INET6 &&
-            in_addr_is_link_local(AF_INET6, &address->in_addr) > 0 &&
-            in_addr_is_null(AF_INET6, (const union in_addr_union*) &address->link->ipv6ll_address) > 0) {
+        if (!ready && address_is_ready(address)) {
+                if (address->callback) {
+                        r = address->callback(address);
+                        if (r < 0)
+                                return r;
+                }
 
-                r = link_ipv6ll_gained(address->link, &address->in_addr.in6);
-                if (r < 0)
-                        return r;
+                if (address->family == AF_INET6 &&
+                    in_addr_is_link_local(AF_INET6, &address->in_addr) > 0 &&
+                    IN6_IS_ADDR_UNSPECIFIED(&address->link->ipv6ll_address) > 0) {
+
+                        r = link_ipv6ll_gained(address->link, &address->in_addr.in6);
+                        if (r < 0)
+                                return r;
+                }
         }
 
         return 0;
@@ -423,6 +448,32 @@ int address_get(Link *link,
         return -ENOENT;
 }
 
+static bool address_exists_internal(Set *addresses, int family, const union in_addr_union *in_addr) {
+        Address *address;
+        Iterator i;
+
+        SET_FOREACH(address, addresses, i) {
+                if (address->family != family)
+                        continue;
+                if (in_addr_equal(address->family, &address->in_addr, in_addr))
+                        return true;
+        }
+
+        return false;
+}
+
+bool address_exists(Link *link, int family, const union in_addr_union *in_addr) {
+        assert(link);
+        assert(IN_SET(family, AF_INET, AF_INET6));
+        assert(in_addr);
+
+        if (address_exists_internal(link->addresses, family, in_addr))
+                return true;
+        if (address_exists_internal(link->addresses_foreign, family, in_addr))
+                return true;
+        return false;
+}
+
 static int address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
         int r;
 
@@ -436,6 +487,8 @@ static int address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link
         r = sd_netlink_message_get_errno(m);
         if (r < 0 && r != -EADDRNOTAVAIL)
                 log_link_message_warning_errno(link, m, r, "Could not drop address");
+        else
+                (void) manager_rtnl_process_address(rtnl, m, link->manager);
 
         return 1;
 }
@@ -550,9 +603,11 @@ int address_configure(
                 Address *address,
                 Link *link,
                 link_netlink_message_handler_t callback,
-                bool update) {
+                bool update,
+                Address **ret) {
 
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
+        Address *a;
         int r;
 
         assert(address);
@@ -573,6 +628,13 @@ int address_configure(
         if (r < 0)
                 return log_link_error_errno(link, r, "Failed to acquire an address from pool: %m");
 
+        if (DEBUG_LOGGING) {
+                _cleanup_free_ char *str = NULL;
+
+                (void) in_addr_to_string(address->family, &address->in_addr, &str);
+                log_link_debug(link, "%s address: %s", update ? "Updating" : "Configuring", strna(str));
+        }
+
         if (update)
                 r = sd_rtnl_message_new_addr_update(link->manager->rtnl, &req,
                                                     link->ifindex, address->family);
@@ -654,9 +716,9 @@ int address_configure(
         link_ref(link);
 
         if (address->family == AF_INET6 && !in_addr_is_null(address->family, &address->in_addr_peer))
-                r = address_add(link, address->family, &address->in_addr_peer, address->prefixlen, NULL);
+                r = address_add(link, address->family, &address->in_addr_peer, address->prefixlen, &a);
         else
-                r = address_add(link, address->family, &address->in_addr, address->prefixlen, NULL);
+                r = address_add(link, address->family, &address->in_addr, address->prefixlen, &a);
         if (r < 0) {
                 address_release(address);
                 return log_link_error_errno(link, r, "Could not add address: %m");
@@ -676,6 +738,9 @@ int address_configure(
                         log_link_warning_errno(link, r, "Failed to start IPv4ACD client, ignoring: %m");
         }
 
+        if (ret)
+                *ret = a;
+
         return 1;
 }
 
@@ -780,18 +845,23 @@ int config_parse_broadcast(
         assert(data);
 
         r = address_new_static(network, filename, section_line, &n);
-        if (r < 0)
-                return r;
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to allocate new address, ignoring assignment: %m");
+                return 0;
+        }
 
         if (n->family == AF_INET6) {
-                log_syntax(unit, LOG_ERR, filename, line, 0,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "Broadcast is not valid for IPv6 addresses, ignoring assignment: %s", rvalue);
                 return 0;
         }
 
         r = in_addr_from_string(AF_INET, rvalue, (union in_addr_union*) &n->broadcast);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Broadcast is invalid, ignoring assignment: %s", rvalue);
                 return 0;
         }
@@ -831,14 +901,18 @@ int config_parse_address(const char *unit,
                 r = address_new_static(network, NULL, 0, &n);
         } else
                 r = address_new_static(network, filename, section_line, &n);
-
-        if (r < 0)
-                return r;
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to allocate new address, ignoring assignment: %m");
+                return 0;
+        }
 
         /* Address=address/prefixlen */
         r = in_addr_prefix_from_string_auto_internal(rvalue, PREFIXLEN_REFUSE, &f, &buffer, &prefixlen);
         if (r == -ENOANO) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "An address '%s' is specified without prefix length. "
                            "The behavior of parsing addresses without prefix length will be changed in the future release. "
                            "Please specify prefix length explicitly.", rvalue);
@@ -846,12 +920,12 @@ int config_parse_address(const char *unit,
                 r = in_addr_prefix_from_string_auto_internal(rvalue, PREFIXLEN_LEGACY, &f, &buffer, &prefixlen);
         }
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "Invalid address '%s', ignoring assignment: %m", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid address '%s', ignoring assignment: %m", rvalue);
                 return 0;
         }
 
         if (n->family != AF_UNSPEC && f != n->family) {
-                log_syntax(unit, LOG_ERR, filename, line, 0, "Address is incompatible, ignoring assignment: %s", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, 0, "Address is incompatible, ignoring assignment: %s", rvalue);
                 return 0;
         }
 
@@ -861,7 +935,7 @@ int config_parse_address(const char *unit,
                  * let's limit the prefix length to 64 or larger. See RFC4193. */
                 if ((f == AF_INET && prefixlen < 8) ||
                     (f == AF_INET6 && prefixlen < 64)) {
-                        log_syntax(unit, LOG_ERR, filename, line, 0,
+                        log_syntax(unit, LOG_WARNING, filename, line, 0,
                                    "Null address with invalid prefixlen='%u', ignoring assignment: %s",
                                    prefixlen, rvalue);
                         return 0;
@@ -907,11 +981,16 @@ int config_parse_label(
         assert(data);
 
         r = address_new_static(network, filename, section_line, &n);
-        if (r < 0)
-                return r;
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to allocate new address, ignoring assignment: %m");
+                return 0;
+        }
 
         if (!address_label_valid(rvalue)) {
-                log_syntax(unit, LOG_ERR, filename, line, 0,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "Interface label is too long or invalid, ignoring assignment: %s", rvalue);
                 return 0;
         }
@@ -936,7 +1015,7 @@ int config_parse_lifetime(const char *unit,
                           void *userdata) {
         Network *network = userdata;
         _cleanup_(address_free_or_set_invalidp) Address *n = NULL;
-        unsigned k;
+        uint32_t k;
         int r;
 
         assert(filename);
@@ -946,8 +1025,13 @@ int config_parse_lifetime(const char *unit,
         assert(data);
 
         r = address_new_static(network, filename, section_line, &n);
-        if (r < 0)
-                return r;
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to allocate new address, ignoring assignment: %m");
+                return 0;
+        }
 
         /* We accept only "forever", "infinity", empty, or "0". */
         if (STR_IN_SET(rvalue, "forever", "infinity", ""))
@@ -955,13 +1039,13 @@ int config_parse_lifetime(const char *unit,
         else if (streq(rvalue, "0"))
                 k = 0;
         else {
-                log_syntax(unit, LOG_ERR, filename, line, 0,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "Invalid PreferredLifetime= value, ignoring: %s", rvalue);
                 return 0;
         }
 
         n->cinfo.ifa_prefered = k;
-        n = NULL;
+        TAKE_PTR(n);
 
         return 0;
 }
@@ -987,12 +1071,17 @@ int config_parse_address_flags(const char *unit,
         assert(data);
 
         r = address_new_static(network, filename, section_line, &n);
-        if (r < 0)
-                return r;
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to allocate new address, ignoring assignment: %m");
+                return 0;
+        }
 
         r = parse_boolean(rvalue);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to parse %s=, ignoring: %s", lvalue, rvalue);
                 return 0;
         }
@@ -1035,8 +1124,13 @@ int config_parse_address_scope(const char *unit,
         assert(data);
 
         r = address_new_static(network, filename, section_line, &n);
-        if (r < 0)
-                return r;
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to allocate new address, ignoring assignment: %m");
+                return 0;
+        }
 
         if (streq(rvalue, "host"))
                 n->scope = RT_SCOPE_HOST;
@@ -1047,7 +1141,7 @@ int config_parse_address_scope(const char *unit,
         else {
                 r = safe_atou8(rvalue , &n->scope);
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
                                    "Could not parse address scope \"%s\", ignoring assignment: %m", rvalue);
                         return 0;
                 }
@@ -1081,8 +1175,13 @@ int config_parse_duplicate_address_detection(
         assert(data);
 
         r = address_new_static(network, filename, section_line, &n);
-        if (r < 0)
-                return r;
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to allocate new address, ignoring assignment: %m");
+                return 0;
+        }
 
         r = parse_boolean(rvalue);
         if (r >= 0) {
@@ -1097,7 +1196,7 @@ int config_parse_duplicate_address_detection(
 
         a = duplicate_address_detection_address_family_from_string(rvalue);
         if (a < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL),
+                log_syntax(unit, LOG_WARNING, filename, line, SYNTHETIC_ERRNO(EINVAL),
                            "Failed to parse %s=, ignoring: %s", lvalue, rvalue);
                 return 0;
         }
index ad2412c75aac673365f2bcdeea012bc142ed374b..3fc9935d16ba7b2e5c7e089e5794762a9bf3ad38 100644 (file)
@@ -20,6 +20,7 @@ typedef struct Address Address;
 typedef struct Network Network;
 typedef struct Link Link;
 typedef struct NetworkConfigSection NetworkConfigSection;
+typedef int (*address_ready_callback_t)(Address *address);
 
 struct Address {
         Network *network;
@@ -47,6 +48,9 @@ struct Address {
         bool autojoin:1;
         AddressFamily duplicate_address_detection;
 
+        /* Called when address become ready */
+        address_ready_callback_t callback;
+
         sd_ipv4acd *acd;
 
         LIST_FIELDS(Address, addresses);
@@ -57,15 +61,18 @@ void address_free(Address *address);
 int address_add_foreign(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret);
 int address_add(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret);
 int address_get(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret);
+bool address_exists(Link *link, int family, const union in_addr_union *in_addr);
 int address_update(Address *address, unsigned char flags, unsigned char scope, const struct ifa_cacheinfo *cinfo);
 int address_drop(Address *address);
-int address_configure(Address *address, Link *link, link_netlink_message_handler_t callback, bool update);
+int address_configure(Address *address, Link *link, link_netlink_message_handler_t callback, bool update, Address **ret);
 int address_remove(Address *address, Link *link, link_netlink_message_handler_t callback);
 bool address_equal(Address *a1, Address *a2);
 bool address_is_ready(const Address *a);
 int address_section_verify(Address *a);
 int configure_ipv4_duplicate_address_detection(Link *link, Address *address);
 
+int generate_ipv6_eui_64_address(Link *link, struct in6_addr *ret);
+
 DEFINE_NETWORK_SECTION_FUNCTIONS(Address, address_free);
 
 extern const struct hash_ops address_hash_ops;
index 41f09287f2b741ca4cf59881829f65b2e7ad4c70..3fc252d211ef8170a9079421ee19e1598af6787a 100644 (file)
@@ -241,7 +241,7 @@ int config_parse_brvlan_vlan(const char *unit, const char *filename,
 
         r = parse_vid_range(rvalue, &vid, &vid_end);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse VLAN, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse VLAN, ignoring: %s", rvalue);
                 return 0;
         }
 
@@ -269,7 +269,7 @@ int config_parse_brvlan_untagged(const char *unit, const char *filename,
 
         r = parse_vid_range(rvalue, &vid, &vid_end);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "Could not parse VLAN: %s", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Could not parse VLAN: %s", rvalue);
                 return 0;
         }
 
index 5087dbbc2ea1a7fb348c7fa5fbf921f5a1440f22..e5504f77381c9d4405466bbd8217ae29e9eea27e 100644 (file)
@@ -7,8 +7,51 @@
 #include "networkd-can.h"
 #include "networkd-link.h"
 #include "networkd-manager.h"
+#include "parse-util.h"
 #include "string-util.h"
 
+#define CAN_TERMINATION_OHM_VALUE 120
+
+int config_parse_can_bitrate(
+                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) {
+
+        uint32_t *br = data;
+        uint64_t sz;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = parse_size(rvalue, 1000, &sz);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse can bitrate '%s', ignoring: %m", rvalue);
+                return 0;
+        }
+
+        /* Linux uses __u32 for bitrates, so the value should not exceed that. */
+        if (sz <= 0 || sz > UINT32_MAX) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "Bit rate out of permitted range 1...4294967295");
+                return 0;
+        }
+
+        *br = (uint32_t) sz;
+
+        return 0;
+}
+
 static int link_up_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
         int r;
 
@@ -69,6 +112,7 @@ static int link_set_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link)
 
 static int link_set_can(Link *link) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
+        struct can_ctrlmode cm = {};
         int r;
 
         assert(link);
@@ -100,11 +144,6 @@ static int link_set_can(Link *link) {
                         .sample_point = link->network->can_sample_point,
                 };
 
-                if (link->network->can_bitrate > UINT32_MAX) {
-                        log_link_error(link, "bitrate (%" PRIu64 ") too big.", link->network->can_bitrate);
-                        return -ERANGE;
-                }
-
                 log_link_debug(link, "Setting bitrate = %d bit/s", bt.bitrate);
                 if (link->network->can_sample_point > 0)
                         log_link_debug(link, "Setting sample point = %d.%d%%", bt.sample_point / 10, bt.sample_point % 10);
@@ -116,6 +155,35 @@ static int link_set_can(Link *link) {
                         return log_link_error_errno(link, r, "Could not append IFLA_CAN_BITTIMING attribute: %m");
         }
 
+        if (link->network->can_data_bitrate > 0 || link->network->can_data_sample_point > 0) {
+                struct can_bittiming bt = {
+                        .bitrate = link->network->can_data_bitrate,
+                        .sample_point = link->network->can_data_sample_point,
+                };
+
+                log_link_debug(link, "Setting data bitrate = %d bit/s", bt.bitrate);
+                if (link->network->can_data_sample_point > 0)
+                        log_link_debug(link, "Setting data sample point = %d.%d%%", bt.sample_point / 10, bt.sample_point % 10);
+                else
+                        log_link_debug(link, "Using default data sample point");
+
+                r = sd_netlink_message_append_data(m, IFLA_CAN_DATA_BITTIMING, &bt, sizeof(bt));
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append IFLA_CAN_DATA_BITTIMING attribute: %m");
+        }
+
+        if (link->network->can_fd_mode >= 0) {
+                cm.mask |= CAN_CTRLMODE_FD;
+                SET_FLAG(cm.flags, CAN_CTRLMODE_FD, link->network->can_fd_mode > 0);
+                log_link_debug(link, "%sabling FD mode", link->network->can_fd_mode > 0 ? "En" : "Dis");
+        }
+
+        if (link->network->can_non_iso >= 0) {
+                cm.mask |= CAN_CTRLMODE_FD_NON_ISO;
+                SET_FLAG(cm.flags, CAN_CTRLMODE_FD_NON_ISO, link->network->can_non_iso > 0);
+                log_link_debug(link, "%sabling FD non-ISO mode", link->network->can_non_iso > 0 ? "En" : "Dis");
+        }
+
         if (link->network->can_restart_us > 0) {
                 char time_string[FORMAT_TIMESPAN_MAX];
                 uint64_t restart_ms;
@@ -140,18 +208,34 @@ static int link_set_can(Link *link) {
         }
 
         if (link->network->can_triple_sampling >= 0) {
-                struct can_ctrlmode cm = {
-                        .mask = CAN_CTRLMODE_3_SAMPLES,
-                        .flags = link->network->can_triple_sampling ? CAN_CTRLMODE_3_SAMPLES : 0,
-                };
-
+                cm.mask |= CAN_CTRLMODE_3_SAMPLES;
+                SET_FLAG(cm.flags, CAN_CTRLMODE_3_SAMPLES, link->network->can_triple_sampling);
                 log_link_debug(link, "%sabling triple-sampling", link->network->can_triple_sampling ? "En" : "Dis");
+        }
 
+        if (link->network->can_listen_only >= 0) {
+                cm.mask |= CAN_CTRLMODE_LISTENONLY;
+                SET_FLAG(cm.flags, CAN_CTRLMODE_LISTENONLY, link->network->can_listen_only);
+                log_link_debug(link, "%sabling listen-only mode", link->network->can_listen_only ? "En" : "Dis");
+        }
+
+        if (cm.mask != 0) {
                 r = sd_netlink_message_append_data(m, IFLA_CAN_CTRLMODE, &cm, sizeof(cm));
                 if (r < 0)
                         return log_link_error_errno(link, r, "Could not append IFLA_CAN_CTRLMODE attribute: %m");
         }
 
+        if (link->network->can_termination >= 0) {
+
+                log_link_debug(link, "%sabling can-termination", link->network->can_termination ? "En" : "Dis");
+
+                r = sd_netlink_message_append_u16(m, IFLA_CAN_TERMINATION,
+                                link->network->can_termination ? CAN_TERMINATION_OHM_VALUE : 0);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append IFLA_CAN_TERMINATION attribute: %m");
+
+        }
+
         r = sd_netlink_message_close_container(m);
         if (r < 0)
                 return log_link_error_errno(link, r, "Failed to close netlink container: %m");
index c744bdfea72c9a8c3108df4535be357180f31b72..30e99b189d3def7491bf873991c6b0ce1d076de9 100644 (file)
@@ -1,6 +1,10 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
+#include "conf-parser.h"
+
 typedef struct Link Link;
 
 int link_configure_can(Link *link);
+
+CONFIG_PARSER_PROTOTYPE(config_parse_can_bitrate);
index 350fea634c6741056dbd80267ba0ff941d4dbebc..233ef9f4f640c8cac3b96c78238e72f18f4f59fd 100644 (file)
@@ -23,11 +23,15 @@ int manager_parse_config_file(Manager *m) {
 
         assert(m);
 
-        r = config_parse_many_nulstr(PKGSYSCONFDIR "/networkd.conf",
-                                     CONF_PATHS_NULSTR("systemd/networkd.conf.d"),
-                                     "Network\0DHCP\0",
-                                     config_item_perf_lookup, networkd_gperf_lookup,
-                                     CONFIG_PARSE_WARN, m);
+        r = config_parse_many_nulstr(
+                        PKGSYSCONFDIR "/networkd.conf",
+                        CONF_PATHS_NULSTR("systemd/networkd.conf.d"),
+                        "Network\0"
+                        "DHCP\0",
+                        config_item_perf_lookup, networkd_gperf_lookup,
+                        CONFIG_PARSE_WARN,
+                        m,
+                        NULL);
         if (r < 0)
                 return r;
 
@@ -140,26 +144,29 @@ int config_parse_duid_rawdata(
         assert(ret);
 
         /* RawData contains DUID in format "NN:NN:NN..." */
-        for (;;) {
+        for (const char *p = rvalue;;) {
                 int n1, n2, len, r;
                 uint32_t byte;
                 _cleanup_free_ char *cbyte = NULL;
 
-                r = extract_first_word(&rvalue, &cbyte, ":", 0);
+                r = extract_first_word(&p, &cbyte, ":", 0);
+                if (r == -ENOMEM)
+                        return log_oom();
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r, "Failed to read DUID, ignoring assignment: %s.", rvalue);
+                        log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to read DUID, ignoring assignment: %s.", rvalue);
                         return 0;
                 }
                 if (r == 0)
                         break;
+
                 if (count >= MAX_DUID_LEN) {
-                        log_syntax(unit, LOG_ERR, filename, line, 0, "Max DUID length exceeded, ignoring assignment: %s.", rvalue);
+                        log_syntax(unit, LOG_WARNING, filename, line, 0, "Max DUID length exceeded, ignoring assignment: %s.", rvalue);
                         return 0;
                 }
 
                 len = strlen(cbyte);
                 if (!IN_SET(len, 1, 2)) {
-                        log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid length - DUID byte: %s, ignoring assignment: %s.", cbyte, rvalue);
+                        log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid length - DUID byte: %s, ignoring assignment: %s.", cbyte, rvalue);
                         return 0;
                 }
                 n1 = unhexchar(cbyte[0]);
@@ -169,7 +176,7 @@ int config_parse_duid_rawdata(
                         n2 = 0;
 
                 if (n1 < 0 || n2 < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid DUID byte: %s. Ignoring assignment: %s.", cbyte, rvalue);
+                        log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid DUID byte: %s. Ignoring assignment: %s.", cbyte, rvalue);
                         return 0;
                 }
 
index 8664d8cdc0d43782def4aa85ac2f7060e7c7ab6e..ecf9bcea85728f1fa6d7b261bd8bcc01b00da889 100644 (file)
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
 #include "dhcp-internal.h"
+#include "dhcp6-internal.h"
 #include "escape.h"
 #include "in-addr-util.h"
 #include "networkd-dhcp-common.h"
@@ -47,7 +48,7 @@ int config_parse_dhcp(
                 else if (streq(rvalue, "both"))
                         s = ADDRESS_FAMILY_YES;
                 else {
-                        log_syntax(unit, LOG_ERR, filename, line, 0,
+                        log_syntax(unit, LOG_WARNING, filename, line, 0,
                                    "Failed to parse DHCP option, ignoring: %s", rvalue);
                         return 0;
                 }
@@ -61,7 +62,7 @@ int config_parse_dhcp(
         return 0;
 }
 
-int config_parse_dhcp_use_dns(
+int config_parse_dhcp_route_metric(
                 const char* unit,
                 const char *filename,
                 unsigned line,
@@ -74,6 +75,7 @@ int config_parse_dhcp_use_dns(
                 void *userdata) {
 
         Network *network = data;
+        uint32_t metric;
         int r;
 
         assert(filename);
@@ -81,20 +83,30 @@ int config_parse_dhcp_use_dns(
         assert(rvalue);
         assert(data);
 
-        r = parse_boolean(rvalue);
+        r = safe_atou32(rvalue, &metric);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
-                           "Failed to parse UseDNS=%s, ignoring assignment: %m", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse RouteMetric=%s, ignoring assignment: %m", rvalue);
                 return 0;
         }
 
-        network->dhcp_use_dns = r;
-        network->dhcp6_use_dns = r;
+        if (streq_ptr(section, "DHCPv4")) {
+                network->dhcp_route_metric = metric;
+                network->dhcp_route_metric_set = true;
+        } else if (streq_ptr(section, "DHCPv6")) {
+                network->dhcp6_route_metric = metric;
+                network->dhcp6_route_metric_set = true;
+        } else { /* [DHCP] section */
+                if (!network->dhcp_route_metric_set)
+                        network->dhcp_route_metric = metric;
+                if (!network->dhcp6_route_metric_set)
+                        network->dhcp6_route_metric = metric;
+        }
 
         return 0;
 }
 
-int config_parse_dhcp_use_sip(
+int config_parse_dhcp_use_dns(
                 const char* unit,
                 const char *filename,
                 unsigned line,
@@ -116,12 +128,23 @@ int config_parse_dhcp_use_sip(
 
         r = parse_boolean(rvalue);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
-                           "Failed to parse UseSIP=%s, ignoring assignment: %m", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse UseDNS=%s, ignoring assignment: %m", rvalue);
                 return 0;
         }
 
-        network->dhcp_use_sip = r;
+        if (streq_ptr(section, "DHCPv4")) {
+                network->dhcp_use_dns = r;
+                network->dhcp_use_dns_set = true;
+        } else if (streq_ptr(section, "DHCPv6")) {
+                network->dhcp6_use_dns = r;
+                network->dhcp6_use_dns_set = true;
+        } else { /* [DHCP] section */
+                if (!network->dhcp_use_dns_set)
+                        network->dhcp_use_dns = r;
+                if (!network->dhcp6_use_dns_set)
+                        network->dhcp6_use_dns = r;
+        }
 
         return 0;
 }
@@ -148,13 +171,23 @@ int config_parse_dhcp_use_ntp(
 
         r = parse_boolean(rvalue);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to parse UseNTP=%s, ignoring assignment: %m", rvalue);
                 return 0;
         }
 
-        network->dhcp_use_ntp = r;
-        network->dhcp6_use_ntp = r;
+        if (streq_ptr(section, "DHCPv4")) {
+                network->dhcp_use_ntp = r;
+                network->dhcp_use_ntp_set = true;
+        } else if (streq_ptr(section, "DHCPv6")) {
+                network->dhcp6_use_ntp = r;
+                network->dhcp6_use_ntp_set = true;
+        } else { /* [DHCP] section */
+                if (!network->dhcp_use_ntp_set)
+                        network->dhcp_use_ntp = r;
+                if (!network->dhcp6_use_ntp_set)
+                        network->dhcp6_use_ntp = r;
+        }
 
         return 0;
 }
@@ -182,7 +215,7 @@ int config_parse_section_route_table(
 
         r = safe_atou32(rvalue, &rt);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to parse RouteTable=%s, ignoring assignment: %m", rvalue);
                 return 0;
         }
@@ -219,7 +252,7 @@ int config_parse_iaid(const char *unit,
 
         r = safe_atou32(rvalue, &iaid);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Unable to read IAID, ignoring assignment: %s", rvalue);
                 return 0;
         }
@@ -230,8 +263,8 @@ int config_parse_iaid(const char *unit,
         return 0;
 }
 
-int config_parse_dhcp6_pd_hint(
-                const charunit,
+int config_parse_dhcp_user_class(
+                const char *unit,
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -242,27 +275,103 @@ int config_parse_dhcp6_pd_hint(
                 void *data,
                 void *userdata) {
 
-        Network *network = data;
+        char ***l = data;
         int r;
 
-        assert(filename);
+        assert(l);
         assert(lvalue);
         assert(rvalue);
-        assert(data);
 
-        r = in_addr_prefix_from_string(rvalue, AF_INET6, (union in_addr_union *) &network->dhcp6_pd_address, &network->dhcp6_pd_length);
-        if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse PrefixDelegationHint=%s, ignoring assignment", rvalue);
+        if (isempty(rvalue)) {
+                *l = strv_free(*l);
                 return 0;
         }
 
-        if (network->dhcp6_pd_length < 1 || network->dhcp6_pd_length > 128) {
-                log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid prefix length='%d', ignoring assignment", network->dhcp6_pd_length);
-                network->dhcp6_pd_length = 0;
+        for (const char *p = rvalue;;) {
+                _cleanup_free_ char *w = NULL;
+
+                r = extract_first_word(&p, &w, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
+                if (r == -ENOMEM)
+                        return log_oom();
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to split user classes option, ignoring: %s", rvalue);
+                        return 0;
+                }
+                if (r == 0)
+                        return 0;
+
+                if (ltype == AF_INET) {
+                        if (strlen(w) > UINT8_MAX) {
+                                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                                           "%s length is not in the range 1-255, ignoring.", w);
+                                continue;
+                        }
+                } else {
+                        if (strlen(w) > UINT16_MAX) {
+                                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                                           "%s length is not in the range 1-65535, ignoring.", w);
+                                continue;
+                        }
+                }
+
+                r = strv_push(l, w);
+                if (r < 0)
+                        return log_oom();
+
+                w = NULL;
+        }
+}
+
+int config_parse_dhcp_vendor_class(
+                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) {
+        char ***l = data;
+        int r;
+
+        assert(l);
+        assert(lvalue);
+        assert(rvalue);
+
+        if (isempty(rvalue)) {
+                *l = strv_free(*l);
                 return 0;
         }
 
-        return 0;
+        for (const char *p = rvalue;;) {
+                _cleanup_free_ char *w = NULL;
+
+                r = extract_first_word(&p, &w, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
+                if (r == -ENOMEM)
+                        return log_oom();
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to split vendor classes option, ignoring: %s", rvalue);
+                        return 0;
+                }
+                if (r == 0)
+                        return 0;
+
+                if (strlen(w) > UINT8_MAX) {
+                        log_syntax(unit, LOG_WARNING, filename, line, 0,
+                                   "%s length is not in the range 1-255, ignoring.", w);
+                        continue;
+                }
+
+                r = strv_push(l, w);
+                if (r < 0)
+                        return log_oom();
+
+                w = NULL;
+        }
 }
 
 int config_parse_dhcp_send_option(
@@ -277,14 +386,15 @@ int config_parse_dhcp_send_option(
                 void *data,
                 void *userdata) {
 
-        _cleanup_(sd_dhcp_option_unrefp) sd_dhcp_option *opt = NULL, *old = NULL;
+        _cleanup_(sd_dhcp_option_unrefp) sd_dhcp_option *opt4 = NULL, *old4 = NULL;
+        _cleanup_(sd_dhcp6_option_unrefp) sd_dhcp6_option *opt6 = NULL, *old6 = NULL;
+        uint32_t uint32_data, enterprise_identifier = 0;
         _cleanup_free_ char *word = NULL, *q = NULL;
         OrderedHashmap **options = data;
+        uint16_t u16, uint16_data;
         union in_addr_union addr;
         DHCPOptionDataType type;
-        uint8_t u, uint8_data;
-        uint16_t uint16_data;
-        uint32_t uint32_data;
+        uint8_t u8, uint8_data;
         const void *udata;
         const char *p;
         ssize_t sz;
@@ -301,40 +411,73 @@ int config_parse_dhcp_send_option(
         }
 
         p = rvalue;
+        if (ltype == AF_INET6 && streq(lvalue, "SendVendorOption")) {
+                r = extract_first_word(&p, &word, ":", 0);
+                if (r == -ENOMEM)
+                        return log_oom();
+                if (r <= 0 || isempty(p)) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Invalid DHCP option, ignoring assignment: %s", rvalue);
+                        return 0;
+                }
+
+                r = safe_atou32(word, &enterprise_identifier);
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to parse DHCPv6 enterprise identifier data, ignoring assignment: %s", p);
+                        return 0;
+                }
+                word = mfree(word);
+        }
+
         r = extract_first_word(&p, &word, ":", 0);
         if (r == -ENOMEM)
                 return log_oom();
-        if (r <= 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+        if (r <= 0 || isempty(p)) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Invalid DHCP option, ignoring assignment: %s", rvalue);
                 return 0;
         }
 
-        r = safe_atou8(word, &u);
-        if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
-                           "Invalid DHCP option, ignoring assignment: %s", rvalue);
-                return 0;
-        }
-        if (u < 1 || u >= 255) {
-                log_syntax(unit, LOG_ERR, filename, line, 0,
-                           "Invalid DHCP option, valid range is 1-254, ignoring assignment: %s", rvalue);
-                return 0;
+        if (ltype == AF_INET6) {
+                r = safe_atou16(word, &u16);
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Invalid DHCP option, ignoring assignment: %s", rvalue);
+                         return 0;
+                }
+                if (u16 < 1 || u16 >= UINT16_MAX) {
+                        log_syntax(unit, LOG_WARNING, filename, line, 0,
+                                   "Invalid DHCP option, valid range is 1-65535, ignoring assignment: %s", rvalue);
+                        return 0;
+                }
+        } else {
+                r = safe_atou8(word, &u8);
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Invalid DHCP option, ignoring assignment: %s", rvalue);
+                         return 0;
+                }
+                if (u8 < 1 || u8 >= UINT8_MAX) {
+                        log_syntax(unit, LOG_WARNING, filename, line, 0,
+                                   "Invalid DHCP option, valid range is 1-254, ignoring assignment: %s", rvalue);
+                        return 0;
+                }
         }
 
         word = mfree(word);
         r = extract_first_word(&p, &word, ":", 0);
         if (r == -ENOMEM)
                 return log_oom();
-        if (r <= 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+        if (r <= 0 || isempty(p)) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Invalid DHCP option, ignoring assignment: %s", rvalue);
                 return 0;
         }
 
         type = dhcp_option_data_type_from_string(word);
         if (type < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, 0,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "Invalid DHCP option data type, ignoring assignment: %s", p);
                 return 0;
         }
@@ -343,8 +486,8 @@ int config_parse_dhcp_send_option(
         case DHCP_OPTION_DATA_UINT8:{
                 r = safe_atou8(p, &uint8_data);
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
-                                   "Failed to parse DHCPv4 uint8 data, ignoring assignment: %s", p);
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to parse DHCP uint8 data, ignoring assignment: %s", p);
                         return 0;
                 }
 
@@ -355,8 +498,8 @@ int config_parse_dhcp_send_option(
         case DHCP_OPTION_DATA_UINT16:{
                 r = safe_atou16(p, &uint16_data);
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
-                                   "Failed to parse DHCPv4 uint16 data, ignoring assignment: %s", p);
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to parse DHCP uint16 data, ignoring assignment: %s", p);
                         return 0;
                 }
 
@@ -367,8 +510,8 @@ int config_parse_dhcp_send_option(
         case DHCP_OPTION_DATA_UINT32: {
                 r = safe_atou32(p, &uint32_data);
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
-                                   "Failed to parse DHCPv4 uint32 data, ignoring assignment: %s", p);
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to parse DHCP uint32 data, ignoring assignment: %s", p);
                         return 0;
                 }
 
@@ -380,8 +523,8 @@ int config_parse_dhcp_send_option(
         case DHCP_OPTION_DATA_IPV4ADDRESS: {
                 r = in_addr_from_string(AF_INET, p, &addr);
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
-                                   "Failed to parse DHCPv4 ipv4address data, ignoring assignment: %s", p);
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to parse DHCP ipv4address data, ignoring assignment: %s", p);
                         return 0;
                 }
 
@@ -389,11 +532,23 @@ int config_parse_dhcp_send_option(
                 sz = sizeof(addr.in.s_addr);
                 break;
         }
+        case DHCP_OPTION_DATA_IPV6ADDRESS: {
+                r = in_addr_from_string(AF_INET6, p, &addr);
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to parse DHCP ipv6address data, ignoring assignment: %s", p);
+                        return 0;
+                }
+
+                udata = &addr.in6;
+                sz = sizeof(addr.in6.s6_addr);
+                break;
+        }
         case DHCP_OPTION_DATA_STRING:
                 sz = cunescape(p, UNESCAPE_ACCEPT_NUL, &q);
                 if (sz < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, sz,
-                                   "Failed to decode DHCPv4 option data, ignoring assignment: %s", p);
+                        log_syntax(unit, LOG_WARNING, filename, line, sz,
+                                   "Failed to decode DHCP option data, ignoring assignment: %s", p);
                 }
 
                 udata = q;
@@ -402,28 +557,117 @@ int config_parse_dhcp_send_option(
                 return -EINVAL;
         }
 
-        r = sd_dhcp_option_new(u, udata, sz, &opt);
-        if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
-                           "Failed to store DHCPv4 option '%s', ignoring assignment: %m", rvalue);
-                return 0;
+        if (ltype == AF_INET6) {
+                r = sd_dhcp6_option_new(u16, udata, sz, enterprise_identifier, &opt6);
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue);
+                        return 0;
+                }
+
+                r = ordered_hashmap_ensure_allocated(options, &dhcp6_option_hash_ops);
+                if (r < 0)
+                        return log_oom();
+
+                /* Overwrite existing option */
+                old6 = ordered_hashmap_get(*options, UINT_TO_PTR(u16));
+                r = ordered_hashmap_replace(*options, UINT_TO_PTR(u16), opt6);
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue);
+                        return 0;
+                }
+                TAKE_PTR(opt6);
+        } else {
+                r = sd_dhcp_option_new(u8, udata, sz, &opt4);
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue);
+                        return 0;
+                }
+
+                r = ordered_hashmap_ensure_allocated(options, &dhcp_option_hash_ops);
+                if (r < 0)
+                        return log_oom();
+
+                /* Overwrite existing option */
+                old4 = ordered_hashmap_get(*options, UINT_TO_PTR(u8));
+                r = ordered_hashmap_replace(*options, UINT_TO_PTR(u8), opt4);
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to store DHCP option '%s', ignoring assignment: %m", rvalue);
+                        return 0;
+                }
+                TAKE_PTR(opt4);
         }
+        return 0;
+}
 
-        r = ordered_hashmap_ensure_allocated(options, &dhcp_option_hash_ops);
-        if (r < 0)
-                return log_oom();
+int config_parse_dhcp_request_options(
+                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 = data;
+        const char *p;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if (isempty(rvalue)) {
+                if (ltype == AF_INET)
+                        network->dhcp_request_options = set_free(network->dhcp_request_options);
+                else
+                        network->dhcp6_request_options = set_free(network->dhcp6_request_options);
 
-        /* Overwrite existing option */
-        old = ordered_hashmap_remove(*options, UINT_TO_PTR(u));
-        r = ordered_hashmap_put(*options, UINT_TO_PTR(u), opt);
-        if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
-                           "Failed to store DHCPv4 option '%s', ignoring assignment: %m", rvalue);
                 return 0;
         }
 
-        TAKE_PTR(opt);
-        return 0;
+        for (p = rvalue;;) {
+                _cleanup_free_ char *n = NULL;
+                uint32_t i;
+
+                r = extract_first_word(&p, &n, NULL, 0);
+                if (r == -ENOMEM)
+                        return log_oom();
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to parse DHCP request option, ignoring assignment: %s",
+                                   rvalue);
+                        return 0;
+                }
+                if (r == 0)
+                        return 0;
+
+                r = safe_atou32(n, &i);
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "DHCP request option is invalid, ignoring assignment: %s", n);
+                        continue;
+                }
+
+                if (i < 1 || i >= UINT8_MAX) {
+                        log_syntax(unit, LOG_WARNING, filename, line, 0,
+                                   "DHCP request option is invalid, valid range is 1-254, ignoring assignment: %s", n);
+                        continue;
+                }
+
+                r = set_ensure_put(ltype == AF_INET ? &network->dhcp_request_options : &network->dhcp6_request_options,
+                                   NULL, UINT32_TO_PTR(i));
+                if (r < 0)
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to store DHCP request option '%s', ignoring assignment: %m", n);
+        }
 }
 
 DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_use_domains, dhcp_use_domains, DHCPUseDomains,
@@ -443,6 +687,7 @@ static const char * const dhcp_option_data_type_table[_DHCP_OPTION_DATA_MAX] = {
         [DHCP_OPTION_DATA_UINT32]      = "uint32",
         [DHCP_OPTION_DATA_STRING]      = "string",
         [DHCP_OPTION_DATA_IPV4ADDRESS] = "ipv4address",
+        [DHCP_OPTION_DATA_IPV6ADDRESS] = "ipv6address",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(dhcp_option_data_type, DHCPOptionDataType);
index 1d6ddbb8cc0b07c6775d5178adb5dd896f22b86e..01400a23858d4cbe200800956560b5f598d43c20 100644 (file)
@@ -21,6 +21,7 @@ typedef enum DHCPOptionDataType {
         DHCP_OPTION_DATA_UINT32,
         DHCP_OPTION_DATA_STRING,
         DHCP_OPTION_DATA_IPV4ADDRESS,
+        DHCP_OPTION_DATA_IPV6ADDRESS,
         _DHCP_OPTION_DATA_MAX,
         _DHCP_OPTION_DATA_INVALID,
 } DHCPOptionDataType;
@@ -41,11 +42,13 @@ const char *dhcp_option_data_type_to_string(DHCPOptionDataType d) _const_;
 DHCPOptionDataType dhcp_option_data_type_from_string(const char *d) _pure_;
 
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_route_metric);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_use_dns);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_use_domains);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_use_ntp);
-CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_use_sip);
 CONFIG_PARSER_PROTOTYPE(config_parse_iaid);
 CONFIG_PARSER_PROTOTYPE(config_parse_section_route_table);
-CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_pd_hint);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_user_class);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_vendor_class);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_send_option);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_request_options);
diff --git a/src/network/networkd-dhcp-server-bus.c b/src/network/networkd-dhcp-server-bus.c
new file mode 100644 (file)
index 0000000..1731d0b
--- /dev/null
@@ -0,0 +1,111 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "sd-dhcp-server.h"
+
+#include "alloc-util.h"
+#include "bus-common-errors.h"
+#include "bus-util.h"
+#include "dhcp-server-internal.h"
+#include "networkd-dhcp-server-bus.h"
+#include "networkd-link-bus.h"
+#include "networkd-manager.h"
+#include "strv.h"
+
+static int property_get_leases(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+        Link *l = userdata;
+        sd_dhcp_server *s;
+        DHCPLease *lease;
+        Iterator i;
+        int r;
+
+        assert(reply);
+        assert(l);
+
+        s = l->dhcp_server;
+        if (!s)
+                return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Link %s has no DHCP server.", l->ifname);
+
+        r = sd_bus_message_open_container(reply, 'a', "(uayayayayt)");
+        if (r < 0)
+                return r;
+
+        HASHMAP_FOREACH(lease, s->leases_by_client_id, i) {
+                r = sd_bus_message_open_container(reply, 'r', "uayayayayt");
+                if (r < 0)
+                        return r;
+
+                r = sd_bus_message_append(reply, "u", (uint32_t)AF_INET);
+                if (r < 0)
+                        return r;
+
+                r = sd_bus_message_append_array(reply, 'y', lease->client_id.data, lease->client_id.length);
+                if (r < 0)
+                        return r;
+
+                r = sd_bus_message_append_array(reply, 'y', &lease->address, sizeof(lease->address));
+                if (r < 0)
+                        return r;
+
+                r = sd_bus_message_append_array(reply, 'y', &lease->gateway, sizeof(lease->gateway));
+                if (r < 0)
+                        return r;
+
+                r = sd_bus_message_append_array(reply, 'y', &lease->chaddr, sizeof(lease->chaddr));
+                if (r < 0)
+                        return r;
+
+                r = sd_bus_message_append_basic(reply, 't', &lease->expiration);
+                if (r < 0)
+                        return r;
+
+                r = sd_bus_message_close_container(reply);
+                if (r < 0)
+                        return r;
+        }
+
+        return sd_bus_message_close_container(reply);
+}
+
+static int dhcp_server_emit_changed(Link *link, const char *property, ...) {
+        _cleanup_free_ char *path = NULL;
+        char **l;
+
+        assert(link);
+
+        path = link_bus_path(link);
+        if (!path)
+                return log_oom();
+
+        l = strv_from_stdarg_alloca(property);
+
+        return sd_bus_emit_properties_changed_strv(
+                        link->manager->bus,
+                        path,
+                        "org.freedesktop.network1.DHCPServer",
+                        l);
+}
+
+void dhcp_server_callback(sd_dhcp_server *s, uint64_t event, void *data) {
+        Link *l = data;
+
+        assert(l);
+
+        if (event & SD_DHCP_SERVER_EVENT_LEASE_CHANGED)
+                (void) dhcp_server_emit_changed(l, "Leases", NULL);
+}
+
+
+const sd_bus_vtable dhcp_server_vtable[] = {
+        SD_BUS_VTABLE_START(0),
+
+        SD_BUS_PROPERTY("Leases", "a(uayayayayt)", property_get_leases, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+
+        SD_BUS_VTABLE_END
+};
diff --git a/src/network/networkd-dhcp-server-bus.h b/src/network/networkd-dhcp-server-bus.h
new file mode 100644 (file)
index 0000000..49164ff
--- /dev/null
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "sd-bus.h"
+#include "networkd-link.h"
+
+extern const sd_bus_vtable dhcp_server_vtable[];
+
+void dhcp_server_callback(sd_dhcp_server *server, uint64_t event, void *data);
index bee75a6930ea690479e9d06b5e4fed00d6ff5928..5129a2e37e2dbce77868bcf1ba184f2c3cc4e9dc 100644 (file)
@@ -2,14 +2,17 @@
 
 #include "sd-dhcp-server.h"
 
+#include "fd-util.h"
+#include "fileio.h"
 #include "networkd-dhcp-server.h"
 #include "networkd-link.h"
 #include "networkd-manager.h"
 #include "networkd-network.h"
 #include "parse-util.h"
-#include "strv.h"
+#include "socket-netlink.h"
 #include "string-table.h"
 #include "string-util.h"
+#include "strv.h"
 
 static Address* link_find_dhcp_server_address(Link *link) {
         Address *address;
@@ -18,117 +21,116 @@ static Address* link_find_dhcp_server_address(Link *link) {
         assert(link->network);
 
         /* The first statically configured address if there is any */
-        LIST_FOREACH(addresses, address, link->network->static_addresses) {
-
-                if (address->family != AF_INET)
-                        continue;
-
-                if (in_addr_is_null(address->family, &address->in_addr))
-                        continue;
-
-                return address;
-        }
+        LIST_FOREACH(addresses, address, link->network->static_addresses)
+                if (address->family == AF_INET &&
+                    !in_addr_is_null(address->family, &address->in_addr))
+                        return address;
 
         /* If that didn't work, find a suitable address we got from the pool */
-        LIST_FOREACH(addresses, address, link->pool_addresses) {
-                if (address->family != AF_INET)
-                        continue;
-
-                return address;
-        }
+        LIST_FOREACH(addresses, address, link->pool_addresses)
+                if (address->family == AF_INET)
+                        return address;
 
         return NULL;
 }
 
-static int link_push_uplink_dns_to_dhcp_server(Link *link, sd_dhcp_server *s) {
+static int link_push_uplink_to_dhcp_server(
+                Link *link,
+                sd_dhcp_lease_server_type what,
+                sd_dhcp_server *s) {
+
         _cleanup_free_ struct in_addr *addresses = NULL;
         size_t n_addresses = 0, n_allocated = 0;
-        unsigned i;
+        bool use_dhcp_lease_data = true;
 
-        log_link_debug(link, "Copying DNS server information from link");
+        assert(link);
 
         if (!link->network)
                 return 0;
+        assert(link->network);
 
-        for (i = 0; i < link->network->n_dns; i++) {
-                struct in_addr ia;
-
-                /* Only look for IPv4 addresses */
-                if (link->network->dns[i].family != AF_INET)
-                        continue;
+        log_link_debug(link, "Copying %s from link", dhcp_lease_server_type_to_string(what));
 
-                ia = link->network->dns[i].address.in;
+        switch (what) {
 
-                /* Never propagate obviously borked data */
-                if (in4_addr_is_null(&ia) || in4_addr_is_localhost(&ia))
-                        continue;
+        case SD_DHCP_LEASE_DNS:
+                /* For DNS we have a special case. We the data configured explicitly locally along with the
+                 * data from the DHCP lease. */
 
-                if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1))
-                        return log_oom();
+                for (unsigned i = 0; i < link->network->n_dns; i++) {
+                        struct in_addr ia;
 
-                addresses[n_addresses++] = ia;
-        }
+                        /* Only look for IPv4 addresses */
+                        if (link->network->dns[i]->family != AF_INET)
+                                continue;
 
-        if (link->network->dhcp_use_dns && link->dhcp_lease) {
-                const struct in_addr *da = NULL;
-                int j, n;
+                        ia = link->network->dns[i]->address.in;
 
-                n = sd_dhcp_lease_get_dns(link->dhcp_lease, &da);
-                if (n > 0) {
+                        /* Never propagate obviously borked data */
+                        if (in4_addr_is_null(&ia) || in4_addr_is_localhost(&ia))
+                                continue;
 
-                        if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + n))
+                        if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1))
                                 return log_oom();
 
-                        for (j = 0; j < n; j++)
-                                if (in4_addr_is_non_local(&da[j]))
-                                        addresses[n_addresses++] = da[j];
+                        addresses[n_addresses++] = ia;
                 }
-        }
 
-        if (n_addresses <= 0)
-                return 0;
+                use_dhcp_lease_data = link->network->dhcp_use_dns;
+                break;
 
-        return sd_dhcp_server_set_dns(s, addresses, n_addresses);
-}
+        case SD_DHCP_LEASE_NTP: {
+                char **i;
 
-static int link_push_uplink_ntp_to_dhcp_server(Link *link, sd_dhcp_server *s) {
-        _cleanup_free_ struct in_addr *addresses = NULL;
-        size_t n_addresses = 0, n_allocated = 0;
-        char **a;
+                /* For NTP things are similar, but for NTP hostnames can be configured too, which we cannot
+                 * propagate via DHCP. Hence let's only propagate those which are IP addresses. */
 
-        if (!link->network)
-                return 0;
+                STRV_FOREACH(i, link->network->ntp) {
+                        union in_addr_union ia;
 
-        log_link_debug(link, "Copying NTP server information from link");
+                        if (in_addr_from_string(AF_INET, *i, &ia) < 0)
+                                continue;
 
-        STRV_FOREACH(a, link->network->ntp) {
-                union in_addr_union ia;
+                        /* Never propagate obviously borked data */
+                        if (in4_addr_is_null(&ia.in) || in4_addr_is_localhost(&ia.in))
+                                continue;
 
-                /* Only look for IPv4 addresses */
-                if (in_addr_from_string(AF_INET, *a, &ia) <= 0)
-                        continue;
+                        if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1))
+                                return log_oom();
 
-                /* Never propagate obviously borked data */
-                if (in4_addr_is_null(&ia.in) || in4_addr_is_localhost(&ia.in))
-                        continue;
+                        addresses[n_addresses++] = ia.in;
+                }
 
-                if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1))
-                        return log_oom();
+                use_dhcp_lease_data = link->network->dhcp_use_ntp;
+                break;
+        }
+
+        case SD_DHCP_LEASE_SIP:
 
-                addresses[n_addresses++] = ia.in;
+                /* For SIP we don't allow explicit, local configuration, but there's control whether to use the data */
+                use_dhcp_lease_data = link->network->dhcp_use_sip;
+                break;
+
+        case SD_DHCP_LEASE_POP3:
+        case SD_DHCP_LEASE_SMTP:
+        case SD_DHCP_LEASE_LPR:
+                /* For the other server types we currently do not allow local configuration of server data,
+                 * since there are typically no local consumers of the data. */
+                break;
+
+        default:
+                assert_not_reached("Unexpected server type");
         }
 
-        if (link->network->dhcp_use_ntp && link->dhcp_lease) {
-                const struct in_addr *da = NULL;
-                int j, n;
+        if (use_dhcp_lease_data && link->dhcp_lease) {
+                const struct in_addr *da;
 
-                n = sd_dhcp_lease_get_ntp(link->dhcp_lease, &da);
+                int n = sd_dhcp_lease_get_servers(link->dhcp_lease, what, &da);
                 if (n > 0) {
-
                         if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + n))
                                 return log_oom();
 
-                        for (j = 0; j < n; j++)
+                        for (int j = 0; j < n; j++)
                                 if (in4_addr_is_non_local(&da[j]))
                                         addresses[n_addresses++] = da[j];
                 }
@@ -137,56 +139,88 @@ static int link_push_uplink_ntp_to_dhcp_server(Link *link, sd_dhcp_server *s) {
         if (n_addresses <= 0)
                 return 0;
 
-        return sd_dhcp_server_set_ntp(s, addresses, n_addresses);
+        return sd_dhcp_server_set_servers(s, what, addresses, n_addresses);
 }
 
-static int link_push_uplink_sip_to_dhcp_server(Link *link, sd_dhcp_server *s) {
-        _cleanup_free_ struct in_addr *addresses = NULL;
-        size_t n_addresses = 0, n_allocated = 0;
-        char **a;
-
-        if (!link->network)
-                return 0;
+static int dhcp4_server_parse_dns_server_string_and_warn(Link *l, const char *string, struct in_addr **addresses, size_t *n_allocated, size_t *n_addresses) {
+        for (;;) {
+                _cleanup_free_ char *word = NULL, *server_name = NULL;
+                union in_addr_union address;
+                int family, r, ifindex = 0;
 
-        log_link_debug(link, "Copying SIP server information from link");
+                r = extract_first_word(&string, &word, NULL, 0);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        break;
 
-        STRV_FOREACH(a, link->network->sip) {
-                union in_addr_union ia;
+                r = in_addr_ifindex_name_from_string_auto(word, &family, &address, &ifindex, &server_name);
+                if (r < 0) {
+                        log_warning_errno(r, "Failed to parse DNS server address '%s', ignoring: %m", word);
+                        continue;
+                }
 
                 /* Only look for IPv4 addresses */
-                if (in_addr_from_string(AF_INET, *a, &ia) <= 0)
+                if (family != AF_INET)
                         continue;
 
                 /* Never propagate obviously borked data */
-                if (in4_addr_is_null(&ia.in) || in4_addr_is_localhost(&ia.in))
+                if (in4_addr_is_null(&address.in) || in4_addr_is_localhost(&address.in))
                         continue;
 
-                if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1))
+                if (!GREEDY_REALLOC(*addresses, *n_allocated, *n_addresses + 1))
                         return log_oom();
 
-                addresses[n_addresses++] = ia.in;
+                (*addresses)[(*n_addresses)++] = address.in;
         }
 
-        if (link->network->dhcp_use_sip && link->dhcp_lease) {
-                const struct in_addr *da = NULL;
-                int j, n;
+        return 0;
+}
 
-                n = sd_dhcp_lease_get_sip(link->dhcp_lease, &da);
-                if (n > 0) {
+static int dhcp4_server_set_dns_from_resolve_conf(Link *link) {
+        _cleanup_free_ struct in_addr *addresses = NULL;
+        size_t n_addresses = 0, n_allocated = 0;
+        _cleanup_fclose_ FILE *f = NULL;
+        int n = 0, r;
 
-                        if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + n))
-                                return log_oom();
+        f = fopen(PRIVATE_UPLINK_RESOLV_CONF, "re");
+        if (!f) {
+                if (errno == ENOENT)
+                        return 0;
 
-                        for (j = 0; j < n; j++)
-                                if (in4_addr_is_non_local(&da[j]))
-                                        addresses[n_addresses++] = da[j];
-                }
+                return log_warning_errno(errno, "Failed to open " PRIVATE_UPLINK_RESOLV_CONF ": %m");
+        }
+
+        for (;;) {
+                _cleanup_free_ char *line = NULL;
+                const char *a;
+                char *l;
+
+                r = read_line(f, LONG_LINE_MAX, &line);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to read " PRIVATE_UPLINK_RESOLV_CONF ": %m");
+                if (r == 0)
+                        break;
+
+                n++;
+
+                l = strstrip(line);
+                if (IN_SET(*l, '#', ';', 0))
+                        continue;
+
+                a = first_word(l, "nameserver");
+                if (!a)
+                        continue;
+
+                r = dhcp4_server_parse_dns_server_string_and_warn(link, a, &addresses, &n_allocated, &n_addresses);
+                if (r < 0)
+                        log_warning_errno(r, "Failed to parse DNS server address '%s', ignoring.", a);
         }
 
         if (n_addresses <= 0)
                 return 0;
 
-        return sd_dhcp_server_set_sip(s, addresses, n_addresses);
+        return sd_dhcp_server_set_dns(link->dhcp_server, addresses, n_addresses);
 }
 
 int dhcp4_server_configure(Link *link) {
@@ -228,57 +262,41 @@ int dhcp4_server_configure(Link *link) {
                         return log_link_error_errno(link, r, "Failed to set default lease time for DHCPv4 server instance: %m");
         }
 
-        if (link->network->dhcp_server_emit_dns) {
-                if (link->network->n_dhcp_server_dns > 0)
-                        r = sd_dhcp_server_set_dns(link->dhcp_server, link->network->dhcp_server_dns, link->network->n_dhcp_server_dns);
-                else {
-                        uplink = manager_find_uplink(link->manager, link);
-                        acquired_uplink = true;
-
-                        if (!uplink) {
-                                log_link_debug(link, "Not emitting DNS server information on link, couldn't find suitable uplink.");
-                                r = 0;
-                        } else
-                                r = link_push_uplink_dns_to_dhcp_server(uplink, link->dhcp_server);
-                }
-                if (r < 0)
-                        log_link_warning_errno(link, r, "Failed to set DNS server for DHCP server, ignoring: %m");
-        }
-
-        if (link->network->dhcp_server_emit_ntp) {
-                if (link->network->n_dhcp_server_ntp > 0)
-                        r = sd_dhcp_server_set_ntp(link->dhcp_server, link->network->dhcp_server_ntp, link->network->n_dhcp_server_ntp);
-                else {
-                        if (!acquired_uplink)
-                                uplink = manager_find_uplink(link->manager, link);
+        for (sd_dhcp_lease_server_type type = 0; type < _SD_DHCP_LEASE_SERVER_TYPE_MAX; type ++) {
 
-                        if (!uplink) {
-                                log_link_debug(link, "Not emitting NTP server information on link, couldn't find suitable uplink.");
-                                r = 0;
-                        } else
-                                r = link_push_uplink_ntp_to_dhcp_server(uplink, link->dhcp_server);
-
-                }
-                if (r < 0)
-                        log_link_warning_errno(link, r, "Failed to set NTP server for DHCP server, ignoring: %m");
-        }
+                if (!link->network->dhcp_server_emit[type].emit)
+                        continue;
 
-        if (link->network->dhcp_server_emit_sip) {
-                if (link->network->n_dhcp_server_sip > 0)
-                        r = sd_dhcp_server_set_sip(link->dhcp_server, link->network->dhcp_server_sip, link->network->n_dhcp_server_sip);
+                if (link->network->dhcp_server_emit[type].n_addresses > 0)
+                        /* Explicitly specified servers to emit */
+                        r = sd_dhcp_server_set_servers(
+                                        link->dhcp_server,
+                                        type,
+                                        link->network->dhcp_server_emit[type].addresses,
+                                        link->network->dhcp_server_emit[type].n_addresses);
                 else {
-                        if (!acquired_uplink)
+                        /* Emission is requested, but nothing explicitly configured. Let's find a suitable upling */
+                        if (!acquired_uplink) {
                                 uplink = manager_find_uplink(link->manager, link);
-
-                        if (!uplink) {
-                                log_link_debug(link, "Not emitting sip server information on link, couldn't find suitable uplink.");
-                                r = 0;
-                        } else
-                                r = link_push_uplink_sip_to_dhcp_server(uplink, link->dhcp_server);
-
+                                acquired_uplink = true;
+                        }
+
+                        if (uplink && uplink->network)
+                                r = link_push_uplink_to_dhcp_server(uplink, type, link->dhcp_server);
+                        else if (type == SD_DHCP_LEASE_DNS)
+                                r = dhcp4_server_set_dns_from_resolve_conf(link);
+                        else {
+                                log_link_debug(link,
+                                               "Not emitting %s on link, couldn't find suitable uplink.",
+                                               dhcp_lease_server_type_to_string(type));
+                                continue;
+                        }
                 }
+
                 if (r < 0)
-                        log_link_warning_errno(link, r, "Failed to set SIP server for DHCP server, ignoring: %m");
+                        log_link_warning_errno(link, r,
+                                               "Failed to set %s for DHCP server, ignoring: %m",
+                                               dhcp_lease_server_type_to_string(type));
         }
 
         r = sd_dhcp_server_set_emit_router(link->dhcp_server, link->network->dhcp_server_emit_router);
@@ -312,6 +330,14 @@ int dhcp4_server_configure(Link *link) {
                         return log_link_error_errno(link, r, "Failed to set DHCPv4 option: %m");
         }
 
+        ORDERED_HASHMAP_FOREACH(p, link->network->dhcp_server_send_vendor_options, i) {
+                r = sd_dhcp_server_add_vendor_option(link->dhcp_server, p);
+                if (r == -EEXIST)
+                        continue;
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Failed to set DHCPv4 option: %m");
+        }
+
         if (!sd_dhcp_server_is_running(link->dhcp_server)) {
                 r = sd_dhcp_server_start(link->dhcp_server);
                 if (r < 0)
@@ -321,7 +347,7 @@ int dhcp4_server_configure(Link *link) {
         return 0;
 }
 
-int config_parse_dhcp_server_dns(
+int config_parse_dhcp_server_emit(
                 const char *unit,
                 const char *filename,
                 unsigned line,
@@ -333,130 +359,21 @@ int config_parse_dhcp_server_dns(
                 void *data,
                 void *userdata) {
 
-        Network *n = data;
-        const char *p = rvalue;
-        int r;
+        NetworkDHCPServerEmitAddress *emit = data;
 
-        assert(filename);
-        assert(lvalue);
+        assert(emit);
         assert(rvalue);
 
-        for (;;) {
-                _cleanup_free_ char *w = NULL;
-                union in_addr_union a;
-                struct in_addr *m;
-
-                r = extract_first_word(&p, &w, NULL, 0);
-                if (r == -ENOMEM)
-                        return log_oom();
-                if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
-                                   "Failed to extract word, ignoring: %s", rvalue);
-                        return 0;
-                }
-                if (r == 0)
-                        break;
-
-                r = in_addr_from_string(AF_INET, w, &a);
-                if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
-                                   "Failed to parse DNS server address '%s', ignoring assignment: %m", w);
-                        continue;
-                }
-
-                m = reallocarray(n->dhcp_server_dns, n->n_dhcp_server_dns + 1, sizeof(struct in_addr));
-                if (!m)
-                        return log_oom();
-
-                m[n->n_dhcp_server_dns++] = a.in;
-                n->dhcp_server_dns = m;
-        }
-
-        return 0;
-}
-
-int config_parse_dhcp_server_ntp(
-                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 *n = data;
-        const char *p = rvalue;
-        int r;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-
-        for (;;) {
-                _cleanup_free_ char *w = NULL;
-                union in_addr_union a;
-                struct in_addr *m;
-
-                r = extract_first_word(&p, &w, NULL, 0);
-                if (r == -ENOMEM)
-                        return log_oom();
-                if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
-                                   "Failed to extract word, ignoring: %s", rvalue);
-                        return 0;
-                }
-                if (r == 0)
-                        return 0;
-
-                r = in_addr_from_string(AF_INET, w, &a);
-                if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
-                                   "Failed to parse NTP server address '%s', ignoring: %m", w);
-                        continue;
-                }
-
-                m = reallocarray(n->dhcp_server_ntp, n->n_dhcp_server_ntp + 1, sizeof(struct in_addr));
-                if (!m)
-                        return log_oom();
-
-                m[n->n_dhcp_server_ntp++] = a.in;
-                n->dhcp_server_ntp = m;
-        }
-}
-
-int config_parse_dhcp_server_sip(
-                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 *n = data;
-        const char *p = rvalue;
-        int r;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-
-        for (;;) {
+        for (const char *p = rvalue;;) {
                 _cleanup_free_ char *w = NULL;
                 union in_addr_union a;
-                struct in_addr *m;
+                int r;
 
                 r = extract_first_word(&p, &w, NULL, 0);
                 if (r == -ENOMEM)
                         return log_oom();
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
                                    "Failed to extract word, ignoring: %s", rvalue);
                         return 0;
                 }
@@ -465,16 +382,16 @@ int config_parse_dhcp_server_sip(
 
                 r = in_addr_from_string(AF_INET, w, &a);
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
-                                   "Failed to parse SIP server address '%s', ignoring: %m", w);
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to parse %s= address '%s', ignoring: %m", lvalue, w);
                         continue;
                 }
 
-                m = reallocarray(n->dhcp_server_sip, n->n_dhcp_server_sip + 1, sizeof(struct in_addr));
+                struct in_addr *m = reallocarray(emit->addresses, emit->n_addresses + 1, sizeof(struct in_addr));
                 if (!m)
                         return log_oom();
 
-                m[n->n_dhcp_server_sip++] = a.in;
-                n->dhcp_server_sip = m;
+                emit->addresses = m;
+                emit->addresses[emit->n_addresses++] = a.in;
         }
 }
index c90d48ec002bb2104a33e91300f8607921afe7a1..2250a30af415794464a1e68fc924d7a0baa624ad 100644 (file)
@@ -9,6 +9,4 @@ typedef struct Link Link;
 
 int dhcp4_server_configure(Link *link);
 
-CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_dns);
-CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_ntp);
-CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_sip);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_emit);
index 13e3e32f40e8c6add46c852127b8f29d9beb6e5a..722e0d213bb045cb7dcf1b0566e9ada283885c99 100644 (file)
@@ -5,6 +5,7 @@
 #include <linux/if.h>
 #include <linux/if_arp.h>
 
+#include "escape.h"
 #include "alloc-util.h"
 #include "dhcp-client-internal.h"
 #include "hostname-util.h"
 #include "string-table.h"
 #include "string-util.h"
 #include "sysctl-util.h"
+#include "web-util.h"
 
-static int dhcp_remove_routes(Link *link, sd_dhcp_lease *lease, const struct in_addr *address, bool remove_all);
-static int dhcp_remove_router(Link *link, sd_dhcp_lease *lease, const struct in_addr *address, bool remove_all);
-static int dhcp_remove_dns_routes(Link *link, sd_dhcp_lease *lease, const struct in_addr *address, bool remove_all);
-static int dhcp_remove_address(Link *link, sd_dhcp_lease *lease, const struct in_addr *address, link_netlink_message_handler_t callback);
-static int dhcp_remove_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link);
-static int dhcp_lease_renew(sd_dhcp_client *client, Link *link);
+static int dhcp4_update_address(Link *link, bool announce);
+static int dhcp4_remove_all(Link *link);
+static int dhcp4_release_old_lease(Link *link, bool force);
 
-void dhcp4_release_old_lease(Link *link) {
-        struct in_addr address = {}, address_old = {};
+static int dhcp4_address_callback(Address *address) {
+        assert(address);
+        assert(address->link);
 
-        assert(link);
+        /* Do not call this callback again. */
+        address->callback = NULL;
 
-        if (!link->dhcp_lease_old)
-                return;
+        return dhcp4_release_old_lease(address->link, true);
+}
 
-        assert(link->dhcp_lease);
+static int dhcp4_release_old_lease(Link *link, bool force) {
+        Route *route;
+        Iterator i;
+        int k, r = 0;
 
-        (void) sd_dhcp_lease_get_address(link->dhcp_lease_old, &address_old);
-        (void) sd_dhcp_lease_get_address(link->dhcp_lease, &address);
+        assert(link);
 
-        (void) dhcp_remove_routes(link, link->dhcp_lease_old, &address_old, false);
-        (void) dhcp_remove_router(link, link->dhcp_lease_old, &address_old, false);
-        (void) dhcp_remove_dns_routes(link, link->dhcp_lease_old, &address_old, false);
+        if (!link->dhcp_address_old && set_isempty(link->dhcp_routes_old))
+                return 0;
 
-        if (!in4_addr_equal(&address_old, &address))
-                (void) dhcp_remove_address(link, link->dhcp_lease_old, &address_old, NULL);
+        if (!force && (link->dhcp_address && !address_is_ready(link->dhcp_address))) {
+                log_link_debug(link, "New DHCPv4 address is not ready. The old lease will be removed later.");
+                link->dhcp_address->callback = dhcp4_address_callback;
+                return 0;
+        }
+
+        log_link_debug(link, "Removing old DHCPv4 address and routes.");
 
-        link->dhcp_lease_old = sd_dhcp_lease_unref(link->dhcp_lease_old);
         link_dirty(link);
+
+        SET_FOREACH(route, link->dhcp_routes_old, i) {
+                k = route_remove(route, link, NULL);
+                if (k < 0)
+                        r = k;
+        }
+
+        if (link->dhcp_address_old) {
+                k = address_remove(link->dhcp_address_old, link, NULL);
+                if (k < 0)
+                        r = k;
+        }
+
+        return r;
 }
 
 static void dhcp4_check_ready(Link *link) {
-        if (link->dhcp4_messages == 0) {
-                link->dhcp4_configured = true;
-                /* New address and routes are configured now. Let's release old lease. */
-                dhcp4_release_old_lease(link);
-                link_check_ready(link);
+        int r;
+
+        if (link->network->dhcp_send_decline && !link->dhcp4_address_bind)
+                return;
+
+        if (link->dhcp4_messages > 0)
+                return;
+
+        link->dhcp4_configured = true;
+
+        /* New address and routes are configured now. Let's release old lease. */
+        r = dhcp4_release_old_lease(link, false);
+        if (r < 0) {
+                link_enter_failed(link);
+                return;
         }
+
+        link_check_ready(link);
 }
 
 static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
@@ -84,25 +116,18 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *li
                 return 1;
         }
 
-        if (link->dhcp4_messages == 0) {
-                if (link->dhcp4_route_failed) {
-                        struct in_addr address = {};
+        if (link->dhcp4_messages == 0 && link->dhcp4_route_failed) {
+                link->dhcp4_route_failed = false;
+                link->dhcp4_route_retrying = true;
 
-                        link->dhcp4_route_failed = false;
-                        link->dhcp4_route_retrying = true;
-
-                        (void) sd_dhcp_lease_get_address(link->dhcp_lease, &address);
-                        (void) dhcp_remove_routes(link, link->dhcp_lease, &address, true);
-                        (void) dhcp_remove_router(link, link->dhcp_lease, &address, true);
-                        (void) dhcp_remove_dns_routes(link, link->dhcp_lease, &address, true);
-                        (void) dhcp_remove_address(link, link->dhcp_lease, &address, dhcp_remove_address_handler);
-
-                        return 1;
-                }
-                if (!link->network->dhcp_send_decline)
-                        dhcp4_check_ready(link);
+                r = dhcp4_remove_all(link);
+                if (r < 0)
+                        link_enter_failed(link);
+                return 1;
         }
 
+        dhcp4_check_ready(link);
+
         return 1;
 }
 
@@ -125,27 +150,25 @@ static bool link_prefixroute(Link *link) {
                 link->manager->dhcp4_prefix_root_cannot_set_table;
 }
 
-static int dhcp_route_configure(Route **route, Link *link) {
+static int dhcp_route_configure(Route *route, Link *link) {
+        Route *ret;
         int r;
 
         assert(route);
-        assert(*route);
         assert(link);
 
-        if (set_contains(link->dhcp_routes, *route))
-                return 0;
-
-        r = route_configure(*route, link, dhcp4_route_handler);
-        if (r <= 0)
-                return r;
+        r = route_configure(route, link, dhcp4_route_handler, &ret);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Failed to set DHCPv4 route: %m");
 
         link->dhcp4_messages++;
 
-        r = set_put(link->dhcp_routes, *route);
+        r = set_ensure_put(&link->dhcp_routes, &route_hash_ops, ret);
         if (r < 0)
-                return r;
+                return log_link_error_errno(link, r, "Failed to store DHCPv4 route: %m");
+
+        (void) set_remove(link->dhcp_routes_old, ret);
 
-        TAKE_PTR(*route);
         return 0;
 }
 
@@ -175,7 +198,7 @@ static int link_set_dns_routes(Link *link, const struct in_addr *address) {
 
                 r = route_new(&route);
                 if (r < 0)
-                        return log_link_error_errno(link, r,  "Could not allocate route: %m");
+                        return log_link_error_errno(link, r, "Could not allocate route: %m");
 
                 /* Set routes to DNS servers. */
 
@@ -188,7 +211,7 @@ static int link_set_dns_routes(Link *link, const struct in_addr *address) {
                 route->priority = link->network->dhcp_route_metric;
                 route->table = table;
 
-                r = dhcp_route_configure(&route, link);
+                r = dhcp_route_configure(route, link);
                 if (r < 0)
                         return log_link_error_errno(link, r, "Could not set route to DNS server: %m");
         }
@@ -232,6 +255,7 @@ static int link_set_dhcp_routes(Link *link) {
         struct in_addr address;
         int r, n, i;
         uint32_t table;
+        Route *rt;
 
         assert(link);
 
@@ -246,12 +270,11 @@ static int link_set_dhcp_routes(Link *link) {
                  * the addresses now, let's not configure the routes either. */
                 return 0;
 
-        r = set_ensure_allocated(&link->dhcp_routes, &route_hash_ops);
-        if (r < 0)
-                return log_oom();
-
-        /* Clear old entries in case the set was already allocated */
-        set_clear(link->dhcp_routes);
+        while ((rt = set_steal_first(link->dhcp_routes))) {
+                r = set_ensure_put(&link->dhcp_routes_old, &route_hash_ops, rt);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Failed to store old DHCPv4 route: %m");
+        }
 
         table = link_get_dhcp_route_table(link);
 
@@ -264,9 +287,9 @@ static int link_set_dhcp_routes(Link *link) {
 
                 r = dhcp_prefix_route_from_lease(link->dhcp_lease, table, &address, &prefix_route);
                 if (r < 0)
-                        return log_link_error_errno(link, r,  "Could not create prefix route: %m");
+                        return log_link_error_errno(link, r, "Could not create prefix route: %m");
 
-                r = dhcp_route_configure(&prefix_route, link);
+                r = dhcp_route_configure(prefix_route, link);
                 if (r < 0)
                         return log_link_error_errno(link, r, "Could not set prefix route: %m");
         }
@@ -317,363 +340,215 @@ static int link_set_dhcp_routes(Link *link) {
                         if (set_contains(link->dhcp_routes, route))
                                 continue;
 
-                        r = dhcp_route_configure(&route, link);
+                        r = dhcp_route_configure(route, link);
                         if (r < 0)
                                 return log_link_error_errno(link, r, "Could not set route: %m");
                 }
         }
 
-        r = sd_dhcp_lease_get_router(link->dhcp_lease, &router);
-        if (IN_SET(r, 0, -ENODATA))
-                log_link_info(link, "DHCP: No gateway received from DHCP server.");
-        else if (r < 0)
-                log_link_warning_errno(link, r, "DHCP error: could not get gateway: %m");
-        else if (in4_addr_is_null(&router[0]))
-                log_link_info(link, "DHCP: Received gateway is null.");
+        if (link->network->dhcp_use_gateway) {
+                r = sd_dhcp_lease_get_router(link->dhcp_lease, &router);
+                if (IN_SET(r, 0, -ENODATA))
+                        log_link_info(link, "DHCP: No gateway received from DHCP server.");
+                else if (r < 0)
+                        log_link_warning_errno(link, r, "DHCP error: could not get gateway: %m");
+                else if (in4_addr_is_null(&router[0]))
+                        log_link_info(link, "DHCP: Received gateway is null.");
 
-        /* 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 (classless_route && static_route)
-                log_link_warning(link, "Classless static routes received from DHCP server: ignoring static-route option and router option");
+                /* 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 (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 && !in4_addr_is_null(&router[0])) {
-                _cleanup_(route_freep) Route *route = NULL, *route_gw = NULL;
+                if (r > 0 && !classless_route && !in4_addr_is_null(&router[0])) {
+                        _cleanup_(route_freep) Route *route = NULL, *route_gw = NULL;
 
-                r = route_new(&route_gw);
-                if (r < 0)
-                        return log_link_error_errno(link, r,  "Could not allocate route: %m");
-
-                /* The dhcp netmask may mask out the gateway. Add an explicit
-                 * route for the gw host so that we can route no matter the
-                 * netmask or existing kernel route tables. */
-                route_gw->family = AF_INET;
-                route_gw->dst.in = router[0];
-                route_gw->dst_prefixlen = 32;
-                route_gw->prefsrc.in = address;
-                route_gw->scope = RT_SCOPE_LINK;
-                route_gw->protocol = RTPROT_DHCP;
-                route_gw->priority = link->network->dhcp_route_metric;
-                route_gw->table = table;
-                route_gw->mtu = link->network->dhcp_route_mtu;
-
-                r = dhcp_route_configure(&route_gw, link);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not set host route: %m");
+                        r = route_new(&route_gw);
+                        if (r < 0)
+                                return log_link_error_errno(link, r, "Could not allocate route: %m");
 
-                r = route_new(&route);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not allocate route: %m");
+                        /* The dhcp netmask may mask out the gateway. Add an explicit
+                         * route for the gw host so that we can route no matter the
+                         * netmask or existing kernel route tables. */
+                        route_gw->family = AF_INET;
+                        route_gw->dst.in = router[0];
+                        route_gw->dst_prefixlen = 32;
+                        route_gw->prefsrc.in = address;
+                        route_gw->scope = RT_SCOPE_LINK;
+                        route_gw->protocol = RTPROT_DHCP;
+                        route_gw->priority = link->network->dhcp_route_metric;
+                        route_gw->table = table;
+                        route_gw->mtu = link->network->dhcp_route_mtu;
+
+                        r = dhcp_route_configure(route_gw, link);
+                        if (r < 0)
+                                return log_link_error_errno(link, r, "Could not set host route: %m");
 
-                route->family = AF_INET;
-                route->gw.in = router[0];
-                route->prefsrc.in = address;
-                route->protocol = RTPROT_DHCP;
-                route->priority = link->network->dhcp_route_metric;
-                route->table = table;
-                route->mtu = link->network->dhcp_route_mtu;
+                        r = route_new(&route);
+                        if (r < 0)
+                                return log_link_error_errno(link, r, "Could not allocate route: %m");
 
-                r = dhcp_route_configure(&route, link);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not set router: %m");
-        }
+                        route->family = AF_INET;
+                        route->gw.in = router[0];
+                        route->prefsrc.in = address;
+                        route->protocol = RTPROT_DHCP;
+                        route->priority = link->network->dhcp_route_metric;
+                        route->table = table;
+                        route->mtu = link->network->dhcp_route_mtu;
 
-        Route *rt;
-        LIST_FOREACH(routes, rt, link->network->static_routes) {
-                if (!rt->gateway_from_dhcp)
-                        continue;
+                        r = dhcp_route_configure(route, link);
+                        if (r < 0)
+                                return log_link_error_errno(link, r, "Could not set router: %m");
+                }
 
-                if (rt->family != AF_INET)
-                        continue;
+                LIST_FOREACH(routes, rt, link->network->static_routes) {
+                        if (!rt->gateway_from_dhcp)
+                                continue;
 
-                rt->gw.in = router[0];
+                        if (rt->family != AF_INET)
+                                continue;
 
-                r = route_configure(rt, link, dhcp4_route_handler);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not set gateway: %m");
-                if (r > 0)
-                        link->dhcp4_messages++;
+                        rt->gw.in = router[0];
+
+                        r = dhcp_route_configure(rt, link);
+                        if (r < 0)
+                                return log_link_error_errno(link, r, "Could not set gateway: %m");
+                }
         }
 
         return link_set_dns_routes(link, &address);
 }
 
-static int dhcp_remove_routes(Link *link, sd_dhcp_lease *lease, const struct in_addr *address, bool remove_all) {
-        _cleanup_free_ sd_dhcp_route **routes = NULL;
-        uint32_t table;
-        int n, i, r;
+static int dhcp_reset_mtu(Link *link) {
+        uint16_t mtu;
+        int r;
 
         assert(link);
-        assert(address);
-
-        if (!link->network->dhcp_use_routes)
-                return 0;
 
-        n = sd_dhcp_lease_get_routes(lease, &routes);
-        if (IN_SET(n, 0, -ENODATA))
+        if (!link->network->dhcp_use_mtu)
                 return 0;
-        else if (n < 0)
-                return log_link_error_errno(link, n, "DHCP error: Failed to get routes: %m");
 
-        table = link_get_dhcp_route_table(link);
-
-        for (i = 0; i < n; i++) {
-                _cleanup_(route_freep) Route *route = NULL;
-
-                r = route_new(&route);
-                if (r < 0)
-                        return log_oom();
-
-                route->family = AF_INET;
-                assert_se(sd_dhcp_route_get_gateway(routes[i], &route->gw.in) >= 0);
-                assert_se(sd_dhcp_route_get_destination(routes[i], &route->dst.in) >= 0);
-                assert_se(sd_dhcp_route_get_destination_prefix_length(routes[i], &route->dst_prefixlen) >= 0);
-                route->priority = link->network->dhcp_route_metric;
-                route->table = table;
-                route->scope = route_scope_from_address(route, address);
-                if (IN_SET(route->scope, RT_SCOPE_LINK, RT_SCOPE_UNIVERSE))
-                        route->prefsrc.in = *address;
+        r = sd_dhcp_lease_get_mtu(link->dhcp_lease, &mtu);
+        if (r < 0)
+                return r;
 
-                if (!remove_all && set_contains(link->dhcp_routes, route))
-                        continue;
+        if (link->original_mtu == mtu)
+                return 0;
 
-                (void) route_remove(route, link, NULL);
+        r = link_set_mtu(link, link->original_mtu);
+        if (r < 0) {
+                log_link_error_errno(link, r, "DHCP error: could not reset MTU: %m");
+                link_enter_failed(link);
+                return r;
         }
 
-        return n;
+        return 0;
 }
 
-static int dhcp_remove_router(Link *link, sd_dhcp_lease *lease, const struct in_addr *address, bool remove_all) {
-        _cleanup_(route_freep) Route *route_gw = NULL, *route = NULL;
-        const struct in_addr *router;
-        uint32_t table;
+static int dhcp_reset_hostname(Link *link) {
+        const char *hostname;
         int r;
 
         assert(link);
-        assert(address);
-
-        if (!link->network->dhcp_use_routes)
-                return 0;
 
-        r = sd_dhcp_lease_get_router(lease, &router);
-        if (IN_SET(r, 0, -ENODATA)) {
-                log_link_debug(link, "DHCP: No gateway received from DHCP server.");
-                return 0;
-        } else if (r < 0)
-                return log_link_error_errno(link, r, "DHCP error: could not get gateway: %m");
-        else if (in4_addr_is_null(&router[0])) {
-                log_link_info(link, "DHCP: Received gateway is null, ignoring.");
+        if (!link->network->dhcp_use_hostname)
                 return 0;
-        }
-
-        table = link_get_dhcp_route_table(link);
-
-        r = route_new(&route_gw);
-        if (r < 0)
-                return log_oom();
 
-        route_gw->family = AF_INET;
-        route_gw->dst.in = router[0];
-        route_gw->dst_prefixlen = 32;
-        route_gw->prefsrc.in = *address;
-        route_gw->scope = RT_SCOPE_LINK;
-        route_gw->protocol = RTPROT_DHCP;
-        route_gw->priority = link->network->dhcp_route_metric;
-        route_gw->table = table;
+        hostname = link->network->dhcp_hostname;
+        if (!hostname)
+                (void) sd_dhcp_lease_get_hostname(link->dhcp_lease, &hostname);
 
-        if (remove_all || !set_contains(link->dhcp_routes, route_gw))
-                (void) route_remove(route_gw, link, NULL);
+        if (!hostname)
+                return 0;
 
-        r = route_new(&route);
+        /* If a hostname was set due to the lease, then unset it now. */
+        r = manager_set_hostname(link->manager, NULL);
         if (r < 0)
-                return log_oom();
-
-        route->family = AF_INET;
-        route->gw.in = router[0];
-        route->prefsrc.in = *address;
-        route->protocol = RTPROT_DHCP;
-        route->priority = link->network->dhcp_route_metric;
-        route->table = table;
-
-        if (remove_all || !set_contains(link->dhcp_routes, route))
-                (void) route_remove(route, link, NULL);
-
-        Route *rt;
-        LIST_FOREACH(routes, rt, link->network->static_routes) {
-                if (!rt->gateway_from_dhcp)
-                        continue;
-
-                if (rt->family != AF_INET)
-                        continue;
-
-                if (!remove_all && in4_addr_equal(router, &rt->gw.in))
-                        continue;
-
-                (void) route_remove(rt, link, NULL);
-        }
+                return log_link_error_errno(link, r, "DHCP error: Failed to reset transient hostname: %m");
 
         return 0;
 }
 
-static int dhcp_remove_dns_routes(Link *link, sd_dhcp_lease *lease, const struct in_addr *address, bool remove_all) {
-        const struct in_addr *dns;
-        uint32_t table;
-        int i, n, r;
+static int dhcp4_remove_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+        int r;
 
+        assert(m);
         assert(link);
-        assert(lease);
-        assert(link->network);
+        assert(link->dhcp4_remove_messages > 0);
 
-        if (!link->network->dhcp_use_dns ||
-            !link->network->dhcp_routes_to_dns)
-                return 0;
+        link->dhcp4_remove_messages--;
 
-        n = sd_dhcp_lease_get_dns(lease, &dns);
-        if (IN_SET(n, 0, -ENODATA))
-                return 0;
-        if (n < 0)
-                return log_link_warning_errno(link, n, "DHCP error: could not get DNS servers: %m");
-
-        table = link_get_dhcp_route_table(link);
-
-        for (i = 0; i < n; i ++) {
-                _cleanup_(route_freep) Route *route = NULL;
-
-                r = route_new(&route);
-                if (r < 0)
-                        return log_link_error_errno(link, r,  "Could not allocate route: %m");
-
-                route->family = AF_INET;
-                route->dst.in = dns[i];
-                route->dst_prefixlen = 32;
-                route->prefsrc.in = *address;
-                route->scope = RT_SCOPE_LINK;
-                route->protocol = RTPROT_DHCP;
-                route->priority = link->network->dhcp_route_metric;
-                route->table = table;
-
-                if (!remove_all && set_contains(link->dhcp_routes, route))
-                        continue;
-
-                (void) route_remove(route, link, NULL);
-        }
+        if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+                return 1;
 
-        if (!link_prefixroute(link)) {
-                _cleanup_(route_freep) Route *prefix_route = NULL;
+        r = sd_netlink_message_get_errno(m);
+        if (r < 0 && r != -ESRCH)
+                log_link_message_warning_errno(link, m, r, "Failed to remove DHCPv4 route, ignoring");
 
-                r = dhcp_prefix_route_from_lease(lease, table, address, &prefix_route);
+        if (link->dhcp4_remove_messages == 0) {
+                r = dhcp4_update_address(link, false);
                 if (r < 0)
-                        return log_link_warning_errno(link, r,  "Could not delete prefix route: %m");
-
-                if (remove_all || !set_contains(link->dhcp_routes, prefix_route))
-                        (void) route_remove(prefix_route, link, NULL);
+                        link_enter_failed(link);
         }
 
-        return 0;
+        return 1;
 }
 
-static int dhcp_remove_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+static int dhcp4_remove_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
         int r;
 
+        assert(m);
         assert(link);
+        assert(link->dhcp4_remove_messages > 0);
 
-        /* This is only used when retrying to assign the address received from DHCPv4 server.
-         * See dhcp4_route_handler(). */
+        link->dhcp4_remove_messages--;
 
         if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
                 return 1;
 
         r = sd_netlink_message_get_errno(m);
-        if (r < 0)
+        if (r < 0 && r != -EADDRNOTAVAIL)
                 log_link_message_warning_errno(link, m, r, "Failed to remove DHCPv4 address, ignoring");
         else
                 (void) manager_rtnl_process_address(rtnl, m, link->manager);
 
-        (void) dhcp_lease_renew(link->dhcp_client, link);
-        return 1;
-}
-
-static int dhcp_remove_address(
-                        Link *link, sd_dhcp_lease *lease,
-                        const struct in_addr *address,
-                        link_netlink_message_handler_t callback) {
-
-        _cleanup_(address_freep) Address *a = NULL;
-        struct in_addr netmask;
-        int r;
-
-        assert(link);
-        assert(address);
-
-        if (in4_addr_is_null(address))
-                return 0;
-
-        r = address_new(&a);
-        if (r < 0)
-                return log_oom();
-
-        a->family = AF_INET;
-        a->in_addr.in = *address;
-
-        if (sd_dhcp_lease_get_netmask(lease, &netmask) >= 0)
-                a->prefixlen = in4_addr_netmask_to_prefixlen(&netmask);
-
-        (void) address_remove(a, link, callback);
-
-        return 0;
-}
-
-static int dhcp_reset_mtu(Link *link) {
-        uint16_t mtu;
-        int r;
-
-        assert(link);
-
-        if (!link->network->dhcp_use_mtu)
-                return 0;
-
-        r = sd_dhcp_lease_get_mtu(link->dhcp_lease, &mtu);
-        if (r < 0)
-                return r;
-
-        if (link->original_mtu == mtu)
-                return 0;
-
-        r = link_set_mtu(link, link->original_mtu);
-        if (r < 0) {
-                log_link_error_errno(link, r, "DHCP error: could not reset MTU: %m");
-                link_enter_failed(link);
-                return r;
+        if (link->dhcp4_remove_messages == 0) {
+                r = dhcp4_update_address(link, false);
+                if (r < 0)
+                        link_enter_failed(link);
         }
 
-        return 0;
+        return 1;
 }
 
-static int dhcp_reset_hostname(Link *link) {
-        const char *hostname;
-        int r;
+static int dhcp4_remove_all(Link *link) {
+        Route *route;
+        Iterator i;
+        int k, r = 0;
 
         assert(link);
 
-        if (!link->network->dhcp_use_hostname)
-                return 0;
-
-        hostname = link->network->dhcp_hostname;
-        if (!hostname)
-                (void) sd_dhcp_lease_get_hostname(link->dhcp_lease, &hostname);
-
-        if (!hostname)
-                return 0;
+        SET_FOREACH(route, link->dhcp_routes, i) {
+                k = route_remove(route, link, dhcp4_remove_route_handler);
+                if (k < 0)
+                        r = k;
+                else
+                        link->dhcp4_remove_messages++;
+        }
 
-        /* If a hostname was set due to the lease, then unset it now. */
-        r = manager_set_hostname(link->manager, NULL);
-        if (r < 0)
-                return log_link_error_errno(link, r, "DHCP error: Failed to reset transient hostname: %m");
+        if (link->dhcp_address) {
+                k = address_remove(link->dhcp_address, link, dhcp4_remove_address_handler);
+                if (k < 0)
+                        r = k;
+                else
+                        link->dhcp4_remove_messages++;
+        }
 
-        return 0;
+        return r;
 }
 
 static int dhcp_lease_lost(Link *link) {
-        struct in_addr address = {};
+        int k, r = 0;
 
         assert(link);
         assert(link->dhcp_lease);
@@ -682,18 +557,27 @@ static int dhcp_lease_lost(Link *link) {
 
         link->dhcp4_configured = false;
 
-        (void) sd_dhcp_lease_get_address(link->dhcp_lease, &address);
-        (void) dhcp_remove_routes(link, link->dhcp_lease, &address, true);
-        (void) dhcp_remove_router(link, link->dhcp_lease, &address, true);
-        (void) dhcp_remove_dns_routes(link, link->dhcp_lease, &address, true);
-        (void) dhcp_remove_address(link, link->dhcp_lease, &address, NULL);
-        (void) dhcp_reset_mtu(link);
-        (void) dhcp_reset_hostname(link);
+        /* dhcp_lease_lost() may be called during renewing IP address. */
+        k = dhcp4_release_old_lease(link, true);
+        if (k < 0)
+                r = k;
+
+        k = dhcp4_remove_all(link);
+        if (k < 0)
+                r = k;
+
+        k = dhcp_reset_mtu(link);
+        if (k < 0)
+                r = k;
+
+        k = dhcp_reset_hostname(link);
+        if (k < 0)
+                r = k;
 
         link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease);
         link_dirty(link);
 
-        return 0;
+        return r;
 }
 
 static void dhcp_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
@@ -718,6 +602,7 @@ static void dhcp_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
                         (void) in_addr_to_string(AF_INET, &address, &pretty);
                         log_link_debug(link, "Successfully claimed DHCP4 address %s", strna(pretty));
                 }
+                link->dhcp4_address_bind = true;
                 dhcp4_check_ready(link);
                 break;
 
@@ -726,7 +611,9 @@ static void dhcp_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
                 (void) in_addr_to_string(AF_INET, &address, &pretty);
                 log_link_warning(link, "DAD conflict. Dropping DHCP4 address %s", strna(pretty));
 
-                (void) sd_dhcp_client_send_decline(link->dhcp_client);
+                r = sd_dhcp_client_send_decline(link->dhcp_client);
+                if (r < 0)
+                        log_link_warning_errno(link, r, "Failed to send DHCP DECLINE, ignoring: %m");
 
                 if (link->dhcp_lease) {
                         r = dhcp_lease_lost(link);
@@ -739,33 +626,71 @@ static void dhcp_address_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
                 assert_not_reached("Invalid IPv4ACD event.");
         }
 
-        sd_ipv4acd_stop(acd);
+        sd_ipv4acd_stop(acd);
+
+        return;
+}
+
+static int configure_dhcpv4_duplicate_address_detection(Link *link) {
+        int r;
+
+        assert(link);
+
+        r = sd_ipv4acd_new(&link->network->dhcp_acd);
+        if (r < 0)
+                return r;
+
+        r = sd_ipv4acd_attach_event(link->network->dhcp_acd, NULL, 0);
+        if (r < 0)
+                return r;
+
+        r = sd_ipv4acd_set_ifindex(link->network->dhcp_acd, link->ifindex);
+        if (r < 0)
+                return r;
+
+        r = sd_ipv4acd_set_mac(link->network->dhcp_acd, &link->mac);
+        if (r < 0)
+                return r;
 
-        return;
+        return 0;
 }
 
-static int configure_dhcpv4_duplicate_address_detection(Link *link) {
+static int dhcp4_start_acd(Link *link) {
+        union in_addr_union addr;
         int r;
 
-        assert(link);
+        if (!link->network->dhcp_send_decline)
+                return 0;
 
-        r = sd_ipv4acd_new(&link->network->dhcp_acd);
+        if (!link->dhcp_lease)
+                return 0;
+
+        link->dhcp4_address_bind = false;
+
+        r = sd_dhcp_lease_get_address(link->dhcp_lease, &addr.in);
         if (r < 0)
                 return r;
 
-        r = sd_ipv4acd_attach_event(link->network->dhcp_acd, NULL, 0);
+        r = sd_ipv4acd_set_address(link->network->dhcp_acd, &addr.in);
         if (r < 0)
                 return r;
 
-        r = sd_ipv4acd_set_ifindex(link->network->dhcp_acd, link->ifindex);
+        r = sd_ipv4acd_set_callback(link->network->dhcp_acd, dhcp_address_on_acd, link);
         if (r < 0)
                 return r;
 
-        r = sd_ipv4acd_set_mac(link->network->dhcp_acd, &link->mac);
+        if (DEBUG_LOGGING) {
+                _cleanup_free_ char *pretty = NULL;
+
+                (void) in_addr_to_string(AF_INET, &addr, &pretty);
+                log_link_debug(link, "Starting IPv4ACD client. Probing DHCPv4 address %s", strna(pretty));
+        }
+
+        r = sd_ipv4acd_start(link->network->dhcp_acd, true);
         if (r < 0)
                 return r;
 
-        return 0;
+        return 1;
 }
 
 static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
@@ -797,168 +722,139 @@ static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *
                 return 1;
         }
 
-        if (link->network->dhcp_send_decline) {
-                union in_addr_union addr;
-
-                (void) sd_dhcp_lease_get_address(link->dhcp_lease, &addr.in);
-
-                r = sd_ipv4acd_set_address(link->network->dhcp_acd, &addr.in);
-                if (r < 0)
-                        return r;
-
-                r = sd_ipv4acd_set_callback(link->network->dhcp_acd, dhcp_address_on_acd, link);
-                if (r < 0)
-                        return r;
-
-                if (DEBUG_LOGGING) {
-                        _cleanup_free_ char *pretty = NULL;
-
-                        (void) in_addr_to_string(AF_INET, &addr, &pretty);
-                        log_link_debug(link, "Starting IPv4ACD client. Probing DHCPv4 address %s", strna(pretty));
-                }
+        r = dhcp4_start_acd(link);
+        if (r < 0) {
+                log_link_error_errno(link, r, "Failed to start IPv4ACD for DHCP4 adddress: %m");
+                link_enter_failed(link);
+                return 1;
+        }
 
-                r = sd_ipv4acd_start(link->network->dhcp_acd, true);
-                if (r < 0)
-                        log_link_warning_errno(link, r, "Failed to start IPv4ACD client, ignoring: %m");
-        } else
-                dhcp4_check_ready(link);
+        dhcp4_check_ready(link);
 
         return 1;
 }
 
-static int dhcp4_update_address(Link *link,
-                                struct in_addr *address,
-                                struct in_addr *netmask,
-                                uint32_t lifetime) {
+static int dhcp4_update_address(Link *link, bool announce) {
         _cleanup_(address_freep) Address *addr = NULL;
+        uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME;
+        struct in_addr address, netmask;
         unsigned prefixlen;
+        Address *ret;
         int r;
 
-        assert(address);
-        assert(netmask);
-        assert(lifetime);
+        assert(link);
+        assert(link->network);
+
+        if (!link->dhcp_lease)
+                return 0;
+
+        link_set_state(link, LINK_STATE_CONFIGURING);
+        link->dhcp4_configured = false;
+
+        /* address_handler calls link_request_set_routes() and link_request_set_nexthop(). Before they
+         * are called, the related flags must be cleared. Otherwise, the link becomes configured state
+         * before routes are configured. */
+        link->static_routes_configured = false;
+        link->static_nexthops_configured = false;
+
+        r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
+        if (r < 0)
+                return log_link_warning_errno(link, r, "DHCP error: no address: %m");
+
+        r = sd_dhcp_lease_get_netmask(link->dhcp_lease, &netmask);
+        if (r < 0)
+                return log_link_warning_errno(link, r, "DHCP error: no netmask: %m");
+
+        if (!FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP)) {
+                r = sd_dhcp_lease_get_lifetime(link->dhcp_lease, &lifetime);
+                if (r < 0)
+                        return log_link_warning_errno(link, r, "DHCP error: no lifetime: %m");
+        }
+
+        prefixlen = in4_addr_netmask_to_prefixlen(&netmask);
 
-        prefixlen = in4_addr_netmask_to_prefixlen(netmask);
+        if (announce) {
+                const struct in_addr *router;
+
+                r = sd_dhcp_lease_get_router(link->dhcp_lease, &router);
+                if (r < 0 && r != -ENODATA)
+                        return log_link_error_errno(link, r, "DHCP error: Could not get gateway: %m");
+
+                if (r > 0 && !in4_addr_is_null(&router[0]))
+                        log_struct(LOG_INFO,
+                                   LOG_LINK_INTERFACE(link),
+                                   LOG_LINK_MESSAGE(link, "DHCPv4 address %u.%u.%u.%u/%u via %u.%u.%u.%u",
+                                                    ADDRESS_FMT_VAL(address),
+                                                    prefixlen,
+                                                    ADDRESS_FMT_VAL(router[0])),
+                                   "ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address),
+                                   "PREFIXLEN=%u", prefixlen,
+                                   "GATEWAY=%u.%u.%u.%u", ADDRESS_FMT_VAL(router[0]));
+                else
+                        log_struct(LOG_INFO,
+                                   LOG_LINK_INTERFACE(link),
+                                   LOG_LINK_MESSAGE(link, "DHCPv4 address %u.%u.%u.%u/%u",
+                                                    ADDRESS_FMT_VAL(address),
+                                                    prefixlen),
+                                   "ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address),
+                                   "PREFIXLEN=%u", prefixlen);
+        }
 
         r = address_new(&addr);
         if (r < 0)
-                return r;
+                return log_oom();
 
         addr->family = AF_INET;
-        addr->in_addr.in.s_addr = address->s_addr;
+        addr->in_addr.in.s_addr = address.s_addr;
         addr->cinfo.ifa_prefered = lifetime;
         addr->cinfo.ifa_valid = lifetime;
         addr->prefixlen = prefixlen;
-        addr->broadcast.s_addr = address->s_addr | ~netmask->s_addr;
+        addr->broadcast.s_addr = address.s_addr | ~netmask.s_addr;
         addr->prefix_route = link_prefixroute(link);
 
         /* allow reusing an existing address and simply update its lifetime
          * in case it already exists */
-        r = address_configure(addr, link, dhcp4_address_handler, true);
+        r = address_configure(addr, link, dhcp4_address_handler, true, &ret);
         if (r < 0)
-                return r;
+                return log_link_error_errno(link, r, "Failed to set DHCPv4 address: %m");
+
+        if (!address_equal(link->dhcp_address, ret))
+                link->dhcp_address_old = link->dhcp_address;
+        link->dhcp_address = ret;
 
         return 0;
 }
 
 static int dhcp_lease_renew(sd_dhcp_client *client, Link *link) {
         sd_dhcp_lease *lease;
-        struct in_addr address;
-        struct in_addr netmask;
-        uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME;
         int r;
 
         assert(link);
         assert(client);
-        assert(link->network);
 
         r = sd_dhcp_client_get_lease(client, &lease);
         if (r < 0)
                 return log_link_warning_errno(link, r, "DHCP error: no lease: %m");
 
         sd_dhcp_lease_unref(link->dhcp_lease);
-        link->dhcp4_configured = false;
         link->dhcp_lease = sd_dhcp_lease_ref(lease);
         link_dirty(link);
 
-        r = sd_dhcp_lease_get_address(lease, &address);
-        if (r < 0)
-                return log_link_warning_errno(link, r, "DHCP error: no address: %m");
-
-        r = sd_dhcp_lease_get_netmask(lease, &netmask);
-        if (r < 0)
-                return log_link_warning_errno(link, r, "DHCP error: no netmask: %m");
-
-        if (!FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP)) {
-                r = sd_dhcp_lease_get_lifetime(link->dhcp_lease, &lifetime);
-                if (r < 0)
-                        return log_link_warning_errno(link, r, "DHCP error: no lifetime: %m");
-        }
-
-        r = dhcp4_update_address(link, &address, &netmask, lifetime);
-        if (r < 0)
-                return log_link_warning_errno(link, r, "Could not update IP address: %m");
-
-        return 0;
+        return dhcp4_update_address(link, false);
 }
 
 static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
-        const struct in_addr *router;
         sd_dhcp_lease *lease;
-        struct in_addr address;
-        struct in_addr netmask;
-        unsigned prefixlen;
-        uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME;
         int r;
 
         assert(client);
         assert(link);
 
-        link->dhcp4_configured = false;
-
         r = sd_dhcp_client_get_lease(client, &lease);
         if (r < 0)
                 return log_link_error_errno(link, r, "DHCP error: No lease: %m");
 
-        r = sd_dhcp_lease_get_address(lease, &address);
-        if (r < 0)
-                return log_link_error_errno(link, r, "DHCP error: No address: %m");
-
-        r = sd_dhcp_lease_get_netmask(lease, &netmask);
-        if (r < 0)
-                return log_link_error_errno(link, r, "DHCP error: No netmask: %m");
-
-        prefixlen = in4_addr_netmask_to_prefixlen(&netmask);
-
-        if (!FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP)) {
-                r = sd_dhcp_lease_get_lifetime(lease, &lifetime);
-                if (r < 0)
-                        return log_link_warning_errno(link, r, "DHCP error: no lifetime: %m");
-        }
-
-        r = sd_dhcp_lease_get_router(lease, &router);
-        if (r < 0 && r != -ENODATA)
-                return log_link_error_errno(link, r, "DHCP error: Could not get gateway: %m");
-
-        if (r > 0 && !in4_addr_is_null(&router[0]))
-                log_struct(LOG_INFO,
-                           LOG_LINK_INTERFACE(link),
-                           LOG_LINK_MESSAGE(link, "DHCPv4 address %u.%u.%u.%u/%u via %u.%u.%u.%u",
-                                            ADDRESS_FMT_VAL(address),
-                                            prefixlen,
-                                            ADDRESS_FMT_VAL(router[0])),
-                           "ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address),
-                           "PREFIXLEN=%u", prefixlen,
-                           "GATEWAY=%u.%u.%u.%u", ADDRESS_FMT_VAL(router[0]));
-        else
-                log_struct(LOG_INFO,
-                           LOG_LINK_INTERFACE(link),
-                           LOG_LINK_MESSAGE(link, "DHCPv4 address %u.%u.%u.%u/%u",
-                                            ADDRESS_FMT_VAL(address),
-                                            prefixlen),
-                           "ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address),
-                           "PREFIXLEN=%u", prefixlen);
-
+        sd_dhcp_lease_unref(link->dhcp_lease);
         link->dhcp_lease = sd_dhcp_lease_ref(lease);
         link_dirty(link);
 
@@ -1009,9 +905,14 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
                 }
         }
 
-        r = dhcp4_update_address(link, &address, &netmask, lifetime);
-        if (r < 0)
-                return log_link_warning_errno(link, r, "Could not update IP address: %m");
+        if (link->dhcp4_remove_messages == 0) {
+                r = dhcp4_update_address(link, true);
+                if (r < 0)
+                        return r;
+        } else
+                log_link_debug(link,
+                               "The link has previously assigned DHCPv4 address or routes. "
+                               "The newly assigned address and routes will set up after old ones are removed.");
 
         return 0;
 }
@@ -1019,35 +920,42 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
 static int dhcp_lease_ip_change(sd_dhcp_client *client, Link *link) {
         int r;
 
-        link->dhcp_lease_old = TAKE_PTR(link->dhcp_lease);
-
-        /* On ip address change, to keep the connectability, we would like to assign new address and
-         * routes, and then release old lease. There are two possible success paths:
-         *
-         * 1. new address and routes are configured.
-         *    -> handled by dhcp_release_old_lease() in dhcp4_route_handler().
-         * 2. new address is configured and no route is requested.
-         *    -> handled by dhcp_release_old_lease() in dhcp4_address_handler().
-         *
-         * On error in assigning new address and routes, then the link always enters to the failed
-         * state. And link_enter_failed() leads to the DHCP client to be stopped. So,
-         * dhcp_release_old_lease() will be also called by link_stop_clients().
-         */
-
         r = dhcp_lease_acquired(client, link);
-        if (r < 0) {
-                /* If it fails, then the new address is not configured yet.
-                 * So, let's simply drop the old lease. */
-                sd_dhcp_lease_unref(link->dhcp_lease);
-                link->dhcp_lease = TAKE_PTR(link->dhcp_lease_old);
+        if (r < 0)
                 (void) dhcp_lease_lost(link);
-                return r;
+
+        return r;
+}
+
+static int dhcp_server_is_deny_listed(Link *link, sd_dhcp_client *client) {
+        sd_dhcp_lease *lease;
+        struct in_addr addr;
+        int r;
+
+        assert(link);
+        assert(link->network);
+        assert(client);
+
+        r = sd_dhcp_client_get_lease(client, &lease);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Failed to get DHCP lease: %m");
+
+        r = sd_dhcp_lease_get_server_identifier(lease, &addr);
+        if (r < 0)
+                return log_link_debug_errno(link, r, "Failed to get DHCP server IP address: %m");
+
+        if (set_contains(link->network->dhcp_deny_listed_ip, UINT32_TO_PTR(addr.s_addr))) {
+                log_struct(LOG_DEBUG,
+                           LOG_LINK_INTERFACE(link),
+                           LOG_LINK_MESSAGE(link, "DHCPv4 IP '%u.%u.%u.%u' found in deny-listed IP addresses, ignoring offer",
+                                            ADDRESS_FMT_VAL(addr)));
+                return true;
         }
 
-        return 0;
+        return false;
 }
 
-static int dhcp_server_is_black_listed(Link *link, sd_dhcp_client *client) {
+static int dhcp_server_is_allow_listed(Link *link, sd_dhcp_client *client) {
         sd_dhcp_lease *lease;
         struct in_addr addr;
         int r;
@@ -1062,12 +970,12 @@ static int dhcp_server_is_black_listed(Link *link, sd_dhcp_client *client) {
 
         r = sd_dhcp_lease_get_server_identifier(lease, &addr);
         if (r < 0)
-                return log_link_debug_errno(link, r, "Failed to get DHCP server ip address: %m");
+                return log_link_debug_errno(link, r, "Failed to get DHCP server IP address: %m");
 
-        if (set_contains(link->network->dhcp_black_listed_ip, UINT32_TO_PTR(addr.s_addr))) {
+        if (set_contains(link->network->dhcp_allow_listed_ip, UINT32_TO_PTR(addr.s_addr))) {
                 log_struct(LOG_DEBUG,
                            LOG_LINK_INTERFACE(link),
-                           LOG_LINK_MESSAGE(link, "DHCPv4 ip '%u.%u.%u.%u' found in black listed ip addresses, ignoring offer",
+                           LOG_LINK_MESSAGE(link, "DHCPv4 IP '%u.%u.%u.%u' found in allow-listed IP addresses, accepting offer",
                                             ADDRESS_FMT_VAL(addr)));
                 return true;
         }
@@ -1105,8 +1013,11 @@ static int dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
                         }
 
                         if (link->dhcp_lease) {
-                                if (link->network->dhcp_send_release)
-                                        (void) sd_dhcp_client_send_release(client);
+                                if (link->network->dhcp_send_release) {
+                                        r = sd_dhcp_client_send_release(client);
+                                        if (r < 0)
+                                                log_link_warning_errno(link, r, "Failed to send DHCP RELEASE, ignoring: %m");
+                                }
 
                                 r = dhcp_lease_lost(link);
                                 if (r < 0) {
@@ -1159,12 +1070,19 @@ static int dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
                         }
                         break;
                 case SD_DHCP_CLIENT_EVENT_SELECTING:
-                        r = dhcp_server_is_black_listed(link, client);
-                        if (r < 0)
-                                return r;
-                        if (r != 0)
-                                return -ENOMSG;
-
+                        if (!set_isempty(link->network->dhcp_allow_listed_ip)) {
+                                r = dhcp_server_is_allow_listed(link, client);
+                                if (r < 0)
+                                        return r;
+                                if (r == 0)
+                                        return -ENOMSG;
+                        } else {
+                                r = dhcp_server_is_deny_listed(link, client);
+                                if (r < 0)
+                                        return r;
+                                if (r != 0)
+                                        return -ENOMSG;
+                        }
                         break;
                 default:
                         if (event < 0)
@@ -1334,11 +1252,11 @@ int dhcp4_configure(Link *link) {
                         return log_oom();
                 if (r < 0)
                         return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to create DHCP4 client: %m");
-        }
 
-        r = sd_dhcp_client_attach_event(link->dhcp_client, NULL, 0);
-        if (r < 0)
-                return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to attach event: %m");
+                r = sd_dhcp_client_attach_event(link->dhcp_client, NULL, 0);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to attach event: %m");
+        }
 
         r = sd_dhcp_client_set_mac(link->dhcp_client,
                                    (const uint8_t *) &link->mac,
@@ -1421,13 +1339,22 @@ int dhcp4_configure(Link *link) {
                         log_link_debug(link, "DHCP4 CLIENT: Failed to set request flag for '%u' already exists, ignoring.", option);
                         continue;
                 }
-
                 if (r < 0)
                         return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set request flag for '%u': %m", option);
         }
 
         ORDERED_HASHMAP_FOREACH(send_option, link->network->dhcp_client_send_options, i) {
-                r = sd_dhcp_client_set_dhcp_option(link->dhcp_client, send_option);
+                r = sd_dhcp_client_add_option(link->dhcp_client, send_option);
+                if (r == -EEXIST)
+                        continue;
+                if (r < 0)
+                        return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set send option: %m");
+        }
+
+        ORDERED_HASHMAP_FOREACH(send_option, link->network->dhcp_client_send_vendor_options, i) {
+                r = sd_dhcp_client_add_vendor_option(link->dhcp_client, send_option);
+                if (r == -EEXIST)
+                        continue;
                 if (r < 0)
                         return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set send option: %m");
         }
@@ -1443,6 +1370,13 @@ int dhcp4_configure(Link *link) {
                         return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set vendor class identifier: %m");
         }
 
+       if (link->network->dhcp_mudurl) {
+                r = sd_dhcp_client_set_mud_url(link->dhcp_client,
+                                               link->network->dhcp_mudurl);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set MUD URL: %m");
+        }
+
         if (link->network->dhcp_user_class) {
                 r = sd_dhcp_client_set_user_class(link->dhcp_client, (const char **) link->network->dhcp_user_class);
                 if (r < 0)
@@ -1464,7 +1398,13 @@ int dhcp4_configure(Link *link) {
         if (link->network->ip_service_type > 0) {
                 r = sd_dhcp_client_set_service_type(link->dhcp_client, link->network->ip_service_type);
                 if (r < 0)
-                        return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set ip service type: %m");
+                        return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set IP service type: %m");
+        }
+
+        if (link->network->dhcp_fallback_lease_lifetime > 0) {
+                r = sd_dhcp_client_set_fallback_lease_lifetime(link->dhcp_client, link->network->dhcp_fallback_lease_lifetime);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed set to lease lifetime: %m");
         }
 
         if (link->network->dhcp_send_decline) {
@@ -1508,13 +1448,13 @@ int config_parse_dhcp_max_attempts(
 
         r = safe_atou64(rvalue, &a);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to parse DHCP maximum attempts, ignoring: %s", rvalue);
                 return 0;
         }
 
         if (a == 0) {
-                log_syntax(unit, LOG_ERR, filename, line, 0,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "%s= must be positive integer or 'infinity', ignoring: %s", lvalue, rvalue);
                 return 0;
         }
@@ -1524,7 +1464,7 @@ int config_parse_dhcp_max_attempts(
         return 0;
 }
 
-int config_parse_dhcp_black_listed_ip_address(
+int config_parse_dhcp_acl_ip_address(
                 const char *unit,
                 const char *filename,
                 unsigned line,
@@ -1537,7 +1477,7 @@ int config_parse_dhcp_black_listed_ip_address(
                 void *userdata) {
 
         Network *network = data;
-        const char *p;
+        Set **acl;
         int r;
 
         assert(filename);
@@ -1545,20 +1485,24 @@ int config_parse_dhcp_black_listed_ip_address(
         assert(rvalue);
         assert(data);
 
+        acl = STR_IN_SET(lvalue, "DenyList", "BlackList") ? &network->dhcp_deny_listed_ip : &network->dhcp_allow_listed_ip;
+
         if (isempty(rvalue)) {
-                network->dhcp_black_listed_ip = set_free(network->dhcp_black_listed_ip);
+                *acl = set_free(*acl);
                 return 0;
         }
 
-        for (p = rvalue;;) {
+        for (const char *p = rvalue;;) {
                 _cleanup_free_ char *n = NULL;
                 union in_addr_union ip;
 
                 r = extract_first_word(&p, &n, NULL, 0);
+                if (r == -ENOMEM)
+                        return log_oom();
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
-                                   "Failed to parse DHCP black listed ip address, ignoring assignment: %s",
-                                   rvalue);
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to parse DHCP '%s=' IP address, ignoring assignment: %s",
+                                   lvalue, rvalue);
                         return 0;
                 }
                 if (r == 0)
@@ -1566,25 +1510,19 @@ int config_parse_dhcp_black_listed_ip_address(
 
                 r = in_addr_from_string(AF_INET, n, &ip);
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
-                                   "DHCP black listed ip address is invalid, ignoring assignment: %s", n);
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "DHCP '%s=' IP address is invalid, ignoring assignment: %s", lvalue, n);
                         continue;
                 }
 
-                r = set_ensure_allocated(&network->dhcp_black_listed_ip, NULL);
-                if (r < 0)
-                        return log_oom();
-
-                r = set_put(network->dhcp_black_listed_ip, UINT32_TO_PTR(ip.in.s_addr));
+                r = set_ensure_put(acl, NULL, UINT32_TO_PTR(ip.in.s_addr));
                 if (r < 0)
-                        log_syntax(unit, LOG_ERR, filename, line, r,
-                                   "Failed to store DHCP black listed ip address '%s', ignoring assignment: %m", n);
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to store DHCP '%s=' IP address '%s', ignoring assignment: %m", lvalue, n);
         }
-
-        return 0;
 }
 
-int config_parse_dhcp_user_class(
+int config_parse_dhcp_ip_service_type(
                 const char *unit,
                 const char *filename,
                 unsigned line,
@@ -1596,49 +1534,22 @@ int config_parse_dhcp_user_class(
                 void *data,
                 void *userdata) {
 
-        char ***l = data;
-        int r;
-
-        assert(l);
+        assert(filename);
         assert(lvalue);
         assert(rvalue);
 
-        if (isempty(rvalue)) {
-                *l = strv_free(*l);
-                return 0;
-        }
-
-        for (;;) {
-                _cleanup_free_ char *w = NULL;
-
-                r = extract_first_word(&rvalue, &w, NULL, 0);
-                if (r == -ENOMEM)
-                        return log_oom();
-                if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
-                                   "Failed to split user classes option, ignoring: %s", rvalue);
-                        break;
-                }
-                if (r == 0)
-                        break;
-
-                if (strlen(w) > 255) {
-                        log_syntax(unit, LOG_ERR, filename, line, 0,
-                                   "%s length is not in the range 1-255, ignoring.", w);
-                        continue;
-                }
-
-                r = strv_push(l, w);
-                if (r < 0)
-                        return log_oom();
-
-                w = NULL;
-        }
+        if (streq(rvalue, "CS4"))
+                *((int *)data) = IPTOS_CLASS_CS4;
+        else if (streq(rvalue, "CS6"))
+                *((int *)data) = IPTOS_CLASS_CS6;
+        else
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "Failed to parse IPServiceType type '%s', ignoring.", rvalue);
 
         return 0;
 }
 
-int config_parse_dhcp_request_options(
+int config_parse_dhcp_mud_url(
                 const char *unit,
                 const char *filename,
                 unsigned line,
@@ -1650,62 +1561,37 @@ int config_parse_dhcp_request_options(
                 void *data,
                 void *userdata) {
 
+        _cleanup_free_ char *unescaped = NULL;
         Network *network = data;
-        const char *p;
         int r;
 
         assert(filename);
         assert(lvalue);
         assert(rvalue);
-        assert(data);
 
         if (isempty(rvalue)) {
-                network->dhcp_request_options = set_free(network->dhcp_request_options);
+                network->dhcp_mudurl = mfree(network->dhcp_mudurl);
                 return 0;
         }
 
-        for (p = rvalue;;) {
-                _cleanup_free_ char *n = NULL;
-                uint32_t i;
-
-                r = extract_first_word(&p, &n, NULL, 0);
-                if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
-                                   "Failed to parse DHCP request option, ignoring assignment: %s",
-                                   rvalue);
-                        return 0;
-                }
-                if (r == 0)
-                        return 0;
-
-                r = safe_atou32(n, &i);
-                if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
-                                   "DHCP request option is invalid, ignoring assignment: %s", n);
-                        continue;
-                }
-
-                if (i < 1 || i >= 255) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
-                                   "DHCP request option is invalid, valid range is 1-254, ignoring assignment: %s", n);
-                        continue;
-                }
+        r = cunescape(rvalue, 0, &unescaped);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to Failed to unescape MUD URL, ignoring: %s", rvalue);
+                return 0;
+        }
 
-                r = set_ensure_allocated(&network->dhcp_request_options, NULL);
-                if (r < 0)
-                        return log_oom();
+        if (!http_url_is_valid(unescaped) || strlen(unescaped) > 255) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "Failed to parse MUD URL '%s', ignoring: %m", rvalue);
 
-                r = set_put(network->dhcp_request_options, UINT32_TO_PTR(i));
-                if (r < 0)
-                        log_syntax(unit, LOG_ERR, filename, line, r,
-                                   "Failed to store DHCP request option '%s', ignoring assignment: %m", n);
+                return 0;
         }
 
-        return 0;
+        return free_and_strdup_warn(&network->dhcp_mudurl, unescaped);
 }
 
-int config_parse_dhcp_ip_service_type(
-                const char *unit,
+int config_parse_dhcp_fallback_lease_lifetime(const char *unit,
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -1715,18 +1601,30 @@ int config_parse_dhcp_ip_service_type(
                 const char *rvalue,
                 void *data,
                 void *userdata) {
+        Network *network = userdata;
+        uint32_t k;
 
         assert(filename);
+        assert(section);
         assert(lvalue);
         assert(rvalue);
+        assert(data);
 
-        if (streq(rvalue, "CS4"))
-                *((int *)data) = IPTOS_CLASS_CS4;
-        else if (streq(rvalue, "CS6"))
-                *((int *)data) = IPTOS_CLASS_CS6;
-        else
+        if (isempty(rvalue)) {
+                network->dhcp_fallback_lease_lifetime = 0;
+                return 0;
+        }
+
+        /* We accept only "forever" or "infinity". */
+        if (STR_IN_SET(rvalue, "forever", "infinity"))
+                k = CACHE_INFO_INFINITY_LIFE_TIME;
+        else {
                 log_syntax(unit, LOG_WARNING, filename, line, 0,
-                           "Failed to parse IPServiceType type '%s', ignoring.", rvalue);
+                           "Invalid LeaseLifetime= value, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        network->dhcp_fallback_lease_lifetime = k;
 
         return 0;
 }
index 95fa5ee4b5bce81c8e42c306658cbc186b6ffee2..7a80897ffcdd6e9f0bca2fd85ccee313474712cd 100644 (file)
@@ -17,14 +17,13 @@ typedef enum DHCPClientIdentifier {
         _DHCP_CLIENT_ID_INVALID = -1,
 } DHCPClientIdentifier;
 
-void dhcp4_release_old_lease(Link *link);
 int dhcp4_configure(Link *link);
 int dhcp4_set_client_identifier(Link *link);
 int dhcp4_set_promote_secondaries(Link *link);
 
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_client_identifier);
-CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_black_listed_ip_address);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_acl_ip_address);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_max_attempts);
-CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_user_class);
-CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_request_options);
 CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_ip_service_type);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_mud_url);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_fallback_lease_lifetime);
index 7304270c60b1e97bcad6e5b77dddc339aac5c1bf..d671284b001906975a8e0ec4a421217838dd804f 100644 (file)
@@ -6,10 +6,10 @@
 #include <netinet/in.h>
 #include <linux/if.h>
 #include <linux/if_arp.h>
-#include "sd-radv.h"
 
 #include "sd-dhcp6-client.h"
 
+#include "escape.h"
 #include "hashmap.h"
 #include "hostname-util.h"
 #include "missing_network.h"
 #include "networkd-dhcp6.h"
 #include "networkd-link.h"
 #include "networkd-manager.h"
+#include "networkd-radv.h"
 #include "siphash24.h"
+#include "string-table.h"
 #include "string-util.h"
 #include "radv-internal.h"
+#include "web-util.h"
 
-static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link);
-static Link *dhcp6_prefix_get(Manager *m, struct in6_addr *addr);
-static int dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link);
-static int dhcp6_prefix_remove_all(Manager *m, Link *link);
+static bool dhcp6_lease_has_pd_prefix(sd_dhcp6_lease *lease) {
+        uint32_t lifetime_preferred, lifetime_valid;
+        union in_addr_union pd_prefix;
+        uint8_t pd_prefix_len;
 
-static bool dhcp6_get_prefix_delegation(Link *link) {
-        if (!link->network)
+        if (!lease)
                 return false;
 
-        return IN_SET(link->network->router_prefix_delegation,
-                      RADV_PREFIX_DELEGATION_DHCP6,
-                      RADV_PREFIX_DELEGATION_BOTH);
+        sd_dhcp6_lease_reset_pd_prefix_iter(lease);
+
+        return sd_dhcp6_lease_get_pd(lease, &pd_prefix.in6, &pd_prefix_len, &lifetime_preferred, &lifetime_valid) >= 0;
 }
 
-static bool dhcp6_enable_prefix_delegation(Link *dhcp6_link) {
-        Manager *manager;
-        Link *l;
+DHCP6DelegatedPrefix *dhcp6_pd_free(DHCP6DelegatedPrefix *p) {
+        if (!p)
+                return NULL;
+
+        if (p->link && p->link->manager) {
+                hashmap_remove(p->link->manager->dhcp6_prefixes, &p->prefix);
+                set_remove(p->link->manager->dhcp6_pd_prefixes, p);
+        }
+
+        link_unref(p->link);
+        return mfree(p);
+}
+
+static void dhcp6_pd_hash_func(const DHCP6DelegatedPrefix *p, struct siphash *state) {
+        assert(p);
+
+        siphash24_compress(&p->pd_prefix, sizeof(p->pd_prefix), state);
+        siphash24_compress(&p->link, sizeof(p->link), state);
+}
+
+static int dhcp6_pd_compare_func(const DHCP6DelegatedPrefix *a, const DHCP6DelegatedPrefix *b) {
+        int r;
+
+        r = memcmp(&a->pd_prefix, &b->pd_prefix, sizeof(a->pd_prefix));
+        if (r != 0)
+                return r;
+
+        return CMP(a->link, b->link);
+}
+
+DEFINE_HASH_OPS(dhcp6_pd_hash_ops, DHCP6DelegatedPrefix, dhcp6_pd_hash_func, dhcp6_pd_compare_func);
+
+static Link *dhcp6_pd_get_link_by_prefix(Link *link, const union in_addr_union *prefix) {
+        DHCP6DelegatedPrefix *pd;
+
+        assert(link);
+        assert(link->manager);
+        assert(prefix);
+
+        pd = hashmap_get(link->manager->dhcp6_prefixes, &prefix->in6);
+        if (!pd)
+                return NULL;
+
+        return pd->link;
+}
+
+static int dhcp6_pd_get_assigned_prefix(Link *link, const union in_addr_union *pd_prefix, union in_addr_union *ret_prefix) {
+        DHCP6DelegatedPrefix *pd, in;
+
+        assert(link);
+        assert(link->manager);
+        assert(pd_prefix);
+        assert(ret_prefix);
+
+        in = (DHCP6DelegatedPrefix) {
+                .pd_prefix = pd_prefix->in6,
+                .link = link,
+        };
+
+        pd = set_get(link->manager->dhcp6_pd_prefixes, &in);
+        if (!pd)
+                return -ENOENT;
+
+        ret_prefix->in6 = pd->prefix;
+        return 0;
+}
+
+static int dhcp6_pd_remove_old(Link *link, bool force);
+
+static int dhcp6_pd_address_callback(Address *address) {
+        Address *a;
         Iterator i;
 
-        assert(dhcp6_link);
+        assert(address);
+        assert(address->link);
 
-        manager = dhcp6_link->manager;
-        assert(manager);
+        /* Make this called only once */
+        SET_FOREACH(a, address->link->dhcp6_pd_addresses, i)
+                a->callback = NULL;
 
-        HASHMAP_FOREACH(l, manager->links, i) {
-                if (l == dhcp6_link)
-                        continue;
+        return dhcp6_pd_remove_old(address->link, true);
+}
 
-                if (!dhcp6_get_prefix_delegation(l))
-                        continue;
+static int dhcp6_pd_remove_old(Link *link, bool force) {
+        Address *address;
+        Route *route;
+        Iterator i;
+        int k, r = 0;
 
-                return true;
+        assert(link);
+        assert(link->manager);
+
+        if (!force && (link->dhcp6_pd_address_messages != 0 || link->dhcp6_pd_route_configured != 0))
+                return 0;
+
+        if (set_isempty(link->dhcp6_pd_addresses_old) && set_isempty(link->dhcp6_pd_routes_old))
+                return 0;
+
+        if (!force) {
+                bool set_callback = !set_isempty(link->dhcp6_pd_addresses);
+
+                SET_FOREACH(address, link->dhcp6_pd_addresses, i)
+                        if (address_is_ready(address)) {
+                                set_callback = false;
+                                break;
+                        }
+
+                if (set_callback) {
+                        SET_FOREACH(address, link->dhcp6_pd_addresses, i)
+                                address->callback = dhcp6_pd_address_callback;
+                        return 0;
+                }
         }
 
-        return false;
+        log_link_debug(link, "Removing old DHCPv6 Prefix Delegation addresses and routes.");
+
+        link_dirty(link);
+
+        SET_FOREACH(route, link->dhcp6_pd_routes_old, i) {
+                k = route_remove(route, link, NULL);
+                if (k < 0)
+                        r = k;
+
+                (void) sd_radv_remove_prefix(link->radv, &route->dst.in6, 64);
+                dhcp6_pd_free(hashmap_get(link->manager->dhcp6_prefixes, &route->dst.in6));
+        }
+
+        SET_FOREACH(address, link->dhcp6_pd_addresses_old, i) {
+                k = address_remove(address, link, NULL);
+                if (k < 0)
+                        r = k;
+        }
+
+        return r;
 }
 
-static int dhcp6_lease_information_acquired(sd_dhcp6_client *client,
-                                        Link *link) {
-        return 0;
+int dhcp6_pd_remove(Link *link) {
+        Address *address;
+        Route *route;
+        Iterator i;
+        int k, r = 0;
+
+        assert(link);
+        assert(link->manager);
+
+        link->dhcp6_pd_address_configured = false;
+        link->dhcp6_pd_route_configured = false;
+
+        k = dhcp6_pd_remove_old(link, true);
+        if (k < 0)
+                r = k;
+
+        if (set_isempty(link->dhcp6_pd_addresses) && set_isempty(link->dhcp6_pd_routes))
+                return r;
+
+        log_link_debug(link, "Removing DHCPv6 Prefix Delegation addresses and routes.");
+
+        link_dirty(link);
+
+        SET_FOREACH(route, link->dhcp6_pd_routes, i) {
+                k = route_remove(route, link, NULL);
+                if (k < 0)
+                        r = k;
+
+                (void) sd_radv_remove_prefix(link->radv, &route->dst.in6, 64);
+                dhcp6_pd_free(hashmap_get(link->manager->dhcp6_prefixes, &route->dst.in6));
+        }
+
+        SET_FOREACH(address, link->dhcp6_pd_addresses, i) {
+                k = address_remove(address, link, NULL);
+                if (k < 0)
+                        r = k;
+        }
+
+        return r;
 }
 
-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;
+static int dhcp6_pd_route_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
         int r;
-        _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
 
-        r = sd_radv_prefix_new(&p);
-        if (r < 0)
-                return r;
+        assert(link);
+        assert(link->dhcp6_pd_route_messages > 0);
 
-        r = sd_radv_prefix_set_prefix(p, prefix, prefix_len);
+        link->dhcp6_pd_route_messages--;
+
+        if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+                return 1;
+
+        r = sd_netlink_message_get_errno(m);
+        if (r < 0 && r != -EEXIST) {
+                log_link_message_warning_errno(link, m, r, "Failed to add DHCPv6 Prefix Delegation route");
+                link_enter_failed(link);
+                return 1;
+        }
+
+        if (link->dhcp6_pd_route_messages == 0) {
+                log_link_debug(link, "DHCPv6 prefix delegation routes set");
+                if (link->dhcp6_pd_prefixes_assigned)
+                        link->dhcp6_pd_route_configured = true;
+
+                r = dhcp6_pd_remove_old(link, false);
+                if (r < 0) {
+                        link_enter_failed(link);
+                        return 1;
+                }
+
+                link_check_ready(link);
+        }
+
+        return 1;
+}
+
+static int dhcp6_set_pd_route(Link *link, const union in_addr_union *prefix, const union in_addr_union *pd_prefix) {
+        _cleanup_(dhcp6_pd_freep) DHCP6DelegatedPrefix *pd = NULL;
+        _cleanup_(route_freep) Route *route = NULL;
+        Link *assigned_link;
+        Route *ret;
+        int r;
+
+        assert(link);
+        assert(link->manager);
+        assert(prefix);
+        assert(pd_prefix);
+
+        r = route_new(&route);
         if (r < 0)
                 return r;
 
-        r = sd_radv_prefix_set_preferred_lifetime(p, lifetime_preferred);
+        route->family = AF_INET6;
+        route->dst = *prefix;
+        route->dst_prefixlen = 64;
+
+        r = route_configure(route, link, dhcp6_pd_route_handler, &ret);
         if (r < 0)
-                return r;
+                return log_link_error_errno(link, r, "Failed to set DHCPv6 prefix route: %m");
 
-        r = sd_radv_prefix_set_valid_lifetime(p, lifetime_valid);
+        link->dhcp6_pd_route_messages++;
+
+        r = set_ensure_put(&link->dhcp6_pd_routes, &route_hash_ops, ret);
         if (r < 0)
-                return r;
+                return log_link_error_errno(link, r, "Failed to store DHCPv6 prefix route: %m");
+
+        (void) set_remove(link->dhcp6_pd_routes_old, ret);
+
+        assigned_link = dhcp6_pd_get_link_by_prefix(link, prefix);
+        if (assigned_link) {
+                assert(assigned_link == link);
+                return 0;
+        }
+
+        pd = new(DHCP6DelegatedPrefix, 1);
+        if (!pd)
+                return log_oom();
 
-        r = sd_radv_stop(radv);
+        *pd = (DHCP6DelegatedPrefix) {
+                .prefix = prefix->in6,
+                .pd_prefix = pd_prefix->in6,
+                .link = link_ref(link),
+        };
+
+        r = hashmap_ensure_allocated(&link->manager->dhcp6_prefixes, &in6_addr_hash_ops);
         if (r < 0)
-                return r;
+                return log_oom();
 
-        r = sd_radv_add_prefix(radv, p, true);
-        if (r < 0 && r != -EEXIST)
-                return r;
+        r = hashmap_put(link->manager->dhcp6_prefixes, &pd->prefix, pd);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Failed to store DHCPv6 prefix route at manager: %m");
 
-        r = dhcp6_prefix_add(link->manager, prefix, link);
+        r = set_ensure_put(&link->manager->dhcp6_pd_prefixes, &dhcp6_pd_hash_ops, pd);
         if (r < 0)
-                return r;
+                return log_link_error_errno(link, r, "Failed to store DHCPv6 prefix route at manager: %m");
 
-        return sd_radv_start(radv);
+        TAKE_PTR(pd);
+        return 0;
 }
 
-static int dhcp6_route_remove_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
+static int dhcp6_pd_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
         int r;
 
         assert(link);
+        assert(link->dhcp6_pd_address_messages > 0);
+
+        link->dhcp6_pd_address_messages--;
 
         if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
                 return 1;
 
         r = sd_netlink_message_get_errno(m);
-        if (r < 0)
-                log_link_message_warning_errno(link, m, r, "Received error on unreachable route removal for DHCPv6 delegated subnet");
+        if (r < 0 && r != -EEXIST) {
+                log_link_message_warning_errno(link, m, r, "Could not set DHCPv6 delegated prefix address");
+                link_enter_failed(link);
+                return 1;
+        } else if (r >= 0)
+                (void) manager_rtnl_process_address(rtnl, m, link->manager);
+
+        if (link->dhcp6_pd_address_messages == 0) {
+                log_link_debug(link, "DHCPv6 delegated prefix addresses set");
+                if (link->dhcp6_pd_prefixes_assigned)
+                        link->dhcp6_pd_address_configured = true;
+
+                r = dhcp6_pd_remove_old(link, false);
+                if (r < 0) {
+                        link_enter_failed(link);
+                        return 1;
+                }
+
+                r = link_request_set_routes(link);
+                if (r < 0) {
+                        link_enter_failed(link);
+                        return 1;
+                }
+        }
 
         return 1;
 }
 
-int dhcp6_lease_pd_prefix_lost(sd_dhcp6_client *client, Link* link) {
-        int r;
-        sd_dhcp6_lease *lease;
-        union in_addr_union pd_prefix;
-        uint8_t pd_prefix_len;
-        uint32_t lifetime_preferred, lifetime_valid;
+static int dhcp6_set_pd_address(Link *link,
+                                const union in_addr_union *prefix,
+                                uint8_t prefix_len,
+                                uint32_t lifetime_preferred,
+                                uint32_t lifetime_valid) {
 
-        r = sd_dhcp6_client_get_lease(client, &lease);
-        if (r < 0)
-                return r;
+        _cleanup_(address_freep) Address *address = NULL;
+        Address *ret;
+        int r;
 
-        sd_dhcp6_lease_reset_pd_prefix_iter(lease);
+        assert(link);
+        assert(link->network);
+        assert(prefix);
 
-        while (sd_dhcp6_lease_get_pd(lease, &pd_prefix.in6, &pd_prefix_len,
-                                     &lifetime_preferred,
-                                     &lifetime_valid) >= 0) {
-                _cleanup_free_ char *buf = NULL;
-                _cleanup_(route_freep) Route *route = NULL;
+        if (!link->network->dhcp6_pd_assign)
+                return 0;
 
-                if (pd_prefix_len >= 64)
-                        continue;
+        r = address_new(&address);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Failed to allocate address for DHCPv6 delegated prefix: %m");
 
-                (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf);
+        address->in_addr = *prefix;
 
-                r = route_new(&route);
+        if (!in_addr_is_null(AF_INET6, &link->network->dhcp6_pd_token))
+                memcpy(address->in_addr.in6.s6_addr + 8, link->network->dhcp6_pd_token.in6.s6_addr + 8, 8);
+        else {
+                r = generate_ipv6_eui_64_address(link, &address->in_addr.in6);
                 if (r < 0)
-                        return r;
+                        return log_link_warning_errno(link, r, "Failed to generate EUI64 address for acquired DHCPv6 delegated prefix: %m");
+        }
 
-                route->family = AF_INET6;
-                route->dst = pd_prefix;
-                route->dst_prefixlen = pd_prefix_len;
-                route->type = RTN_UNREACHABLE;
+        address->prefixlen = prefix_len;
+        address->family = AF_INET6;
+        address->cinfo.ifa_prefered = lifetime_preferred;
+        address->cinfo.ifa_valid = lifetime_valid;
 
-                r = route_remove(route, link, dhcp6_route_remove_handler);
-                if (r < 0) {
-                        log_link_warning_errno(link, r, "Cannot delete unreachable route for DHCPv6 delegated subnet %s/%u: %m",
-                                               strnull(buf),
-                                               pd_prefix_len);
-                        continue;
-                }
+        r = address_configure(address, link, dhcp6_pd_address_handler, true, &ret);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Failed to set DHCPv6 delegated prefix address: %m");
 
-                log_link_debug(link, "Removing unreachable route %s/%u",
-                               strnull(buf), pd_prefix_len);
-        }
+        link->dhcp6_pd_address_messages++;
+
+        r = set_ensure_put(&link->dhcp6_pd_addresses, &address_hash_ops, ret);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Failed to store DHCPv6 delegated prefix address: %m");
+
+        (void) set_remove(link->dhcp6_pd_addresses_old, ret);
 
         return 0;
 }
 
-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;
-        uint64_t n_prefixes, n_used = 0;
-        _cleanup_free_ char *buf = NULL;
-        _cleanup_free_ char *assigned_buf = NULL;
+static int dhcp6_pd_assign_prefix(Link *link, const union in_addr_union *prefix, const union in_addr_union *pd_prefix,
+                                  uint8_t prefix_len, uint32_t lifetime_preferred, uint32_t lifetime_valid) {
         int r;
 
-        assert(manager);
-        assert(pd_prefix_len <= 64);
+        assert(link);
+        assert(prefix);
 
-        prefix.in6 = *pd_prefix;
+        r = radv_add_prefix(link, &prefix->in6, prefix_len, lifetime_preferred, lifetime_valid);
+        if (r < 0)
+                return r;
+
+        r = dhcp6_set_pd_route(link, prefix, pd_prefix);
+        if (r < 0)
+                return r;
 
-        r = in_addr_mask(AF_INET6, &prefix, pd_prefix_len);
+        r = dhcp6_set_pd_address(link, prefix, prefix_len, lifetime_preferred, lifetime_valid);
         if (r < 0)
                 return r;
 
+        return 0;
+}
+
+bool link_dhcp6_pd_is_enabled(Link *link) {
+        if (!link->network)
+                return false;
+
+        return link->network->router_prefix_delegation & RADV_PREFIX_DELEGATION_DHCP6;
+}
+
+static bool link_has_preferred_subnet_id(Link *link) {
+        if (!link->network)
+                return false;
+
+        return link->network->dhcp6_pd_subnet_id >= 0;
+}
+
+static int dhcp6_get_preferred_delegated_prefix(
+                Link *link,
+                const union in_addr_union *masked_pd_prefix,
+                uint8_t pd_prefix_len,
+                union in_addr_union *ret) {
+
+        /* We start off with the original PD prefix we have been assigned and iterate from there */
+        union in_addr_union prefix;
+        uint64_t n_prefixes;
+        Link *assigned_link;
+        int r;
+
+        assert(link);
+        assert(link->manager);
+        assert(masked_pd_prefix);
+        assert(pd_prefix_len <= 64);
+
         n_prefixes = UINT64_C(1) << (64 - pd_prefix_len);
+        prefix = *masked_pd_prefix;
 
-        (void) in_addr_to_string(AF_INET6, &prefix, &buf);
-        log_link_debug(dhcp6_link, "Assigning up to %" PRIu64 " prefixes from %s/%u",
-                       n_prefixes, strnull(buf), pd_prefix_len);
+        if (link_has_preferred_subnet_id(link)) {
+                uint64_t subnet_id = link->network->dhcp6_pd_subnet_id;
 
-        while (hashmap_iterate(manager->links, i, (void **)&link, NULL)) {
-                Link *assigned_link;
+                /* If the link has a preference for a particular subnet id try to allocate that */
+                if (subnet_id >= n_prefixes)
+                        return log_link_warning_errno(link, SYNTHETIC_ERRNO(ERANGE),
+                                                      "subnet id %" PRIu64 " is out of range. Only have %" PRIu64 " subnets.",
+                                                      subnet_id, n_prefixes);
 
-                if (n_used == n_prefixes) {
-                        log_link_debug(dhcp6_link, "Assigned %" PRIu64 "/%" PRIu64 " prefixes from %s/%u",
-                                       n_used, n_prefixes, strnull(buf), pd_prefix_len);
+                r = in_addr_prefix_nth(AF_INET6, &prefix, 64, subnet_id);
+                if (r < 0)
+                        return log_link_warning_errno(link, r,
+                                                      "subnet id %" PRIu64 " is out of range. Only have %" PRIu64 " subnets.",
+                                                      subnet_id, n_prefixes);
+
+                /* Verify that the prefix we did calculate fits in the pd prefix.
+                 * This should not fail as we checked the prefix size beforehand */
+                assert_se(in_addr_prefix_covers(AF_INET6, masked_pd_prefix, pd_prefix_len, &prefix) > 0);
+
+                assigned_link = dhcp6_pd_get_link_by_prefix(link, &prefix);
+                if (assigned_link && assigned_link != link) {
+                        _cleanup_free_ char *assigned_buf = NULL;
+
+                        (void) in_addr_to_string(AF_INET6, &prefix, &assigned_buf);
+                        return log_link_warning_errno(link, SYNTHETIC_ERRNO(EAGAIN),
+                                                      "The requested prefix %s is already assigned to another link.",
+                                                      strna(assigned_buf));
+                }
+
+                *ret = prefix;
+                return 0;
+        }
 
-                        return -EAGAIN;
+        for (uint64_t n = 0; n < n_prefixes; n++) {
+                /* If we do not have an allocation preference just iterate
+                 * through the address space and return the first free prefix. */
+                assigned_link = dhcp6_pd_get_link_by_prefix(link, &prefix);
+                if (!assigned_link || assigned_link == link) {
+                        *ret = prefix;
+                        return 0;
                 }
 
+                r = in_addr_prefix_next(AF_INET6, &prefix, 64);
+                if (r < 0)
+                        return log_link_warning_errno(link, r, "Can't allocate another prefix. Out of address space?: %m");
+        }
+
+        return log_link_warning_errno(link, SYNTHETIC_ERRNO(ERANGE), "Couldn't find a suitable prefix. Ran out of address space.");
+}
+
+static void dhcp6_pd_prefix_distribute(Link *dhcp6_link,
+                                      const union in_addr_union *masked_pd_prefix,
+                                      uint8_t pd_prefix_len,
+                                      uint32_t lifetime_preferred,
+                                      uint32_t lifetime_valid,
+                                      bool assign_preferred_subnet_id) {
+
+        Iterator i;
+        Link *link;
+        int r;
+
+        assert(dhcp6_link);
+        assert(dhcp6_link->manager);
+        assert(masked_pd_prefix);
+        assert(pd_prefix_len <= 64);
+
+        HASHMAP_FOREACH(link, dhcp6_link->manager->links, i) {
+                _cleanup_free_ char *assigned_buf = NULL;
+                union in_addr_union assigned_prefix;
+
                 if (link == dhcp6_link)
                         continue;
 
-                if (!dhcp6_get_prefix_delegation(link))
+                if (!link_dhcp6_pd_is_enabled(link))
                         continue;
 
-                assigned_link = dhcp6_prefix_get(manager, &prefix.in6);
-                if (assigned_link && assigned_link != link)
+                if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
                         continue;
 
-                (void) in_addr_to_string(AF_INET6, &prefix, &assigned_buf);
-                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/64 from %s/%u for link: %m",
-                                             assigned_link ? "update": "assign",
-                                             strnull(assigned_buf),
-                                             strnull(buf), pd_prefix_len);
+                if (assign_preferred_subnet_id != link_has_preferred_subnet_id(link))
+                        continue;
 
-                        if (!assigned_link)
+                r = dhcp6_pd_get_assigned_prefix(link, masked_pd_prefix, &assigned_prefix);
+                if (r < 0) {
+                        r = dhcp6_get_preferred_delegated_prefix(link, masked_pd_prefix, pd_prefix_len, &assigned_prefix);
+                        if (r < 0) {
+                                link->dhcp6_pd_prefixes_assigned = false;
                                 continue;
+                        }
+                }
 
+                (void) in_addr_to_string(AF_INET6, &assigned_prefix, &assigned_buf);
+                r = dhcp6_pd_assign_prefix(link, &assigned_prefix, masked_pd_prefix, 64,
+                                           lifetime_preferred, lifetime_valid);
+                if (r < 0) {
+                        log_link_error_errno(link, r, "Unable to assign/update prefix %s/64: %m",
+                                             strna(assigned_buf));
+                        link_enter_failed(link);
                 } else
-                        log_link_debug(link, "Assigned prefix %" PRIu64 "/%" PRIu64 " %s/64 from %s/%u to link",
-                                       n_used + 1, n_prefixes,
-                                       strnull(assigned_buf),
-                                       strnull(buf), pd_prefix_len);
+                        log_link_debug(link, "Assigned prefix %s/64", strna(assigned_buf));
+        }
+}
 
-                n_used++;
+static int dhcp6_pd_prepare(Link *link) {
+        Address *address;
+        Route *route;
+        int r;
 
-                r = in_addr_prefix_next(AF_INET6, &prefix, 64);
-                if (r < 0 && n_used < n_prefixes)
-                        return r;
+        if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+                return 0;
+
+        if (!link_dhcp6_pd_is_enabled(link))
+                return 0;
+
+        link_dirty(link);
+
+        link->dhcp6_pd_address_configured = false;
+        link->dhcp6_pd_route_configured = false;
+        link->dhcp6_pd_prefixes_assigned = true;
+
+        while ((address = set_steal_first(link->dhcp6_pd_addresses))) {
+                r = set_ensure_put(&link->dhcp6_pd_addresses_old, &address_hash_ops, address);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Failed to store old DHCPv6 Prefix Delegation address: %m");
+        }
+
+        while ((route = set_steal_first(link->dhcp6_pd_routes))) {
+                r = set_ensure_put(&link->dhcp6_pd_routes_old, &route_hash_ops, route);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Failed to store old DHCPv6 Prefix Delegation route: %m");
         }
 
         return 0;
 }
 
-static int dhcp6_route_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
+static int dhcp6_pd_finalize(Link *link) {
         int r;
 
-        assert(link);
-
         if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
-                return 1;
+                return 0;
 
-        r = sd_netlink_message_get_errno(m);
-        if (r < 0 && r != -EEXIST)
-                log_link_message_warning_errno(link, m, r, "Received error when adding unreachable route for DHCPv6 delegated subnet");
+        if (!link_dhcp6_pd_is_enabled(link))
+                return 0;
 
-        return 1;
-}
+        if (link->dhcp6_pd_address_messages == 0) {
+                if (link->dhcp6_pd_prefixes_assigned)
+                        link->dhcp6_pd_address_configured = true;
+        } else {
+                log_link_debug(link, "Setting DHCPv6 PD addresses");
+                /* address_handler calls link_request_set_routes() and link_request_set_nexthop().
+                 * Before they are called, the related flags must be cleared. Otherwise, the link
+                 * becomes configured state before routes are configured. */
+                link->static_routes_configured = false;
+                link->static_nexthops_configured = false;
+        }
 
-static int dhcp6_lease_pd_prefix_acquired(sd_dhcp6_client *client, Link *link) {
-        int r;
-        sd_dhcp6_lease *lease;
-        union in_addr_union pd_prefix;
-        uint8_t pd_prefix_len;
-        uint32_t lifetime_preferred, lifetime_valid;
-        Iterator i = ITERATOR_FIRST;
+        if (link->dhcp6_pd_route_messages == 0) {
+                if (link->dhcp6_pd_prefixes_assigned)
+                        link->dhcp6_pd_route_configured = true;
+        } else
+                log_link_debug(link, "Setting DHCPv6 PD routes");
 
-        r = sd_dhcp6_client_get_lease(client, &lease);
+        r = dhcp6_pd_remove_old(link, false);
         if (r < 0)
                 return r;
 
-        sd_dhcp6_lease_reset_pd_prefix_iter(lease);
+        if (link->dhcp6_pd_address_configured && link->dhcp6_pd_route_configured)
+                link_check_ready(link);
+        else
+                link_set_state(link, LINK_STATE_CONFIGURING);
 
-        while (sd_dhcp6_lease_get_pd(lease, &pd_prefix.in6, &pd_prefix_len,
-                                     &lifetime_preferred,
-                                     &lifetime_valid) >= 0) {
+        return 0;
+}
 
-                _cleanup_free_ char *buf = NULL;
+static void dhcp6_pd_prefix_lost(Link *dhcp6_link) {
+        Link *link;
+        Iterator i;
+        int r;
 
-                (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf);
+        assert(dhcp6_link);
+        assert(dhcp6_link->manager);
 
-                if (pd_prefix_len > 64) {
-                        log_link_debug(link, "PD Prefix length > 64, ignoring prefix %s/%u",
-                                       strnull(buf), pd_prefix_len);
+        HASHMAP_FOREACH(link, dhcp6_link->manager->links, i) {
+                if (link == dhcp6_link)
                         continue;
-                }
 
-                if (pd_prefix_len < 48)
-                        log_link_warning(link, "PD Prefix length < 48, looks unusual %s/%u",
-                                       strnull(buf), pd_prefix_len);
+                if (!link_dhcp6_pd_is_enabled(link))
+                        continue;
 
-                if (pd_prefix_len < 64) {
-                        _cleanup_(route_freep) Route *route = NULL;
+                r = dhcp6_pd_remove(link);
+                if (r < 0)
+                        link_enter_failed(link);
+        }
+}
 
-                        r = route_new(&route);
-                        if (r < 0)
-                                return r;
+static int dhcp6_remove_old(Link *link, bool force);
 
-                        route->family = AF_INET6;
-                        route->dst = pd_prefix;
-                        route->dst_prefixlen = pd_prefix_len;
-                        route->table = link_get_dhcp_route_table(link);
-                        route->type = RTN_UNREACHABLE;
+static int dhcp6_address_callback(Address *address) {
+        Address *a;
+        Iterator i;
 
-                        r = route_configure(route, link, dhcp6_route_handler);
-                        if (r < 0) {
-                                log_link_warning_errno(link, r, "Cannot configure unreachable route for delegated subnet %s/%u: %m",
-                                                       strnull(buf),
-                                                       pd_prefix_len);
-                                continue;
+        assert(address);
+        assert(address->link);
+
+        /* Make this called only once */
+        SET_FOREACH(a, address->link->dhcp6_addresses, i)
+                a->callback = NULL;
+
+        return dhcp6_remove_old(address->link, true);
+}
+
+static int dhcp6_remove_old(Link *link, bool force) {
+        Address *address;
+        Route *route;
+        Iterator i;
+        int k, r = 0;
+
+        assert(link);
+
+        if (!force && (!link->dhcp6_address_configured || !link->dhcp6_route_configured))
+                return 0;
+
+        if (set_isempty(link->dhcp6_addresses_old) && set_isempty(link->dhcp6_routes_old))
+                return 0;
+
+        if (!force) {
+                bool set_callback = !set_isempty(link->dhcp6_addresses);
+
+                SET_FOREACH(address, link->dhcp6_addresses, i)
+                        if (address_is_ready(address)) {
+                                set_callback = false;
+                                break;
                         }
 
-                        log_link_debug(link, "Configuring unreachable route for %s/%u",
-                                       strnull(buf), pd_prefix_len);
-                } else
-                        log_link_debug(link, "Not adding a blocking route since distributed prefix is /64");
+                if (set_callback) {
+                        SET_FOREACH(address, link->dhcp6_addresses, i)
+                                address->callback = dhcp6_address_callback;
+                        return 0;
+                }
+        }
 
-                r = dhcp6_pd_prefix_distribute(link, &i, &pd_prefix.in6,
-                                               pd_prefix_len,
-                                               lifetime_preferred,
-                                               lifetime_valid);
-                if (r < 0 && r != -EAGAIN)
-                        return r;
+        log_link_debug(link, "Removing old DHCPv6 addresses and routes.");
 
-                if (r >= 0)
-                        i = ITERATOR_FIRST;
+        link_dirty(link);
+
+        SET_FOREACH(route, link->dhcp6_routes_old, i) {
+                k = route_remove(route, link, NULL);
+                if (k < 0)
+                        r = k;
         }
 
-        return 0;
+        SET_FOREACH(address, link->dhcp6_addresses_old, i) {
+                k = address_remove(address, link, NULL);
+                if (k < 0)
+                        r = k;
+        }
+
+        return r;
 }
 
-int dhcp6_request_prefix_delegation(Link *link) {
-        Link *l;
+static int dhcp6_remove(Link *link) {
+        Address *address;
+        Route *route;
         Iterator i;
+        int k, r = 0;
 
-        assert_return(link, -EINVAL);
-        assert_return(link->manager, -EOPNOTSUPP);
+        assert(link);
 
-        if (dhcp6_get_prefix_delegation(link) <= 0)
-                return 0;
+        link->dhcp6_address_configured = false;
+        link->dhcp6_route_configured = false;
 
-        log_link_debug(link, "Requesting DHCPv6 prefixes to be delegated for new link");
+        k = dhcp6_remove_old(link, true);
+        if (k < 0)
+                r = k;
 
-        HASHMAP_FOREACH(l, link->manager->links, i) {
-                int r, enabled;
+        if (set_isempty(link->dhcp6_addresses) && set_isempty(link->dhcp6_routes))
+                return r;
 
-                if (l == link)
-                        continue;
+        log_link_debug(link, "Removing DHCPv6 addresses and routes.");
 
-                if (!l->dhcp6_client)
-                        continue;
+        link_dirty(link);
 
-                r = sd_dhcp6_client_get_prefix_delegation(l->dhcp6_client, &enabled);
+        SET_FOREACH(route, link->dhcp6_routes, i) {
+                k = route_remove(route, link, NULL);
+                if (k < 0)
+                        r = k;
+        }
+
+        SET_FOREACH(address, link->dhcp6_addresses, i) {
+                k = address_remove(address, link, NULL);
+                if (k < 0)
+                        r = k;
+        }
+
+        return r;
+}
+
+static int dhcp6_route_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
+        int r;
+
+        assert(link);
+        assert(link->dhcp6_route_messages > 0);
+
+        link->dhcp6_route_messages--;
+
+        if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+                return 1;
+
+        r = sd_netlink_message_get_errno(m);
+        if (r < 0 && r != -EEXIST) {
+                log_link_message_warning_errno(link, m, r, "Failed to add unreachable route for DHCPv6 delegated subnet");
+                link_enter_failed(link);
+                return 1;
+        }
+
+        if (link->dhcp6_route_messages == 0) {
+                log_link_debug(link, "Unreachable routes for DHCPv6 delegated subnets set");
+                link->dhcp6_route_configured = true;
+
+                r = dhcp6_remove_old(link, false);
                 if (r < 0) {
-                        log_link_warning_errno(l, r, "Cannot get prefix delegation when adding new link");
-                        continue;
+                        link_enter_failed(link);
+                        return 1;
                 }
 
-                if (enabled == 0) {
-                        r = sd_dhcp6_client_set_prefix_delegation(l->dhcp6_client, 1);
-                        if (r < 0) {
-                                log_link_warning_errno(l, r, "Cannot enable prefix delegation when adding new link");
-                                continue;
-                        }
-                }
+                link_check_ready(link);
+        }
 
-                r = sd_dhcp6_client_is_running(l->dhcp6_client);
-                if (r <= 0)
-                        continue;
+        return 1;
+}
 
-                if (enabled != 0) {
-                        log_link_debug(l, "Requesting re-assignment of delegated prefixes after adding new link");
-                        (void) dhcp6_lease_pd_prefix_acquired(l->dhcp6_client, l);
+static int dhcp6_set_unreachable_route(Link *link, const union in_addr_union *addr, uint8_t prefixlen) {
+        _cleanup_(route_freep) Route *route = NULL;
+        _cleanup_free_ char *buf = NULL;
+        Route *ret;
+        int r;
+
+        assert(link);
+        assert(addr);
+
+        (void) in_addr_to_string(AF_INET6, addr, &buf);
+
+        if (prefixlen > 64) {
+                log_link_debug(link, "PD Prefix length > 64, ignoring prefix %s/%u",
+                               strna(buf), prefixlen);
+                return 0;
+        }
+
+        if (prefixlen == 64) {
+                log_link_debug(link, "Not adding a blocking route for DHCPv6 delegated subnet %s/64 since distributed prefix is 64",
+                               strna(buf));
+                return 1;
+        }
+
+        if (prefixlen < 48)
+                log_link_warning(link, "PD Prefix length < 48, looks unusual %s/%u",
+                                 strna(buf), prefixlen);
+
+        r = route_new(&route);
+        if (r < 0)
+                return log_oom();
+
+        route->family = AF_INET6;
+        route->dst = *addr;
+        route->dst_prefixlen = prefixlen;
+        route->table = link_get_dhcp_route_table(link);
+        route->type = RTN_UNREACHABLE;
+
+        r = route_configure(route, link, dhcp6_route_handler, &ret);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Failed to set unreachable route for DHCPv6 delegated subnet %s/%u: %m",
+                                            strna(buf), prefixlen);
+
+        link->dhcp6_route_messages++;
+
+        r = set_ensure_put(&link->dhcp6_routes, &route_hash_ops, ret);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Failed to store unreachable route for DHCPv6 delegated subnet %s/%u: %m",
+                                            strna(buf), prefixlen);
+
+        (void) set_remove(link->dhcp6_routes_old, ret);
+
+        return 1;
+}
+
+static int dhcp6_pd_prefix_acquired(Link *dhcp6_link) {
+        Iterator i;
+        Link *link;
+        int r;
 
+        assert(dhcp6_link);
+        assert(dhcp6_link->dhcp6_lease);
+
+        HASHMAP_FOREACH(link, dhcp6_link->manager->links, i) {
+                if (link == dhcp6_link)
                         continue;
-                }
 
-                r = sd_dhcp6_client_stop(l->dhcp6_client);
-                if (r < 0) {
-                        log_link_warning_errno(l, r, "Cannot stop DHCPv6 prefix delegation client after adding new link");
+                r = dhcp6_pd_prepare(link);
+                if (r < 0)
+                        link_enter_failed(link);
+        }
+
+        for (sd_dhcp6_lease_reset_pd_prefix_iter(dhcp6_link->dhcp6_lease);;) {
+                uint32_t lifetime_preferred, lifetime_valid;
+                union in_addr_union pd_prefix, prefix;
+                uint8_t pd_prefix_len;
+
+                r = sd_dhcp6_lease_get_pd(dhcp6_link->dhcp6_lease, &pd_prefix.in6, &pd_prefix_len,
+                                          &lifetime_preferred, &lifetime_valid);
+                if (r < 0)
+                        break;
+
+                r = dhcp6_set_unreachable_route(dhcp6_link, &pd_prefix, pd_prefix_len);
+                if (r < 0)
+                        return r;
+                if (r == 0)
                         continue;
+
+                /* We are doing prefix allocation in two steps:
+                 * 1. all those links that have a preferred subnet id will be assigned their subnet
+                 * 2. all those links that remain will receive prefixes in sequential order. Prefixes
+                 *    that were previously already allocated to another link will be skipped.
+                 * The assignment has to be split in two phases since subnet id
+                 * preferences should be honored. Meaning that any subnet id should be
+                 * handed out to the requesting link and not to some link that didn't
+                 * specify any preference. */
+
+                assert(pd_prefix_len <= 64);
+
+                prefix = pd_prefix;
+                r = in_addr_mask(AF_INET6, &prefix, pd_prefix_len);
+                if (r < 0)
+                        return log_link_error_errno(dhcp6_link, r, "Failed to mask DHCPv6 PD prefix: %m");
+
+                if (DEBUG_LOGGING) {
+                        uint64_t n_prefixes = UINT64_C(1) << (64 - pd_prefix_len);
+                        _cleanup_free_ char *buf = NULL;
+
+                        (void) in_addr_to_string(AF_INET6, &prefix, &buf);
+                        log_link_debug(dhcp6_link, "Assigning up to %" PRIu64 " prefixes from %s/%u",
+                                       n_prefixes, strna(buf), pd_prefix_len);
                 }
 
-                r = sd_dhcp6_client_start(l->dhcp6_client);
-                if (r < 0) {
-                        log_link_warning_errno(l, r, "Cannot restart DHCPv6 prefix delegation client after adding new link");
+                dhcp6_pd_prefix_distribute(dhcp6_link,
+                                           &prefix,
+                                           pd_prefix_len,
+                                           lifetime_preferred,
+                                           lifetime_valid,
+                                           true);
+
+                dhcp6_pd_prefix_distribute(dhcp6_link,
+                                           &prefix,
+                                           pd_prefix_len,
+                                           lifetime_preferred,
+                                           lifetime_valid,
+                                           false);
+        }
+
+        HASHMAP_FOREACH(link, dhcp6_link->manager->links, i) {
+                if (link == dhcp6_link)
                         continue;
-                }
 
-                log_link_debug(l, "Restarted DHCPv6 client to acquire prefix delegations after adding new link");
+                r = dhcp6_pd_finalize(link);
+                if (r < 0)
+                        link_enter_failed(link);
         }
 
         return 0;
@@ -396,6 +934,9 @@ static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *
         int r;
 
         assert(link);
+        assert(link->dhcp6_address_messages > 0);
+
+        link->dhcp6_address_messages--;
 
         if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
                 return 1;
@@ -408,28 +949,40 @@ static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *
         } else if (r >= 0)
                 (void) manager_rtnl_process_address(rtnl, m, link->manager);
 
-        r = link_request_set_routes(link);
-        if (r < 0) {
-                link_enter_failed(link);
-                return 1;
+        if (link->dhcp6_address_messages == 0) {
+                log_link_debug(link, "DHCPv6 addresses set");
+                link->dhcp6_address_configured = true;
+
+                r = dhcp6_remove_old(link, false);
+                if (r < 0) {
+                        link_enter_failed(link);
+                        return 1;
+                }
+
+                r = link_request_set_routes(link);
+                if (r < 0) {
+                        link_enter_failed(link);
+                        return 1;
+                }
         }
 
         return 1;
 }
 
-static int dhcp6_address_change(
+static int dhcp6_update_address(
                 Link *link,
-                struct in6_addr *ip6_addr,
+                const struct in6_addr *ip6_addr,
                 uint32_t lifetime_preferred,
                 uint32_t lifetime_valid) {
 
         _cleanup_(address_freep) Address *addr = NULL;
         _cleanup_free_ char *buffer = NULL;
+        Address *ret;
         int r;
 
         r = address_new(&addr);
         if (r < 0)
-                return r;
+                return log_oom();
 
         addr->family = AF_INET6;
         addr->in_addr.in6 = *ip6_addr;
@@ -439,44 +992,146 @@ static int dhcp6_address_change(
         addr->cinfo.ifa_valid = lifetime_valid;
 
         (void) in_addr_to_string(addr->family, &addr->in_addr, &buffer);
-        log_link_info(link,
-                      "DHCPv6 address %s/%d timeout preferred %d valid %d",
-                      strnull(buffer), addr->prefixlen, lifetime_preferred, lifetime_valid);
+        log_link_info(link, "DHCPv6 address %s/%u timeout preferred %d valid %d",
+                      strna(buffer), addr->prefixlen, lifetime_preferred, lifetime_valid);
 
-        r = address_configure(addr, link, dhcp6_address_handler, true);
+        r = address_configure(addr, link, dhcp6_address_handler, true, &ret);
         if (r < 0)
-                return log_link_warning_errno(link, r, "Could not assign DHCPv6 address: %m");
+                return log_link_error_errno(link, r, "Failed to set DHCPv6 address %s/%u: %m",
+                                            strna(buffer), addr->prefixlen);
+
+        link->dhcp6_address_messages++;
+
+        r = set_ensure_put(&link->dhcp6_addresses, &address_hash_ops, ret);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Failed to store DHCPv6 address %s/%u: %m",
+                                            strna(buffer), addr->prefixlen);
+
+        (void) set_remove(link->dhcp6_addresses_old, ret);
 
         return 0;
 }
 
-static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link) {
+static int dhcp6_address_acquired(Link *link) {
         int r;
+
+        assert(link);
+        assert(link->dhcp6_lease);
+
+        for (sd_dhcp6_lease_reset_address_iter(link->dhcp6_lease);;) {
+                uint32_t lifetime_preferred, lifetime_valid;
+                struct in6_addr ip6_addr;
+
+                r = sd_dhcp6_lease_get_address(link->dhcp6_lease, &ip6_addr, &lifetime_preferred, &lifetime_valid);
+                if (r < 0)
+                        break;
+
+                r = dhcp6_update_address(link, &ip6_addr, lifetime_preferred, lifetime_valid);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+static int dhcp6_lease_ip_acquired(sd_dhcp6_client *client, Link *link) {
+        _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease_old = NULL;
         sd_dhcp6_lease *lease;
-        struct in6_addr ip6_addr;
-        uint32_t lifetime_preferred, lifetime_valid;
+        Address *a;
+        Route *rt;
+        int r;
+
+        link->dhcp6_address_configured = false;
+        link->dhcp6_route_configured = false;
+
+        link_dirty(link);
+
+        while ((a = set_steal_first(link->dhcp6_addresses))) {
+                r = set_ensure_put(&link->dhcp6_addresses_old, &address_hash_ops, a);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Failed to store old DHCPv6 address: %m");
+        }
+
+        while ((rt = set_steal_first(link->dhcp6_routes))) {
+                r = set_ensure_put(&link->dhcp6_routes_old, &route_hash_ops, rt);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Failed to store old DHCPv6 route: %m");
+        }
 
         r = sd_dhcp6_client_get_lease(client, &lease);
         if (r < 0)
-                return r;
+                return log_link_error_errno(link, r, "Failed to get DHCPv6 lease: %m");
 
-        sd_dhcp6_lease_reset_address_iter(lease);
+        lease_old = TAKE_PTR(link->dhcp6_lease);
+        link->dhcp6_lease = sd_dhcp6_lease_ref(lease);
 
-        while (sd_dhcp6_lease_get_address(lease, &ip6_addr,
-                                                 &lifetime_preferred,
-                                                 &lifetime_valid) >= 0) {
+        r = dhcp6_address_acquired(link);
+        if (r < 0)
+                return r;
 
-                r = dhcp6_address_change(link, &ip6_addr, lifetime_preferred, lifetime_valid);
+        if (dhcp6_lease_has_pd_prefix(lease)) {
+                r = dhcp6_pd_prefix_acquired(link);
                 if (r < 0)
                         return r;
+        } else if (dhcp6_lease_has_pd_prefix(lease_old))
+                /* When we had PD prefixes but not now, we need to remove them. */
+                dhcp6_pd_prefix_lost(link);
+
+        if (link->dhcp6_address_messages == 0)
+                link->dhcp6_address_configured = true;
+        else {
+                log_link_debug(link, "Setting DHCPv6 addresses");
+                /* address_handler calls link_request_set_routes() and link_request_set_nexthop().
+                 * Before they are called, the related flags must be cleared. Otherwise, the link
+                 * becomes configured state before routes are configured. */
+                link->static_routes_configured = false;
+                link->static_nexthops_configured = false;
         }
 
+        if (link->dhcp6_route_messages == 0)
+                link->dhcp6_route_configured = true;
+        else
+                log_link_debug(link, "Setting unreachable routes for DHCPv6 delegated subnets");
+
+        r = dhcp6_remove_old(link, false);
+        if (r < 0)
+                return r;
+
+        if (link->dhcp6_address_configured && link->dhcp6_route_configured)
+                link_check_ready(link);
+        else
+                link_set_state(link, LINK_STATE_CONFIGURING);
+
         return 0;
 }
 
-static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
+static int dhcp6_lease_information_acquired(sd_dhcp6_client *client, Link *link) {
+        return 0;
+}
+
+static int dhcp6_lease_lost(Link *link) {
         int r;
+
+        assert(link);
+        assert(link->manager);
+
+        log_link_info(link, "DHCPv6 lease lost");
+
+        if (dhcp6_lease_has_pd_prefix(link->dhcp6_lease))
+                dhcp6_pd_prefix_lost(link);
+
+        link->dhcp6_lease = sd_dhcp6_lease_unref(link->dhcp6_lease);
+
+        r = dhcp6_remove(link);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
         Link *link = userdata;
+        int r;
 
         assert(link);
         assert(link->network);
@@ -484,41 +1139,27 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
         if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
                 return;
 
-        switch(event) {
+        switch (event) {
         case SD_DHCP6_CLIENT_EVENT_STOP:
         case SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE:
         case SD_DHCP6_CLIENT_EVENT_RETRANS_MAX:
-                if (sd_dhcp6_client_get_lease(client, NULL) >= 0)
-                        log_link_warning(link, "DHCPv6 lease lost");
-
-                (void) dhcp6_lease_pd_prefix_lost(client, link);
-                (void) dhcp6_prefix_remove_all(link->manager, link);
-
-                link_dirty(link);
-                link->dhcp6_configured = false;
+                r = dhcp6_lease_lost(link);
+                if (r < 0)
+                        link_enter_failed(link);
                 break;
 
         case SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE:
-                r = dhcp6_lease_address_acquired(client, link);
+                r = dhcp6_lease_ip_acquired(client, link);
                 if (r < 0) {
                         link_enter_failed(link);
                         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);
-                if (r < 0) {
+                if (r < 0)
                         link_enter_failed(link);
-                        return;
-                }
-
-                link_dirty(link);
-                link->dhcp6_configured = true;
                 break;
 
         default:
@@ -528,8 +1169,6 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
                         log_link_warning(link, "DHCPv6 unknown event: %d", event);
                 return;
         }
-
-        link_check_ready(link);
 }
 
 int dhcp6_request_address(Link *link, int ir) {
@@ -539,7 +1178,7 @@ int dhcp6_request_address(Link *link, int ir) {
         assert(link);
         assert(link->dhcp6_client);
         assert(link->network);
-        assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*)&link->ipv6ll_address) > 0);
+        assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address) > 0);
 
         r = sd_dhcp6_client_is_running(link->dhcp6_client);
         if (r < 0)
@@ -553,9 +1192,8 @@ int dhcp6_request_address(Link *link, int ir) {
         if (pd && ir && link->network->dhcp6_force_pd_other_information) {
                 log_link_debug(link, "Enabling managed mode to request DHCPv6 PD with 'Other Information' set");
 
-                r = sd_dhcp6_client_set_address_request(link->dhcp6_client,
-                                                        false);
-                if (r < 0 )
+                r = sd_dhcp6_client_set_address_request(link->dhcp6_client, false);
+                if (r < 0)
                         return r;
 
                 ir = false;
@@ -589,6 +1227,81 @@ int dhcp6_request_address(Link *link, int ir) {
         return 0;
 }
 
+int dhcp6_request_prefix_delegation(Link *link) {
+        Link *l;
+        Iterator i;
+
+        assert(link);
+        assert(link->manager);
+
+        if (!link_dhcp6_pd_is_enabled(link))
+                return 0;
+
+        log_link_debug(link, "Requesting DHCPv6 prefixes to be delegated for new link");
+
+        HASHMAP_FOREACH(l, link->manager->links, i) {
+                int r, enabled;
+
+                if (l == link)
+                        continue;
+
+                if (!l->dhcp6_client)
+                        continue;
+
+                r = sd_dhcp6_client_get_prefix_delegation(l->dhcp6_client, &enabled);
+                if (r < 0) {
+                        log_link_warning_errno(l, r, "Cannot get prefix delegation when adding new link: %m");
+                        link_enter_failed(l);
+                        continue;
+                }
+
+                if (enabled == 0) {
+                        r = sd_dhcp6_client_set_prefix_delegation(l->dhcp6_client, 1);
+                        if (r < 0) {
+                                log_link_warning_errno(l, r, "Cannot enable prefix delegation when adding new link: %m");
+                                link_enter_failed(l);
+                                continue;
+                        }
+                }
+
+                r = sd_dhcp6_client_is_running(l->dhcp6_client);
+                if (r <= 0)
+                        continue;
+
+                if (enabled != 0) {
+                        if (dhcp6_lease_has_pd_prefix(l->dhcp6_lease)) {
+                                log_link_debug(l, "Requesting re-assignment of delegated prefixes after adding new link");
+                                r = dhcp6_pd_prefix_acquired(l);
+                                if (r < 0)
+                                        link_enter_failed(l);
+                        }
+                        continue;
+                }
+
+                r = sd_dhcp6_client_stop(l->dhcp6_client);
+                if (r < 0) {
+                        log_link_warning_errno(l, r, "Cannot stop DHCPv6 prefix delegation client after adding new link: %m");
+                        link_enter_failed(l);
+                        continue;
+                }
+
+                r = sd_dhcp6_client_start(l->dhcp6_client);
+                if (r < 0) {
+                        log_link_warning_errno(l, r, "Cannot restart DHCPv6 prefix delegation client after adding new link: %m");
+                        link_enter_failed(l);
+                        continue;
+                }
+
+                log_link_debug(l, "Restarted DHCPv6 client to acquire prefix delegations after adding new link");
+        }
+
+        /* dhcp6_pd_prefix_acquired() may make the link in failed state. */
+        if (link->state == LINK_STATE_FAILED)
+                return -ENOANO;
+
+        return 0;
+}
+
 static int dhcp6_set_hostname(sd_dhcp6_client *client, Link *link) {
         _cleanup_free_ char *hostname = NULL;
         const char *hn;
@@ -618,9 +1331,33 @@ static int dhcp6_set_hostname(sd_dhcp6_client *client, Link *link) {
         return 0;
 }
 
+static bool dhcp6_enable_prefix_delegation(Link *dhcp6_link) {
+        Link *link;
+        Iterator i;
+
+        assert(dhcp6_link);
+        assert(dhcp6_link->manager);
+
+        HASHMAP_FOREACH(link, dhcp6_link->manager->links, i) {
+                if (link == dhcp6_link)
+                        continue;
+
+                if (!link_dhcp6_pd_is_enabled(link))
+                        continue;
+
+                return true;
+        }
+
+        return false;
+}
+
 int dhcp6_configure(Link *link) {
         _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
+        sd_dhcp6_option *vendor_option;
+        sd_dhcp6_option *send_option;
+        void *request_options;
         const DUID *duid;
+        Iterator i;
         int r;
 
         assert(link);
@@ -662,6 +1399,14 @@ int dhcp6_configure(Link *link) {
         if (r < 0)
                 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set DUID: %m");
 
+        ORDERED_HASHMAP_FOREACH(send_option, link->network->dhcp6_client_send_options, i) {
+                r = sd_dhcp6_client_add_option(client, send_option);
+                if (r == -EEXIST)
+                        continue;
+                if (r < 0)
+                        return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set option: %m");
+        }
+
         r = dhcp6_set_hostname(client, link);
         if (r < 0)
                 return r;
@@ -676,6 +1421,44 @@ int dhcp6_configure(Link *link) {
                         return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set request flag for rapid commit: %m");
         }
 
+        if (link->network->dhcp6_mudurl) {
+                r = sd_dhcp6_client_set_request_mud_url(client, link->network->dhcp6_mudurl);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set MUD URL: %m");
+        }
+
+        SET_FOREACH(request_options, link->network->dhcp6_request_options, i) {
+                uint32_t option = PTR_TO_UINT32(request_options);
+
+                r = sd_dhcp6_client_set_request_option(client, option);
+                if (r == -EEXIST) {
+                        log_link_debug(link, "DHCP6 CLIENT: Failed to set request flag for '%u' already exists, ignoring.", option);
+                        continue;
+                }
+                if (r < 0)
+                        return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set request flag for '%u': %m", option);
+        }
+
+        if (link->network->dhcp6_user_class) {
+                r = sd_dhcp6_client_set_request_user_class(client, link->network->dhcp6_user_class);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set user class: %m");
+        }
+
+        if (link->network->dhcp6_vendor_class) {
+                r = sd_dhcp6_client_set_request_vendor_class(client, link->network->dhcp6_vendor_class);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set vendor class: %m");
+        }
+
+        ORDERED_HASHMAP_FOREACH(vendor_option, link->network->dhcp6_client_send_vendor_options, i) {
+                r = sd_dhcp6_client_add_vendor_option(client, vendor_option);
+                if (r == -EEXIST)
+                        continue;
+                if (r < 0)
+                        return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set vendor option: %m");
+        }
+
         r = sd_dhcp6_client_set_callback(client, dhcp6_handler, link);
         if (r < 0)
                 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set callback: %m");
@@ -697,142 +1480,177 @@ int dhcp6_configure(Link *link) {
         return 0;
 }
 
-static Link *dhcp6_prefix_get(Manager *m, struct in6_addr *addr) {
-        assert_return(m, NULL);
-        assert_return(addr, NULL);
-
-        return hashmap_get(m->dhcp6_prefixes, addr);
-}
-
-static int dhcp6_route_add_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
+int config_parse_dhcp6_pd_hint(
+                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 = data;
         int r;
 
-        assert(link);
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
 
-        if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
-                return 1;
+        r = in_addr_prefix_from_string(rvalue, AF_INET6, (union in_addr_union *) &network->dhcp6_pd_address, &network->dhcp6_pd_length);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse PrefixDelegationHint=%s, ignoring assignment", rvalue);
+                return 0;
+        }
 
-        r = sd_netlink_message_get_errno(m);
-        if (r < 0 && r != -EEXIST) {
-                log_link_message_warning_errno(link, m, r, "Received error adding DHCPv6 Prefix Delegation route");
-                link_enter_failed(link);
-                return 1;
+        if (network->dhcp6_pd_length < 1 || network->dhcp6_pd_length > 128) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid prefix length='%d', ignoring assignment", network->dhcp6_pd_length);
+                network->dhcp6_pd_length = 0;
+                return 0;
         }
 
-        return 1;
+        return 0;
 }
 
-static int dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link) {
-        _cleanup_(route_freep) Route *route = NULL;
-        _cleanup_free_ struct in6_addr *a = NULL;
-        _cleanup_free_ char *buf = NULL;
-        Link *assigned_link;
+int config_parse_dhcp6_mud_url(
+                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_ char *unescaped = NULL;
+        Network *network = data;
         int r;
 
-        assert_return(m, -EINVAL);
-        assert_return(addr, -EINVAL);
-
-        r = route_new(&route);
-        if (r < 0)
-                return r;
-
-        route->family = AF_INET6;
-        route->dst.in6 = *addr;
-        route->dst_prefixlen = 64;
-
-        r = route_configure(route, link, dhcp6_route_add_handler);
-        if (r < 0)
-                return r;
-
-        (void) in_addr_to_string(AF_INET6, (union in_addr_union *) addr, &buf);
-        log_link_debug(link, "Adding prefix route %s/64", strnull(buf));
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
 
-        assigned_link = hashmap_get(m->dhcp6_prefixes, addr);
-        if (assigned_link) {
-                assert(assigned_link == link);
+        if (isempty(rvalue)) {
+                network->dhcp6_mudurl = mfree(network->dhcp6_mudurl);
                 return 0;
         }
 
-        a = newdup(struct in6_addr, addr, 1);
-        if (!a)
-                return -ENOMEM;
-
-        r = hashmap_ensure_allocated(&m->dhcp6_prefixes, &in6_addr_hash_ops);
-        if (r < 0)
-                return r;
+        r = cunescape(rvalue, 0, &unescaped);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to Failed to unescape MUD URL, ignoring: %s", rvalue);
+                return 0;
+        }
 
-        r = hashmap_put(m->dhcp6_prefixes, a, link);
-        if (r < 0)
-                return r;
+        if (!http_url_is_valid(unescaped) || strlen(unescaped) > UINT8_MAX) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "Failed to parse MUD URL '%s', ignoring: %m", rvalue);
+                return 0;
+        }
 
-        TAKE_PTR(a);
-        link_ref(link);
-        return 0;
+        return free_and_replace(network->dhcp6_mudurl, unescaped);
 }
 
-static int dhcp6_prefix_remove_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
+DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp6_client_start_mode, dhcp6_client_start_mode, DHCP6ClientStartMode,
+                         "Failed to parse WithoutRA= setting");
+
+static const char* const dhcp6_client_start_mode_table[_DHCP6_CLIENT_START_MODE_MAX] = {
+        [DHCP6_CLIENT_START_MODE_NO]                  = "no",
+        [DHCP6_CLIENT_START_MODE_INFORMATION_REQUEST] = "information-request",
+        [DHCP6_CLIENT_START_MODE_SOLICIT]             = "solicit",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(dhcp6_client_start_mode, DHCP6ClientStartMode);
+
+int config_parse_dhcp6_pd_subnet_id(
+                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) {
+
+        int64_t *p = data;
+        uint64_t t;
         int r;
 
-        assert(link);
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
 
-        if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
-                return 1;
+        if (isempty(rvalue) || streq(rvalue, "auto")) {
+                *p = -1;
+                return 0;
+        }
 
-        r = sd_netlink_message_get_errno(m);
+        r = safe_atoux64(rvalue, &t);
         if (r < 0) {
-                log_link_message_warning_errno(link, m, r, "Received error on DHCPv6 Prefix Delegation route removal");
-                link_enter_failed(link);
-                return 1;
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse %s=, ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
+        }
+        if (t > INT64_MAX) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "Invalid subnet id '%s', ignoring assignment.",
+                           rvalue);
+                return 0;
         }
 
-        return 1;
+        *p = (int64_t) t;
+
+        return 0;
 }
 
-int dhcp6_prefix_remove(Manager *m, struct in6_addr *addr) {
-        _cleanup_free_ struct in6_addr *a = NULL;
-        _cleanup_(link_unrefp) Link *l = NULL;
-        _cleanup_(route_freep) Route *route = NULL;
-        _cleanup_free_ char *buf = NULL;
+int config_parse_dhcp6_pd_token(
+                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 = data, tmp;
         int r;
 
-        assert_return(m, -EINVAL);
-        assert_return(addr, -EINVAL);
-
-        l = hashmap_remove2(m->dhcp6_prefixes, addr, (void **) &a);
-        if (!l)
-                return -EINVAL;
-
-        (void) sd_radv_remove_prefix(l->radv, addr, 64);
-
-        r = route_new(&route);
-        if (r < 0)
-                return r;
-
-        route->family = AF_INET6;
-        route->dst.in6 = *addr;
-        route->dst_prefixlen = 64;
-
-        r = route_remove(route, l, dhcp6_prefix_remove_handler);
-        if (r < 0)
-                return r;
-
-        (void) in_addr_to_string(AF_INET6, (union in_addr_union *) addr, &buf);
-        log_link_debug(l, "Removing prefix route %s/64", strnull(buf));
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
 
-        return 0;
-}
+        if (isempty(rvalue)) {
+                *addr = IN_ADDR_NULL;
+                return 0;
+        }
 
-static int dhcp6_prefix_remove_all(Manager *m, Link *link) {
-        struct in6_addr *addr;
-        Iterator i;
-        Link *l;
+        r = in_addr_from_string(AF_INET6, rvalue, &tmp);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse DHCPv6 Prefix Delegation token, ignoring: %s", rvalue);
+                return 0;
+        }
 
-        assert_return(m, -EINVAL);
-        assert_return(link, -EINVAL);
+        if (in_addr_is_null(AF_INET6, &tmp)) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "DHCPv6 Prefix Delegation token cannot be the ANY address, ignoring: %s", rvalue);
+                return 0;
+        }
 
-        HASHMAP_FOREACH_KEY(l, addr, m->dhcp6_prefixes, i)
-                if (l == link)
-                        (void) dhcp6_prefix_remove(m, addr);
+        *addr = tmp;
 
         return 0;
 }
index 26d810f40c35a6a0f5fcad6ea906d794bfdf56b0..214456096da7f9440a3ade1d2ab64bd37ec34640 100644 (file)
@@ -4,12 +4,39 @@
 #include "sd-dhcp6-client.h"
 
 #include "conf-parser.h"
+#include "macro.h"
+
+typedef enum DHCP6ClientStartMode {
+        DHCP6_CLIENT_START_MODE_NO,
+        DHCP6_CLIENT_START_MODE_INFORMATION_REQUEST,
+        DHCP6_CLIENT_START_MODE_SOLICIT,
+        _DHCP6_CLIENT_START_MODE_MAX,
+        _DHCP6_CLIENT_START_MODE_INVALID = -1,
+} DHCP6ClientStartMode;
 
 typedef struct Link Link;
 typedef struct Manager Manager;
 
-int dhcp6_request_prefix_delegation(Link *link);
+typedef struct DHCP6DelegatedPrefix {
+        struct in6_addr prefix;     /* Prefix assigned to the link */
+        struct in6_addr pd_prefix;  /* PD prefix provided by DHCP6 lease */
+        Link *link;
+} DHCP6DelegatedPrefix;
+
+DHCP6DelegatedPrefix *dhcp6_pd_free(DHCP6DelegatedPrefix *p);
+DEFINE_TRIVIAL_CLEANUP_FUNC(DHCP6DelegatedPrefix*, dhcp6_pd_free);
+
+bool link_dhcp6_pd_is_enabled(Link *link);
+int dhcp6_pd_remove(Link *link);
 int dhcp6_configure(Link *link);
 int dhcp6_request_address(Link *link, int ir);
-int dhcp6_lease_pd_prefix_lost(sd_dhcp6_client *client, Link* link);
-int dhcp6_prefix_remove(Manager *m, struct in6_addr *addr);
+int dhcp6_request_prefix_delegation(Link *link);
+
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_pd_hint);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_mud_url);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_client_start_mode);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_pd_subnet_id);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp6_pd_token);
+
+const char* dhcp6_client_start_mode_to_string(DHCP6ClientStartMode i) _const_;
+DHCP6ClientStartMode dhcp6_client_start_mode_from_string(const char *s) _pure_;
index df18682245e48999ceb094591122e9a4f208f770..833d13cf00a5ccf56de986d0b0db2aa77d12459d 100644 (file)
@@ -218,7 +218,7 @@ int config_parse_fdb_hwaddr(
 
         r = ether_addr_from_string(rvalue, &fdb_entry->mac_addr);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "Not a valid MAC address, ignoring assignment: %s", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Not a valid MAC address, ignoring assignment: %s", rvalue);
                 return 0;
         }
 
@@ -293,7 +293,7 @@ int config_parse_fdb_destination(
 
         r = in_addr_from_string_auto(rvalue, &fdb_entry->family, &fdb_entry->destination_addr);
         if (r < 0)
-                return log_syntax(unit, LOG_ERR, filename, line, r,
+                return log_syntax(unit, LOG_WARNING, filename, line, r,
                                   "FDB destination IP address is invalid, ignoring assignment: %s",
                                   rvalue);
 
@@ -331,14 +331,14 @@ int config_parse_fdb_vxlan_vni(
 
         r = safe_atou32(rvalue, &vni);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to parse VXLAN Network Identifier (VNI), ignoring assignment: %s",
                            rvalue);
                 return 0;
         }
 
         if (vni > VXLAN_VID_MAX) {
-                log_syntax(unit, LOG_ERR, filename, line, 0,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "FDB invalid VXLAN Network Identifier (VNI), ignoring assignment: %s",
                            rvalue);
                 return 0;
@@ -379,7 +379,7 @@ int config_parse_fdb_ntf_flags(
 
         f = fdb_ntf_flags_from_string(rvalue);
         if (f < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, 0,
+                log_syntax(unit, LOG_WARNING, filename, line, SYNTHETIC_ERRNO(EINVAL),
                            "FDB failed to parse AssociatedWith=, ignoring assignment: %s",
                            rvalue);
                 return 0;
index d74d37559937573c7ffdfa17923e46299e4e1ea8..aaabb3d1b32878956205383a627317ec8a6c1321 100644 (file)
@@ -20,5 +20,6 @@ struct ConfigPerfItem;
 %%
 Network.SpeedMeter,            config_parse_bool,                      0,          offsetof(Manager, use_speed_meter)
 Network.SpeedMeterIntervalSec, config_parse_sec,                       0,          offsetof(Manager, speed_meter_interval_usec)
+Network.ManageForeignRoutes,   config_parse_bool,                      0,          offsetof(Manager, manage_foreign_routes)
 DHCP.DUIDType,                 config_parse_duid_type,                 0,          offsetof(Manager, duid)
 DHCP.DUIDRawData,              config_parse_duid_rawdata,              0,          offsetof(Manager, duid)
index 5750ea091efe6838713c0f866b6a9a448b0e3972..e844799b5735393545d0aa2afde23a823792605d 100644 (file)
@@ -17,7 +17,7 @@ static int ipv4ll_address_lost(Link *link) {
 
         assert(link);
 
-        link->ipv4ll_address = false;
+        link->ipv4ll_address_configured = false;
 
         r = sd_ipv4ll_get_address(link->ipv4ll, &addr);
         if (r < 0)
@@ -47,7 +47,7 @@ static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link
         int r;
 
         assert(link);
-        assert(!link->ipv4ll_address);
+        assert(!link->ipv4ll_address_configured);
 
         r = sd_netlink_message_get_errno(m);
         if (r < 0 && r != -EEXIST) {
@@ -57,7 +57,7 @@ static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link
         } else if (r >= 0)
                 (void) manager_rtnl_process_address(rtnl, m, link->manager);
 
-        link->ipv4ll_address = true;
+        link->ipv4ll_address_configured = true;
         link_check_ready(link);
 
         return 1;
@@ -71,7 +71,7 @@ static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) {
         assert(ll);
         assert(link);
 
-        link->ipv4ll_address = false;
+        link->ipv4ll_address_configured = false;
 
         r = sd_ipv4ll_get_address(ll, &address);
         if (r == -ENOENT)
@@ -92,7 +92,7 @@ static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) {
         ll_addr->broadcast.s_addr = ll_addr->in_addr.in.s_addr | htobe32(0xfffffffflu >> ll_addr->prefixlen);
         ll_addr->scope = RT_SCOPE_LINK;
 
-        r = address_configure(ll_addr, link, ipv4ll_address_handler, false);
+        r = address_configure(ll_addr, link, ipv4ll_address_handler, false, NULL);
         if (r < 0)
                 return r;
 
@@ -154,6 +154,10 @@ int ipv4ll_configure(Link *link) {
                 r = sd_ipv4ll_new(&link->ipv4ll);
                 if (r < 0)
                         return r;
+
+                r = sd_ipv4ll_attach_event(link->ipv4ll, NULL, 0);
+                if (r < 0)
+                        return r;
         }
 
         if (link->sd_device &&
@@ -163,10 +167,6 @@ int ipv4ll_configure(Link *link) {
                         return r;
         }
 
-        r = sd_ipv4ll_attach_event(link->ipv4ll, NULL, 0);
-        if (r < 0)
-                return r;
-
         r = sd_ipv4ll_set_mac(link->ipv4ll, &link->mac);
         if (r < 0)
                 return r;
@@ -208,7 +208,7 @@ int config_parse_ipv4ll(
 
         r = parse_boolean(rvalue);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to parse %s=%s, ignoring assignment. "
                            "Note that the setting %s= is deprecated, please use LinkLocalAddressing= instead.",
                            lvalue, rvalue, lvalue);
index b207866c758a7d14d2a57e214f747c5bcfe8f764..7051ba9dac4fc05f81cdc4b2ff37208b9d24ec32 100644 (file)
@@ -113,17 +113,18 @@ int config_parse_ipv6_proxy_ndp_address(
 
         r = ipv6_proxy_ndp_address_new_static(network, &ipv6_proxy_ndp_address);
         if (r < 0)
-                return r;
+                return log_oom();
 
         r = in_addr_from_string(AF_INET6, rvalue, &buffer);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse IPv6 proxy NDP address, ignoring: %s",
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse IPv6 proxy NDP address, ignoring: %s",
                            rvalue);
                 return 0;
         }
 
         if (in_addr_is_null(AF_INET6, &buffer)) {
-                log_syntax(unit, LOG_ERR, filename, line, 0,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "IPv6 proxy NDP address cannot be the ANY address, ignoring: %s", rvalue);
                 return 0;
         }
index efb72b60e79aa0994d173aef792fa476f091291f..f623a9b4a1a22e1910891843da8ece621c79e6a8 100644 (file)
@@ -6,14 +6,16 @@
 
 #include "alloc-util.h"
 #include "bus-common-errors.h"
+#include "bus-get-properties.h"
+#include "bus-message-util.h"
 #include "bus-polkit.h"
-#include "bus-util.h"
 #include "dns-domain.h"
 #include "networkd-link-bus.h"
 #include "networkd-link.h"
 #include "networkd-manager.h"
 #include "parse-util.h"
 #include "resolve-util.h"
+#include "socket-netlink.h"
 #include "strv.h"
 #include "user-util.h"
 
@@ -108,15 +110,18 @@ int bus_link_method_set_ntp_servers(sd_bus_message *message, void *userdata, sd_
 
         strv_free_and_replace(l->ntp, ntp);
 
-        (void) link_dirty(l);
+        link_dirty(l);
+        r = link_save_and_clean(l);
+        if (r < 0)
+                return r;
 
         return sd_bus_reply_method_return(message, NULL);
 }
 
-int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error) {
-        _cleanup_free_ struct in_addr_data *dns = NULL;
-        size_t allocated = 0, n = 0;
+static int bus_link_method_set_dns_servers_internal(sd_bus_message *message, void *userdata, sd_bus_error *error, bool extended) {
+        struct in_addr_full **dns;
         Link *l = userdata;
+        size_t n;
         int r;
 
         assert(message);
@@ -126,52 +131,7 @@ int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_
         if (r < 0)
                 return r;
 
-        r = sd_bus_message_enter_container(message, 'a', "(iay)");
-        if (r < 0)
-                return r;
-
-        for (;;) {
-                int family;
-                size_t sz;
-                const void *d;
-
-                assert_cc(sizeof(int) == sizeof(int32_t));
-
-                r = sd_bus_message_enter_container(message, 'r', "iay");
-                if (r < 0)
-                        return r;
-                if (r == 0)
-                        break;
-
-                r = sd_bus_message_read(message, "i", &family);
-                if (r < 0)
-                        return r;
-
-                if (!IN_SET(family, AF_INET, AF_INET6))
-                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
-
-                r = sd_bus_message_read_array(message, 'y', &d, &sz);
-                if (r < 0)
-                        return r;
-                if (sz != FAMILY_ADDRESS_SIZE(family))
-                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size");
-
-                if (!dns_server_address_valid(family, d))
-                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNS server address");
-
-                r = sd_bus_message_exit_container(message);
-                if (r < 0)
-                        return r;
-
-                if (!GREEDY_REALLOC(dns, allocated, n+1))
-                        return -ENOMEM;
-
-                dns[n].family = family;
-                memcpy(&dns[n].address, d, sz);
-                n++;
-        }
-
-        r = sd_bus_message_exit_container(message);
+        r = bus_message_read_dns_servers(message, error, extended, &dns, &n);
         if (r < 0)
                 return r;
 
@@ -180,16 +140,40 @@ int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_
                                     NULL, true, UID_INVALID,
                                     &l->manager->polkit_registry, error);
         if (r < 0)
-                return r;
-        if (r == 0)
-                return 1; /* Polkit will call us back */
+                goto finalize;
+        if (r == 0) {
+                r = 1; /* Polkit will call us back */
+                goto finalize;
+        }
+
+        if (l->n_dns != (unsigned) -1)
+                for (unsigned i = 0; i < l->n_dns; i++)
+                        in_addr_full_free(l->dns[i]);
 
         free_and_replace(l->dns, dns);
         l->n_dns = n;
 
-        (void) link_dirty(l);
+        link_dirty(l);
+        r = link_save_and_clean(l);
+        if (r < 0)
+                return r;
 
         return sd_bus_reply_method_return(message, NULL);
+
+finalize:
+        for (size_t i = 0; i < n; i++)
+                in_addr_full_free(dns[i]);
+        free(dns);
+
+        return r;
+}
+
+int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        return bus_link_method_set_dns_servers_internal(message, userdata, error, false);
+}
+
+int bus_link_method_set_dns_servers_ex(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        return bus_link_method_set_dns_servers_internal(message, userdata, error, true);
 }
 
 int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) {
@@ -262,7 +246,10 @@ int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_
         l->search_domains = TAKE_PTR(search_domains);
         l->route_domains = TAKE_PTR(route_domains);
 
-        (void) link_dirty(l);
+        link_dirty(l);
+        r = link_save_and_clean(l);
+        if (r < 0)
+                return r;
 
         return sd_bus_reply_method_return(message, NULL);
 }
@@ -293,7 +280,11 @@ int bus_link_method_set_default_route(sd_bus_message *message, void *userdata, s
 
         if (l->dns_default_route != b) {
                 l->dns_default_route = b;
-                (void) link_dirty(l);
+
+                link_dirty(l);
+                r = link_save_and_clean(l);
+                if (r < 0)
+                        return r;
         }
 
         return sd_bus_reply_method_return(message, NULL);
@@ -335,7 +326,11 @@ int bus_link_method_set_llmnr(sd_bus_message *message, void *userdata, sd_bus_er
 
         if (l->llmnr != mode) {
                 l->llmnr = mode;
-                (void) link_dirty(l);
+
+                link_dirty(l);
+                r = link_save_and_clean(l);
+                if (r < 0)
+                        return r;
         }
 
         return sd_bus_reply_method_return(message, NULL);
@@ -377,7 +372,11 @@ int bus_link_method_set_mdns(sd_bus_message *message, void *userdata, sd_bus_err
 
         if (l->mdns != mode) {
                 l->mdns = mode;
-                (void) link_dirty(l);
+
+                link_dirty(l);
+                r = link_save_and_clean(l);
+                if (r < 0)
+                        return r;
         }
 
         return sd_bus_reply_method_return(message, NULL);
@@ -419,7 +418,11 @@ int bus_link_method_set_dns_over_tls(sd_bus_message *message, void *userdata, sd
 
         if (l->dns_over_tls_mode != mode) {
                 l->dns_over_tls_mode = mode;
-                (void) link_dirty(l);
+
+                link_dirty(l);
+                r = link_save_and_clean(l);
+                if (r < 0)
+                        return r;
         }
 
         return sd_bus_reply_method_return(message, NULL);
@@ -461,7 +464,11 @@ int bus_link_method_set_dnssec(sd_bus_message *message, void *userdata, sd_bus_e
 
         if (l->dnssec_mode != mode) {
                 l->dnssec_mode = mode;
-                (void) link_dirty(l);
+
+                link_dirty(l);
+                r = link_save_and_clean(l);
+                if (r < 0)
+                        return r;
         }
 
         return sd_bus_reply_method_return(message, NULL);
@@ -498,7 +505,7 @@ int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, v
                 return -ENOMEM;
 
         STRV_FOREACH(i, ntas) {
-                r = set_put_strdup(ns, *i);
+                r = set_put_strdup(&ns, *i);
                 if (r < 0)
                         return r;
         }
@@ -515,7 +522,10 @@ int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, v
         set_free_free(l->dnssec_negative_trust_anchors);
         l->dnssec_negative_trust_anchors = TAKE_PTR(ns);
 
-        (void) link_dirty(l);
+        link_dirty(l);
+        r = link_save_and_clean(l);
+        if (r < 0)
+                return r;
 
         return sd_bus_reply_method_return(message, NULL);
 }
@@ -541,7 +551,11 @@ int bus_link_method_revert_ntp(sd_bus_message *message, void *userdata, sd_bus_e
                 return 1; /* Polkit will call us back */
 
         link_ntp_settings_clear(l);
-        (void) link_dirty(l);
+
+        link_dirty(l);
+        r = link_save_and_clean(l);
+        if (r < 0)
+                return r;
 
         return sd_bus_reply_method_return(message, NULL);
 }
@@ -567,7 +581,40 @@ int bus_link_method_revert_dns(sd_bus_message *message, void *userdata, sd_bus_e
                 return 1; /* Polkit will call us back */
 
         link_dns_settings_clear(l);
-        (void) link_dirty(l);
+
+        link_dirty(l);
+        r = link_save_and_clean(l);
+        if (r < 0)
+                return r;
+
+        return sd_bus_reply_method_return(message, NULL);
+}
+
+int bus_link_method_force_renew(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        Link *l = userdata;
+        int r;
+
+        assert(l);
+
+        if (!l->network)
+                return sd_bus_error_setf(error, BUS_ERROR_UNMANAGED_INTERFACE,
+                                         "Interface %s is not managed by systemd-networkd",
+                                         l->ifname);
+
+        r = bus_verify_polkit_async(message, CAP_NET_ADMIN,
+                                    "org.freedesktop.network1.forcerenew",
+                                    NULL, true, UID_INVALID,
+                                    &l->manager->polkit_registry, error);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return 1; /* Polkit will call us back */
+
+        if (l->dhcp_server) {
+                r = sd_dhcp_server_forcerenew(l->dhcp_server);
+                if (r < 0)
+                        return r;
+        }
 
         return sd_bus_reply_method_return(message, NULL);
 }
@@ -622,10 +669,9 @@ int bus_link_method_reconfigure(sd_bus_message *message, void *userdata, sd_bus_
                 return r;
 
         link_set_state(l, LINK_STATE_INITIALIZED);
-        r = link_save(l);
+        r = link_save_and_clean(l);
         if (r < 0)
                 return r;
-        link_clean(l);
 
         return sd_bus_reply_method_return(message, NULL);
 }
@@ -641,6 +687,7 @@ const sd_bus_vtable link_vtable[] = {
 
         SD_BUS_METHOD("SetNTP", "as", NULL, bus_link_method_set_ntp_servers, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("SetDNS", "a(iay)", NULL, bus_link_method_set_dns_servers, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("SetDNSEx", "a(iayqs)", NULL, bus_link_method_set_dns_servers_ex, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("SetDomains", "a(sb)", NULL, bus_link_method_set_domains, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("SetDefaultRoute", "b", NULL, bus_link_method_set_default_route, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("SetLLMNR", "s", NULL, bus_link_method_set_llmnr, SD_BUS_VTABLE_UNPRIVILEGED),
@@ -651,6 +698,7 @@ const sd_bus_vtable link_vtable[] = {
         SD_BUS_METHOD("RevertNTP", NULL, NULL, bus_link_method_revert_ntp, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("RevertDNS", NULL, NULL, bus_link_method_revert_dns, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("Renew", NULL, NULL, bus_link_method_renew, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("ForceRenew", NULL, NULL, bus_link_method_force_renew, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("Reconfigure", NULL, NULL, bus_link_method_reconfigure, SD_BUS_VTABLE_UNPRIVILEGED),
 
         SD_BUS_VTABLE_END
@@ -730,6 +778,9 @@ int link_object_find(sd_bus *bus, const char *path, const char *interface, void
         if (r < 0)
                 return 0;
 
+        if (streq(interface, "org.freedesktop.network1.DHCPServer") && !link->dhcp_server)
+                return 0;
+
         *found = link;
 
         return 1;
index 09e4ad68a11606cec3262aeaf66281b6027c596f..94474f22fffbf1bd9a9cdac3bd1b942a003507b3 100644 (file)
@@ -21,6 +21,7 @@ int property_get_address_state(sd_bus *bus, const char *path, const char *interf
 
 int bus_link_method_set_ntp_servers(sd_bus_message *message, void *userdata, sd_bus_error *error);
 int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_link_method_set_dns_servers_ex(sd_bus_message *message, void *userdata, sd_bus_error *error);
 int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_error *error);
 int bus_link_method_set_default_route(sd_bus_message *message, void *userdata, sd_bus_error *error);
 int bus_link_method_set_llmnr(sd_bus_message *message, void *userdata, sd_bus_error *error);
@@ -31,4 +32,5 @@ int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, v
 int bus_link_method_revert_ntp(sd_bus_message *message, void *userdata, sd_bus_error *error);
 int bus_link_method_revert_dns(sd_bus_message *message, void *userdata, sd_bus_error *error);
 int bus_link_method_renew(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_link_method_force_renew(sd_bus_message *message, void *userdata, sd_bus_error *error);
 int bus_link_method_reconfigure(sd_bus_message *message, void *userdata, sd_bus_error *error);
index bc7bf2595c6871e9aacfb439e9fb5968d99e9a68..0057d184f7658c88d61dfffd699dffb7d0e7aca2 100644 (file)
@@ -3,6 +3,7 @@
 #include <netinet/in.h>
 #include <linux/if.h>
 #include <linux/if_arp.h>
+#include <linux/if_link.h>
 #include <unistd.h>
 
 #include "alloc-util.h"
 #include "networkd-manager.h"
 #include "networkd-ndisc.h"
 #include "networkd-neighbor.h"
+#include "networkd-sriov.h"
 #include "networkd-radv.h"
 #include "networkd-routing-policy-rule.h"
 #include "networkd-wifi.h"
-#include "qdisc.h"
 #include "set.h"
 #include "socket-util.h"
 #include "stat-util.h"
@@ -42,6 +43,7 @@
 #include "string-table.h"
 #include "strv.h"
 #include "sysctl-util.h"
+#include "tc.h"
 #include "tmpfile-util.h"
 #include "udev-util.h"
 #include "util.h"
@@ -346,6 +348,9 @@ static void link_update_master_operstate(Link *link, NetDev *netdev) {
         if (!netdev)
                 return;
 
+        if (netdev->ifindex <= 0)
+                return;
+
         if (link_get(link->manager, netdev->ifindex, &master) < 0)
                 return;
 
@@ -661,6 +666,9 @@ void link_ntp_settings_clear(Link *link) {
 }
 
 void link_dns_settings_clear(Link *link) {
+        if (link->n_dns != (unsigned) -1)
+                for (unsigned i = 0; i < link->n_dns; i++)
+                        in_addr_full_free(link->dns[i]);
         link->dns = mfree(link->dns);
         link->n_dns = (unsigned) -1;
 
@@ -683,7 +691,6 @@ static void link_free_engines(Link *link) {
         link->dhcp_server = sd_dhcp_server_unref(link->dhcp_server);
         link->dhcp_client = sd_dhcp_client_unref(link->dhcp_client);
         link->dhcp_lease = sd_dhcp_lease_unref(link->dhcp_lease);
-        link->dhcp_routes = set_free(link->dhcp_routes);
 
         link->lldp = sd_lldp_unref(link->lldp);
 
@@ -691,6 +698,7 @@ static void link_free_engines(Link *link) {
 
         link->ipv4ll = sd_ipv4ll_unref(link->ipv4ll);
         link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client);
+        link->dhcp6_lease = sd_dhcp6_lease_unref(link->dhcp6_lease);
         link->ndisc = sd_ndisc_unref(link->ndisc);
         link->radv = sd_radv_unref(link->radv);
 }
@@ -703,17 +711,32 @@ static Link *link_free(Link *link) {
         link_ntp_settings_clear(link);
         link_dns_settings_clear(link);
 
-        link->routes = set_free_with_destructor(link->routes, route_free);
-        link->routes_foreign = set_free_with_destructor(link->routes_foreign, route_free);
-
-        link->nexthops = set_free_with_destructor(link->nexthops, nexthop_free);
-        link->nexthops_foreign = set_free_with_destructor(link->nexthops_foreign, nexthop_free);
-
-        link->neighbors = set_free_with_destructor(link->neighbors, neighbor_free);
-        link->neighbors_foreign = set_free_with_destructor(link->neighbors_foreign, neighbor_free);
-
-        link->addresses = set_free_with_destructor(link->addresses, address_free);
-        link->addresses_foreign = set_free_with_destructor(link->addresses_foreign, address_free);
+        link->routes = set_free(link->routes);
+        link->routes_foreign = set_free(link->routes_foreign);
+        link->dhcp_routes = set_free(link->dhcp_routes);
+        link->dhcp_routes_old = set_free(link->dhcp_routes_old);
+        link->dhcp6_routes = set_free(link->dhcp6_routes);
+        link->dhcp6_routes_old = set_free(link->dhcp6_routes_old);
+        link->dhcp6_pd_routes = set_free(link->dhcp6_pd_routes);
+        link->dhcp6_pd_routes_old = set_free(link->dhcp6_pd_routes_old);
+        link->ndisc_routes = set_free(link->ndisc_routes);
+        link->ndisc_routes_old = set_free(link->ndisc_routes_old);
+
+        link->nexthops = set_free(link->nexthops);
+        link->nexthops_foreign = set_free(link->nexthops_foreign);
+
+        link->neighbors = set_free(link->neighbors);
+        link->neighbors_foreign = set_free(link->neighbors_foreign);
+
+        link->addresses = set_free(link->addresses);
+        link->addresses_foreign = set_free(link->addresses_foreign);
+        link->static_addresses = set_free(link->static_addresses);
+        link->dhcp6_addresses = set_free(link->dhcp6_addresses);
+        link->dhcp6_addresses_old = set_free(link->dhcp6_addresses_old);
+        link->dhcp6_pd_addresses = set_free(link->dhcp6_pd_addresses);
+        link->dhcp6_pd_addresses_old = set_free(link->dhcp6_pd_addresses_old);
+        link->ndisc_addresses = set_free(link->ndisc_addresses);
+        link->ndisc_addresses_old = set_free(link->ndisc_addresses_old);
 
         while ((address = link->pool_addresses)) {
                 LIST_REMOVE(addresses, link->pool_addresses, address);
@@ -752,7 +775,7 @@ int link_get(Manager *m, int ifindex, Link **ret) {
         Link *link;
 
         assert(m);
-        assert(ifindex);
+        assert(ifindex > 0);
         assert(ret);
 
         link = hashmap_get(m->links, INT_TO_PTR(ifindex));
@@ -795,8 +818,6 @@ int link_stop_clients(Link *link, bool may_keep_dhcp) {
         assert(link->manager);
         assert(link->manager->event);
 
-        dhcp4_release_old_lease(link);
-
         bool keep_dhcp = may_keep_dhcp &&
                          link->network &&
                          (link->manager->restarting ||
@@ -828,6 +849,12 @@ int link_stop_clients(Link *link, bool may_keep_dhcp) {
                         r = log_link_warning_errno(link, k, "Could not stop DHCPv6 client: %m");
         }
 
+        if (link_dhcp6_pd_is_enabled(link)) {
+                k = dhcp6_pd_remove(link);
+                if (k < 0)
+                        r = log_link_warning_errno(link, k, "Could not remove DHCPv6 PD addresses and routes: %m");
+        }
+
         if (link->ndisc) {
                 k = sd_ndisc_stop(link->ndisc);
                 if (k < 0)
@@ -927,10 +954,9 @@ static int link_request_set_routing_policy_rule(Link *link) {
         }
 
         routing_policy_rule_purge(link->manager, link);
-        if (link->routing_policy_rule_messages == 0) {
+        if (link->routing_policy_rule_messages == 0)
                 link->routing_policy_rules_configured = true;
-                link_check_ready(link);
-        } else {
+        else {
                 log_link_debug(link, "Setting routing policy rules");
                 link_set_state(link, LINK_STATE_CONFIGURING);
         }
@@ -967,10 +993,12 @@ static int nexthop_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link)
         return 1;
 }
 
-int link_request_set_nexthop(Link *link) {
+static int link_request_set_nexthop(Link *link) {
         NextHop *nh;
         int r;
 
+        link->static_nexthops_configured = false;
+
         LIST_FOREACH(nexthops, nh, link->network->static_nexthops) {
                 r = nexthop_configure(nh, link, nexthop_handler);
                 if (r < 0)
@@ -1013,7 +1041,7 @@ static int route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
         if (link->route_messages == 0) {
                 log_link_debug(link, "Routes set");
                 link->static_routes_configured = true;
-                link_check_ready(link);
+                link_request_set_nexthop(link);
         }
 
         return 1;
@@ -1030,12 +1058,12 @@ int link_request_set_routes(Link *link) {
 
         assert(link);
         assert(link->network);
-        assert(link->addresses_configured);
-        assert(link->address_messages == 0);
         assert(link->state != _LINK_STATE_INVALID);
 
         link->static_routes_configured = false;
-        link->static_routes_ready = false;
+
+        if (!link->addresses_ready)
+                return 0;
 
         if (!link_has_carrier(link) && !link->network->configure_without_carrier)
                 /* During configuring addresses, the link lost its carrier. As networkd is dropping
@@ -1055,7 +1083,7 @@ int link_request_set_routes(Link *link) {
                         if ((in_addr_is_null(rt->family, &rt->gw) && ordered_set_isempty(rt->multipath_routes)) != (phase == PHASE_NON_GATEWAY))
                                 continue;
 
-                        r = route_configure(rt, link, route_handler);
+                        r = route_configure(rt, link, route_handler, NULL);
                         if (r < 0)
                                 return log_link_warning_errno(link, r, "Could not set routes: %m");
                         if (r > 0)
@@ -1064,7 +1092,7 @@ int link_request_set_routes(Link *link) {
 
         if (link->route_messages == 0) {
                 link->static_routes_configured = true;
-                link_check_ready(link);
+                link_request_set_nexthop(link);
         } else {
                 log_link_debug(link, "Setting routes");
                 link_set_state(link, LINK_STATE_CONFIGURING);
@@ -1076,75 +1104,109 @@ int link_request_set_routes(Link *link) {
 void link_check_ready(Link *link) {
         Address *a;
         Iterator i;
-        int r;
 
         assert(link);
 
-        if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+        if (link->state == LINK_STATE_CONFIGURED)
                 return;
 
+        if (link->state != LINK_STATE_CONFIGURING) {
+                log_link_debug(link, "%s(): link is in %s state.", __func__, link_state_to_string(link->state));
+                return;
+        }
+
         if (!link->network)
                 return;
 
-        if (!link->addresses_configured)
+        if (!link->addresses_configured) {
+                log_link_debug(link, "%s(): static addresses are not configured.", __func__);
                 return;
+        }
 
-        if (!link->neighbors_configured)
+        if (!link->neighbors_configured) {
+                log_link_debug(link, "%s(): static neighbors are not configured.", __func__);
                 return;
+        }
 
         SET_FOREACH(a, link->addresses, i)
-                if (!address_is_ready(a))
+                if (!address_is_ready(a)) {
+                        _cleanup_free_ char *str = NULL;
+
+                        (void) in_addr_to_string(a->family, &a->in_addr, &str);
+                        log_link_debug(link, "%s(): an address %s/%d is not ready.", __func__, strnull(str), a->prefixlen);
                         return;
+                }
 
-        if (!link->addresses_ready) {
-                link->addresses_ready = true;
-                r = link_request_set_routes(link);
-                if (r < 0)
-                        link_enter_failed(link);
+        if (!link->static_routes_configured) {
+                log_link_debug(link, "%s(): static routes are not configured.", __func__);
                 return;
         }
 
-        if (!link->static_routes_configured)
-                return;
-
-        if (!link->static_routes_ready) {
-                link->static_routes_ready = true;
-                r = link_request_set_nexthop(link);
-                if (r < 0)
-                        link_enter_failed(link);
+        if (!link->static_nexthops_configured) {
+                log_link_debug(link, "%s(): static nexthops are not configured.", __func__);
                 return;
         }
 
-        if (!link->static_nexthops_configured)
+        if (!link->routing_policy_rules_configured) {
+                log_link_debug(link, "%s(): static routing policy rules are not configured.", __func__);
                 return;
+        }
 
-        if (!link->routing_policy_rules_configured)
+        if (!link->tc_configured) {
+                log_link_debug(link, "%s(): traffic controls are not configured.", __func__);
                 return;
+        }
 
-        if (!link->qdiscs_configured)
+        if (!link->sr_iov_configured) {
+                log_link_debug(link, "%s(): SR-IOV is not configured.", __func__);
                 return;
+        }
 
         if (link_has_carrier(link) || !link->network->configure_without_carrier) {
 
-                if (link_ipv4ll_enabled(link, ADDRESS_FAMILY_IPV4) && !link->ipv4ll_address)
+                if (link_ipv4ll_enabled(link, ADDRESS_FAMILY_IPV4) && !link->ipv4ll_address_configured) {
+                        log_link_debug(link, "%s(): IPv4LL is not configured.", __func__);
                         return;
+                }
 
                 if (link_ipv6ll_enabled(link) &&
-                    in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address))
+                    in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address)) {
+                        log_link_debug(link, "%s(): IPv6LL is not configured.", __func__);
                         return;
+                }
 
-                if ((link_dhcp4_enabled(link) || link_dhcp6_enabled(link) || link_ipv6_accept_ra_enabled(link)) &&
-                    !link->dhcp4_configured &&
-                    !link->dhcp6_configured &&
-                    !link->ndisc_configured &&
-                    !(link_ipv4ll_enabled(link, ADDRESS_FAMILY_FALLBACK_IPV4) && link->ipv4ll_address))
-                        /* When DHCP or RA is enabled, at least one protocol must provide an address, or
-                         * an IPv4ll fallback address must be configured. */
+                if ((link_dhcp4_enabled(link) || link_dhcp6_enabled(link)) &&
+                    !link->dhcp_address && set_isempty(link->dhcp6_addresses) && set_isempty(link->ndisc_addresses) &&
+                    !(link_ipv4ll_enabled(link, ADDRESS_FAMILY_FALLBACK_IPV4) && link->ipv4ll_address_configured)) {
+                        log_link_debug(link, "%s(): DHCP4 or DHCP6 is enabled but no dynamic address is assigned yet.", __func__);
                         return;
+                }
+
+                if (link_dhcp4_enabled(link) || link_dhcp6_enabled(link) || link_dhcp6_pd_is_enabled(link) || link_ipv6_accept_ra_enabled(link)) {
+                        if (!link->dhcp4_configured &&
+                            !(link->dhcp6_address_configured && link->dhcp6_route_configured) &&
+                            !(link->dhcp6_pd_address_configured && link->dhcp6_pd_route_configured) &&
+                            !(link->ndisc_addresses_configured && link->ndisc_routes_configured) &&
+                            !(link_ipv4ll_enabled(link, ADDRESS_FAMILY_FALLBACK_IPV4) && link->ipv4ll_address_configured)) {
+                                /* When DHCP or RA is enabled, at least one protocol must provide an address, or
+                                 * an IPv4ll fallback address must be configured. */
+                                log_link_debug(link, "%s(): dynamic addresses or routes are not configured.", __func__);
+                                return;
+                        }
+
+                        log_link_debug(link, "%s(): dhcp4:%s dhcp6_addresses:%s dhcp_routes:%s dhcp_pd_addresses:%s dhcp_pd_routes:%s ndisc_addresses:%s ndisc_routes:%s",
+                                       __func__,
+                                       yes_no(link->dhcp4_configured),
+                                       yes_no(link->dhcp6_address_configured),
+                                       yes_no(link->dhcp6_route_configured),
+                                       yes_no(link->dhcp6_pd_address_configured),
+                                       yes_no(link->dhcp6_pd_route_configured),
+                                       yes_no(link->ndisc_addresses_configured),
+                                       yes_no(link->ndisc_routes_configured));
+                }
         }
 
-        if (link->state != LINK_STATE_CONFIGURED)
-                link_enter_configured(link);
+        link_enter_configured(link);
 
         return;
 }
@@ -1176,6 +1238,50 @@ static int link_request_set_neighbors(Link *link) {
         return 0;
 }
 
+static int link_set_bridge_fdb(Link *link) {
+        FdbEntry *fdb_entry;
+        int r;
+
+        LIST_FOREACH(static_fdb_entries, fdb_entry, link->network->static_fdb_entries) {
+                r = fdb_entry_configure(link, fdb_entry);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Failed to add MAC entry to static MAC table: %m");
+        }
+
+        return 0;
+}
+
+static int static_address_ready_callback(Address *address) {
+        Address *a;
+        Iterator i;
+        Link *link;
+
+        assert(address);
+        assert(address->link);
+
+        link = address->link;
+
+        if (!link->addresses_configured)
+                return 0;
+
+        SET_FOREACH(a, link->static_addresses, i)
+                if (!address_is_ready(a)) {
+                        _cleanup_free_ char *str = NULL;
+
+                        (void) in_addr_to_string(a->family, &a->in_addr, &str);
+                        log_link_debug(link, "an address %s/%u is not ready", strnull(str), a->prefixlen);
+                        return 0;
+                }
+
+        /* This should not be called again */
+        SET_FOREACH(a, link->static_addresses, i)
+                a->callback = NULL;
+
+        link->addresses_ready = true;
+
+        return link_request_set_routes(link);
+}
+
 static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
         int r;
 
@@ -1201,23 +1307,50 @@ static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link)
                 (void) manager_rtnl_process_address(rtnl, m, link->manager);
 
         if (link->address_messages == 0) {
+                Address *a;
+
                 log_link_debug(link, "Addresses set");
                 link->addresses_configured = true;
-                link_check_ready(link);
+
+                /* When all static addresses are already ready, then static_address_ready_callback()
+                 * will not be called automatically. So, call it here. */
+                a = set_first(link->static_addresses);
+                if (!a) {
+                        log_link_warning(link, "No static address is stored.");
+                        link_enter_failed(link);
+                        return 1;
+                }
+                if (!a->callback) {
+                        log_link_warning(link, "Address ready callback is not set.");
+                        link_enter_failed(link);
+                        return 1;
+                }
+                r = a->callback(a);
+                if (r < 0)
+                        link_enter_failed(link);
         }
 
         return 1;
 }
 
-static int link_set_bridge_fdb(Link *link) {
-        FdbEntry *fdb_entry;
+static int static_address_configure(Address *address, Link *link, bool update) {
+        Address *ret;
         int r;
 
-        LIST_FOREACH(static_fdb_entries, fdb_entry, link->network->static_fdb_entries) {
-                r = fdb_entry_configure(link, fdb_entry);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Failed to add MAC entry to static MAC table: %m");
-        }
+        assert(address);
+        assert(link);
+
+        r = address_configure(address, link, address_handler, update, &ret);
+        if (r < 0)
+                return log_link_warning_errno(link, r, "Could not configure static address: %m");
+
+        link->address_messages++;
+
+        r = set_ensure_put(&link->static_addresses, &address_hash_ops, ret);
+        if (r < 0)
+                return log_link_warning_errno(link, r, "Failed to store static address: %m");
+
+        ret->callback = static_address_ready_callback;
 
         return 0;
 }
@@ -1225,6 +1358,7 @@ static int link_set_bridge_fdb(Link *link) {
 static int link_request_set_addresses(Link *link) {
         AddressLabel *label;
         Address *ad;
+        Prefix *p;
         int r;
 
         assert(link);
@@ -1236,7 +1370,6 @@ static int link_request_set_addresses(Link *link) {
         link->addresses_ready = false;
         link->neighbors_configured = false;
         link->static_routes_configured = false;
-        link->static_routes_ready = false;
         link->static_nexthops_configured = false;
         link->routing_policy_rules_configured = false;
 
@@ -1251,15 +1384,41 @@ static int link_request_set_addresses(Link *link) {
         LIST_FOREACH(addresses, ad, link->network->static_addresses) {
                 bool update;
 
-                update = address_get(link, ad->family, &ad->in_addr, ad->prefixlen, NULL) > 0;
+                if (ad->family == AF_INET6 && !in_addr_is_null(ad->family, &ad->in_addr_peer))
+                        update = address_get(link, ad->family, &ad->in_addr_peer, ad->prefixlen, NULL) > 0;
+                else
+                        update = address_get(link, ad->family, &ad->in_addr, ad->prefixlen, NULL) > 0;
 
-                r = address_configure(ad, link, address_handler, update);
+                r = static_address_configure(ad, link, update);
                 if (r < 0)
-                        return log_link_warning_errno(link, r, "Could not set addresses: %m");
-                if (r > 0)
-                        link->address_messages++;
+                        return r;
         }
 
+        if (link->network->router_prefix_delegation & RADV_PREFIX_DELEGATION_STATIC)
+                LIST_FOREACH(prefixes, p, link->network->static_prefixes) {
+                        _cleanup_(address_freep) Address *address = NULL;
+
+                        if (!p->assign)
+                                continue;
+
+                        r = address_new(&address);
+                        if (r < 0)
+                                return log_oom();
+
+                        r = sd_radv_prefix_get_prefix(p->radv_prefix, &address->in_addr.in6, &address->prefixlen);
+                        if (r < 0)
+                                return log_link_warning_errno(link, r, "Could not get RA prefix: %m");
+
+                        r = generate_ipv6_eui_64_address(link, &address->in_addr.in6);
+                        if (r < 0)
+                                return log_link_warning_errno(link, r, "Could not generate EUI64 address: %m");
+
+                        address->family = AF_INET6;
+                        r = static_address_configure(address, link, true);
+                        if (r < 0)
+                                return r;
+                }
+
         LIST_FOREACH(labels, label, link->network->address_labels) {
                 r = address_label_configure(label, link, NULL, false);
                 if (r < 0)
@@ -1268,8 +1427,7 @@ static int link_request_set_addresses(Link *link) {
                 link->address_label_messages++;
         }
 
-        /* now that we can figure out a default address for the dhcp server,
-           start it */
+        /* now that we can figure out a default address for the dhcp server, start it */
         if (link_dhcp4_server_enabled(link) && (link->flags & IFF_UP)) {
                 r = dhcp4_server_configure(link);
                 if (r < 0)
@@ -1279,7 +1437,10 @@ static int link_request_set_addresses(Link *link) {
 
         if (link->address_messages == 0) {
                 link->addresses_configured = true;
-                link_check_ready(link);
+                link->addresses_ready = true;
+                r = link_request_set_routes(link);
+                if (r < 0)
+                        return r;
         } else {
                 log_link_debug(link, "Setting addresses");
                 link_set_state(link, LINK_STATE_CONFIGURING);
@@ -1526,7 +1687,22 @@ static int link_acquire_ipv6_conf(Link *link) {
                         return log_link_warning_errno(link, r, "Could not start IPv6 Router Advertisement: %m");
         }
 
-        (void) dhcp6_request_prefix_delegation(link);
+        if (link_dhcp6_enabled(link) && IN_SET(link->network->dhcp6_without_ra,
+                                               DHCP6_CLIENT_START_MODE_INFORMATION_REQUEST,
+                                               DHCP6_CLIENT_START_MODE_SOLICIT)) {
+                assert(link->dhcp6_client);
+                assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*)&link->ipv6ll_address) > 0);
+
+                r = dhcp6_request_address(link, link->network->dhcp6_without_ra == DHCP6_CLIENT_START_MODE_INFORMATION_REQUEST);
+                if (r < 0 && r != -EBUSY)
+                        return log_link_warning_errno(link, r, "Could not acquire DHCPv6 lease: %m");
+                else
+                        log_link_debug(link, "Acquiring DHCPv6 lease");
+        }
+
+        r = dhcp6_request_prefix_delegation(link);
+        if (r < 0)
+                return log_link_warning_errno(link, r, "Failed to request DHCPv6 prefix delegation: %m");
 
         return 0;
 }
@@ -1654,12 +1830,18 @@ static int link_configure_addrgen_mode(Link *link) {
 
         if (!link_ipv6ll_enabled(link))
                 ipv6ll_mode = IN6_ADDR_GEN_MODE_NONE;
-        else if (sysctl_read_ip_property(AF_INET6, link->ifname, "stable_secret", NULL) < 0)
-                /* The file may not exist. And even if it exists, when stable_secret is unset,
-                 * reading the file fails with EIO. */
-                ipv6ll_mode = IN6_ADDR_GEN_MODE_EUI64;
-        else
-                ipv6ll_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY;
+        else if (link->network->ipv6ll_address_gen_mode < 0) {
+                r = sysctl_read_ip_property(AF_INET6, link->ifname, "stable_secret", NULL);
+                if (r < 0) {
+                        /* The file may not exist. And even if it exists, when stable_secret is unset,
+                         * reading the file fails with EIO. */
+                        log_link_debug_errno(link, r, "Failed to read sysctl property stable_secret: %m");
+
+                        ipv6ll_mode = IN6_ADDR_GEN_MODE_EUI64;
+                } else
+                        ipv6ll_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY;
+        } else
+                ipv6ll_mode = link->network->ipv6ll_address_gen_mode;
 
         r = sd_netlink_message_append_u8(req, IFLA_INET6_ADDR_GEN_MODE, ipv6ll_mode);
         if (r < 0)
@@ -1787,6 +1969,53 @@ int link_down(Link *link, link_netlink_message_handler_t callback) {
         return 0;
 }
 
+static int link_group_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+        int r;
+
+        assert(link);
+
+        if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+                return 1;
+
+        r = sd_netlink_message_get_errno(m);
+        if (r < 0)
+                log_link_message_warning_errno(link, m, r, "Could not set group for the interface");
+
+        return 1;
+}
+
+static int link_set_group(Link *link) {
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
+        int r;
+
+        assert(link);
+        assert(link->network);
+        assert(link->manager);
+        assert(link->manager->rtnl);
+
+        if (link->network->group <= 0)
+                return 0;
+
+        log_link_debug(link, "Setting group");
+
+        r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m");
+
+        r = sd_netlink_message_append_u32(req, IFLA_GROUP, link->network->group);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not set link group: %m");
+
+        r = netlink_call_async(link->manager->rtnl, NULL, req, link_group_handler,
+                               link_netlink_destroy_callback, link);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
+
+        link_ref(link);
+
+        return 0;
+}
+
 static int link_handle_bound_to_list(Link *link) {
         Link *l;
         Iterator i;
@@ -2031,11 +2260,7 @@ static int link_append_to_master(Link *link, NetDev *netdev) {
         if (r < 0)
                 return r;
 
-        r = set_ensure_allocated(&master->slaves, NULL);
-        if (r < 0)
-                return r;
-
-        r = set_put(master->slaves, link);
+        r = set_ensure_put(&master->slaves, NULL, link);
         if (r <= 0)
                 return r;
 
@@ -2438,6 +2663,22 @@ static int link_set_ipv6_mtu(Link *link) {
         return 0;
 }
 
+static int link_set_ipv4_accept_local(Link *link) {
+        int r;
+
+        if (link->flags & IFF_LOOPBACK)
+                return 0;
+
+        if (link->network->ipv4_accept_local < 0)
+                return 0;
+
+        r = sysctl_write_ip_property_boolean(AF_INET, link->ifname, "accept_local", link->network->ipv4_accept_local);
+        if (r < 0)
+                log_link_warning_errno(link, r, "Cannot set IPv4 accept_local flag for interface: %m");
+
+        return 0;
+}
+
 static bool link_is_static_address_configured(Link *link, Address *address) {
         Address *net_address;
 
@@ -2450,6 +2691,9 @@ static bool link_is_static_address_configured(Link *link, Address *address) {
         LIST_FOREACH(addresses, net_address, link->network->static_addresses)
                 if (address_equal(net_address, address))
                         return true;
+                else if (address->family == AF_INET6 && net_address->family == AF_INET6 &&
+                         in_addr_equal(AF_INET6, &address->in_addr, &net_address->in_addr_peer) > 0)
+                        return true;
 
         return false;
 }
@@ -2705,24 +2949,46 @@ static int link_configure_ipv4_dad(Link *link) {
         return 0;
 }
 
-static int link_configure_qdiscs(Link *link) {
-        QDisc *qdisc;
+static int link_configure_traffic_control(Link *link) {
+        TrafficControl *tc;
         Iterator i;
         int r;
 
-        link->qdiscs_configured = false;
-        link->qdisc_messages = 0;
+        link->tc_configured = false;
+        link->tc_messages = 0;
 
-        ORDERED_HASHMAP_FOREACH(qdisc, link->network->qdiscs_by_section, i) {
-                r = qdisc_configure(link, qdisc);
+        ORDERED_HASHMAP_FOREACH(tc, link->network->tc_by_section, i) {
+                r = traffic_control_configure(link, tc);
                 if (r < 0)
                         return r;
         }
 
-        if (link->qdisc_messages == 0)
-                link->qdiscs_configured = true;
+        if (link->tc_messages == 0)
+                link->tc_configured = true;
         else
-                log_link_debug(link, "Configuring queuing discipline (qdisc)");
+                log_link_debug(link, "Configuring traffic control");
+
+        return 0;
+}
+
+static int link_configure_sr_iov(Link *link) {
+        SRIOV *sr_iov;
+        Iterator i;
+        int r;
+
+        link->sr_iov_configured = false;
+        link->sr_iov_messages = 0;
+
+        ORDERED_HASHMAP_FOREACH(sr_iov, link->network->sr_iov_by_section, i) {
+                r = sr_iov_configure(link, sr_iov);
+                if (r < 0)
+                        return r;
+        }
+
+        if (link->sr_iov_messages == 0)
+                link->sr_iov_configured = true;
+        else
+                log_link_debug(link, "Configuring SR-IOV");
 
         return 0;
 }
@@ -2734,7 +3000,11 @@ static int link_configure(Link *link) {
         assert(link->network);
         assert(link->state == LINK_STATE_INITIALIZED);
 
-        r = link_configure_qdiscs(link);
+        r = link_configure_traffic_control(link);
+        if (r < 0)
+                return r;
+
+        r = link_configure_sr_iov(link);
         if (r < 0)
                 return r;
 
@@ -2777,10 +3047,18 @@ static int link_configure(Link *link) {
         if (r < 0)
                 return r;
 
+        r = link_set_ipv4_accept_local(link);
+        if (r < 0)
+                return r;
+
         r = link_set_flags(link);
         if (r < 0)
                 return r;
 
+        r = link_set_group(link);
+        if (r < 0)
+                return r;
+
         if (link_ipv4ll_enabled(link, ADDRESS_FAMILY_IPV4 | ADDRESS_FAMILY_FALLBACK_IPV4)) {
                 r = ipv4ll_configure(link);
                 if (r < 0)
@@ -3015,12 +3293,12 @@ static int link_configure_duid(Link *link) {
                 r = set_put(m->links_requesting_uuid, link);
                 if (r < 0)
                         return log_oom();
+                if (r > 0)
+                        link_ref(link);
 
                 r = set_put(m->duids_requesting_uuid, duid);
                 if (r < 0)
                         return log_oom();
-
-                link_ref(link);
         }
 
         return 0;
@@ -3445,6 +3723,10 @@ network_file_fail:
                 if (r < 0)
                         return log_link_error_errno(link, r, "Failed to create DHCPv4 client: %m");
 
+                r = sd_dhcp_client_attach_event(link->dhcp_client, NULL, 0);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Failed to attach DHCPv4 event: %m");
+
                 r = sd_dhcp_client_set_request_address(link->dhcp_client, &address.in);
                 if (r < 0)
                         return log_link_error_errno(link, r, "Failed to set initial DHCPv4 address %s: %m", dhcp4_address);
@@ -3463,6 +3745,10 @@ dhcp4_address_fail:
                 if (r < 0)
                         return log_link_error_errno(link, r, "Failed to create IPv4LL client: %m");
 
+                r = sd_ipv4ll_attach_event(link->ipv4ll, NULL, 0);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Failed to attach IPv4LL event: %m");
+
                 r = sd_ipv4ll_set_address(link->ipv4ll, &address.in);
                 if (r < 0)
                         return log_link_error_errno(link, r, "Failed to set initial IPv4LL address %s: %m", ipv4ll_address);
@@ -3740,78 +4026,114 @@ int link_update(Link *link, sd_netlink_message *m) {
         /* The kernel may broadcast NEWLINK messages without the MAC address
            set, simply ignore them. */
         r = sd_netlink_message_read_ether_addr(m, IFLA_ADDRESS, &mac);
-        if (r >= 0) {
-                if (memcmp(link->mac.ether_addr_octet, mac.ether_addr_octet,
-                           ETH_ALEN)) {
-
-                        memcpy(link->mac.ether_addr_octet, mac.ether_addr_octet,
-                               ETH_ALEN);
-
-                        log_link_debug(link, "MAC address: "
-                                       "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
-                                       mac.ether_addr_octet[0],
-                                       mac.ether_addr_octet[1],
-                                       mac.ether_addr_octet[2],
-                                       mac.ether_addr_octet[3],
-                                       mac.ether_addr_octet[4],
-                                       mac.ether_addr_octet[5]);
-
-                        if (link->ipv4ll) {
-                                r = sd_ipv4ll_set_mac(link->ipv4ll, &link->mac);
+        if (r >= 0 && memcmp(link->mac.ether_addr_octet, mac.ether_addr_octet, ETH_ALEN) != 0) {
+
+                memcpy(link->mac.ether_addr_octet, mac.ether_addr_octet, ETH_ALEN);
+
+                log_link_debug(link, "Gained new MAC address: "
+                               "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
+                               mac.ether_addr_octet[0],
+                               mac.ether_addr_octet[1],
+                               mac.ether_addr_octet[2],
+                               mac.ether_addr_octet[3],
+                               mac.ether_addr_octet[4],
+                               mac.ether_addr_octet[5]);
+
+                if (link->ipv4ll) {
+                        bool restart = sd_ipv4ll_is_running(link->ipv4ll) > 0;
+
+                        if (restart) {
+                                r = sd_ipv4ll_stop(link->ipv4ll);
                                 if (r < 0)
-                                        return log_link_warning_errno(link, r, "Could not update MAC address in IPv4LL client: %m");
+                                        return log_link_warning_errno(link, r, "Could not stop IPv4LL client: %m");
                         }
 
-                        if (link->dhcp_client) {
-                                r = sd_dhcp_client_set_mac(link->dhcp_client,
-                                                           (const uint8_t *) &link->mac,
-                                                           sizeof (link->mac),
-                                                           ARPHRD_ETHER);
+                        r = sd_ipv4ll_set_mac(link->ipv4ll, &link->mac);
+                        if (r < 0)
+                                return log_link_warning_errno(link, r, "Could not update MAC address in IPv4LL client: %m");
+
+                        if (restart) {
+                                r = sd_ipv4ll_start(link->ipv4ll);
                                 if (r < 0)
-                                        return log_link_warning_errno(link, r, "Could not update MAC address in DHCP client: %m");
+                                        return log_link_warning_errno(link, r, "Could not restart IPv4LL client: %m");
+                        }
+                }
+
+                if (link->dhcp_client) {
+                        r = sd_dhcp_client_set_mac(link->dhcp_client,
+                                                   (const uint8_t *) &link->mac,
+                                                   sizeof (link->mac),
+                                                   ARPHRD_ETHER);
+                        if (r < 0)
+                                return log_link_warning_errno(link, r, "Could not update MAC address in DHCP client: %m");
+
+                        r = dhcp4_set_client_identifier(link);
+                        if (r < 0)
+                                return log_link_warning_errno(link, r, "Could not set DHCP client identifier: %m");
+                }
+
+                if (link->dhcp6_client) {
+                        const DUID* duid = link_get_duid(link);
+                        bool restart = sd_dhcp6_client_is_running(link->dhcp6_client) > 0;
 
-                                r = dhcp4_set_client_identifier(link);
+                        if (restart) {
+                                r = sd_dhcp6_client_stop(link->dhcp6_client);
                                 if (r < 0)
-                                        return r;
+                                        return log_link_warning_errno(link, r, "Could not stop DHCPv6 client: %m");
                         }
 
-                        if (link->dhcp6_client) {
-                                const DUID* duid = link_get_duid(link);
+                        r = sd_dhcp6_client_set_mac(link->dhcp6_client,
+                                                    (const uint8_t *) &link->mac,
+                                                    sizeof (link->mac),
+                                                    ARPHRD_ETHER);
+                        if (r < 0)
+                                return log_link_warning_errno(link, r, "Could not update MAC address in DHCPv6 client: %m");
 
-                                r = sd_dhcp6_client_set_mac(link->dhcp6_client,
-                                                            (const uint8_t *) &link->mac,
-                                                            sizeof (link->mac),
-                                                            ARPHRD_ETHER);
+                        if (link->network->iaid_set) {
+                                r = sd_dhcp6_client_set_iaid(link->dhcp6_client, link->network->iaid);
                                 if (r < 0)
-                                        return log_link_warning_errno(link, r, "Could not update MAC address in DHCPv6 client: %m");
-
-                                if (link->network->iaid_set) {
-                                        r = sd_dhcp6_client_set_iaid(link->dhcp6_client,
-                                                                     link->network->iaid);
-                                        if (r < 0)
-                                                return log_link_warning_errno(link, r, "Could not update DHCPv6 IAID: %m");
-                                }
-
-                                r = sd_dhcp6_client_set_duid(link->dhcp6_client,
-                                                             duid->type,
-                                                             duid->raw_data_len > 0 ? duid->raw_data : NULL,
-                                                             duid->raw_data_len);
+                                        return log_link_warning_errno(link, r, "Could not update DHCPv6 IAID: %m");
+                        }
+
+                        r = sd_dhcp6_client_set_duid(link->dhcp6_client,
+                                                     duid->type,
+                                                     duid->raw_data_len > 0 ? duid->raw_data : NULL,
+                                                     duid->raw_data_len);
+                        if (r < 0)
+                                return log_link_warning_errno(link, r, "Could not update DHCPv6 DUID: %m");
+
+                        if (restart) {
+                                r = sd_dhcp6_client_start(link->dhcp6_client);
                                 if (r < 0)
-                                        return log_link_warning_errno(link, r, "Could not update DHCPv6 DUID: %m");
+                                        return log_link_warning_errno(link, r, "Could not restart DHCPv6 client: %m");
                         }
+                }
+
+                if (link->radv) {
+                        bool restart = sd_radv_is_running(link->radv);
 
-                        if (link->radv) {
-                                r = sd_radv_set_mac(link->radv, &link->mac);
+                        if (restart) {
+                                r = sd_radv_stop(link->radv);
                                 if (r < 0)
-                                        return log_link_warning_errno(link, r, "Could not update MAC for Router Advertisement: %m");
+                                        return log_link_warning_errno(link, r, "Could not stop Router Advertisement: %m");
                         }
 
-                        if (link->ndisc) {
-                                r = sd_ndisc_set_mac(link->ndisc, &link->mac);
+                        r = sd_radv_set_mac(link->radv, &link->mac);
+                        if (r < 0)
+                                return log_link_warning_errno(link, r, "Could not update MAC for Router Advertisement: %m");
+
+                        if (restart) {
+                                r = sd_radv_start(link->radv);
                                 if (r < 0)
-                                        return log_link_warning_errno(link, r, "Could not update MAC for ndisc: %m");
+                                        return log_link_warning_errno(link, r, "Could not restart 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");
+                }
         }
 
         old_master = link->master_ifindex;
@@ -3880,32 +4202,80 @@ static void print_link_hashmap(FILE *f, const char *prefix, Hashmap* h) {
         fputc('\n', f);
 }
 
-static void link_save_dns(FILE *f, struct in_addr_data *dns, unsigned n_dns, bool *space) {
-        unsigned j;
-        int r;
+static void link_save_dns(Link *link, FILE *f, struct in_addr_full **dns, unsigned n_dns, bool *space) {
+        for (unsigned j = 0; j < n_dns; j++) {
+                const char *str;
 
-        for (j = 0; j < n_dns; j++) {
-                _cleanup_free_ char *b = NULL;
+                if (dns[j]->ifindex != 0 && dns[j]->ifindex != link->ifindex)
+                        continue;
 
-                r = in_addr_to_string(dns[j].family, &dns[j].address, &b);
-                if (r < 0) {
-                        log_debug_errno(r, "Failed to format address, ignoring: %m");
+                str = in_addr_full_to_string(dns[j]);
+                if (!str)
                         continue;
-                }
 
                 if (*space)
                         fputc(' ', f);
-                fputs(b, f);
+                fputs(str, f);
                 *space = true;
         }
 }
 
+static void serialize_addresses(
+                FILE *f,
+                const char *lvalue,
+                bool *space,
+                char **addresses,
+                sd_dhcp_lease *lease,
+                bool conditional,
+                sd_dhcp_lease_server_type what,
+                sd_dhcp6_lease *lease6,
+                bool conditional6,
+                int (*lease6_get_addr)(sd_dhcp6_lease*, const struct in6_addr**),
+                int (*lease6_get_fqdn)(sd_dhcp6_lease*, char ***)) {
+        int r;
+
+        bool _space = false;
+        if (!space)
+                space = &_space;
+
+        if (lvalue)
+                fprintf(f, "%s=", lvalue);
+        fputstrv(f, addresses, NULL, space);
+
+        if (lease && conditional) {
+                const struct in_addr *lease_addresses;
+
+                r = sd_dhcp_lease_get_servers(lease, what, &lease_addresses);
+                if (r > 0)
+                        serialize_in_addrs(f, lease_addresses, r, space, in4_addr_is_non_local);
+        }
+
+        if (lease6 && conditional6 && lease6_get_addr) {
+                const struct in6_addr *in6_addrs;
+
+                r = lease6_get_addr(lease6, &in6_addrs);
+                if (r > 0)
+                        serialize_in6_addrs(f, in6_addrs, r, space);
+        }
+
+        if (lease6 && conditional6 && lease6_get_fqdn) {
+                char **in6_hosts;
+
+                r = lease6_get_fqdn(lease6, &in6_hosts);
+                if (r > 0)
+                        fputstrv(f, in6_hosts, NULL, space);
+        }
+
+        if (lvalue)
+                fputc('\n', f);
+}
+
 int link_save(Link *link) {
+        const char *admin_state, *oper_state, *carrier_state, *address_state;
         _cleanup_free_ char *temp_path = NULL;
         _cleanup_fclose_ FILE *f = NULL;
-        const char *admin_state, *oper_state, *carrier_state, *address_state;
-        Address *a;
         Route *route;
+        Address *a;
         Iterator i;
         int r;
 
@@ -3950,133 +4320,78 @@ int link_save(Link *link) {
         if (link->network) {
                 char **dhcp6_domains = NULL, **dhcp_domains = NULL;
                 const char *dhcp_domainname = NULL, *p;
-                sd_dhcp6_lease *dhcp6_lease = NULL;
                 bool space;
 
                 fprintf(f, "REQUIRED_FOR_ONLINE=%s\n",
                         yes_no(link->network->required_for_online));
 
-                fprintf(f, "REQUIRED_OPER_STATE_FOR_ONLINE=%s",
-                        strempty(link_operstate_to_string(link->network->required_operstate_for_online.min)));
-
-                if (link->network->required_operstate_for_online.max != LINK_OPERSTATE_RANGE_DEFAULT.max)
-                        fprintf(f, ":%s",
-                                strempty(link_operstate_to_string(link->network->required_operstate_for_online.max)));
-
-                fprintf(f, "\n");
-
-                if (link->dhcp6_client) {
-                        r = sd_dhcp6_client_get_lease(link->dhcp6_client, &dhcp6_lease);
-                        if (r < 0 && r != -ENOMSG)
-                                log_link_debug_errno(link, r, "Failed to get DHCPv6 lease: %m");
-                }
+                LinkOperationalStateRange st = link->network->required_operstate_for_online;
+                fprintf(f, "REQUIRED_OPER_STATE_FOR_ONLINE=%s%s%s\n",
+                        strempty(link_operstate_to_string(st.min)),
+                        st.max != LINK_OPERSTATE_RANGE_DEFAULT.max ? ":" : "",
+                        st.max != LINK_OPERSTATE_RANGE_DEFAULT.max ? strempty(link_operstate_to_string(st.max)) : "");
 
                 fprintf(f, "NETWORK_FILE=%s\n", link->network->filename);
 
+                /************************************************************/
+
                 fputs("DNS=", f);
                 space = false;
-
                 if (link->n_dns != (unsigned) -1)
-                        link_save_dns(f, link->dns, link->n_dns, &space);
+                        link_save_dns(link, f, link->dns, link->n_dns, &space);
                 else
-                        link_save_dns(f, link->network->dns, link->network->n_dns, &space);
-
-                if (link->network->dhcp_use_dns &&
-                    link->dhcp_lease) {
-                        const struct in_addr *addresses;
-
-                        r = sd_dhcp_lease_get_dns(link->dhcp_lease, &addresses);
-                        if (r > 0)
-                                if (serialize_in_addrs(f, addresses, r, space, in4_addr_is_non_local) > 0)
-                                        space = true;
-                }
-
-                if (link->network->dhcp6_use_dns && dhcp6_lease) {
-                        struct in6_addr *in6_addrs;
-
-                        r = sd_dhcp6_lease_get_dns(dhcp6_lease, &in6_addrs);
-                        if (r > 0) {
-                                if (space)
-                                        fputc(' ', f);
-                                serialize_in6_addrs(f, in6_addrs, r);
-                                space = true;
-                        }
-                }
-
-                /* Make sure to flush out old entries before we use the NDISC data */
+                        link_save_dns(link, f, link->network->dns, link->network->n_dns, &space);
+
+                serialize_addresses(f, NULL, &space,
+                                    NULL,
+                                    link->dhcp_lease,
+                                    link->network->dhcp_use_dns,
+                                    SD_DHCP_LEASE_DNS,
+                                    link->dhcp6_lease,
+                                    link->network->dhcp6_use_dns,
+                                    sd_dhcp6_lease_get_dns,
+                                    NULL);
+
+                /* Make sure to flush out old entries before we use the NDisc data */
                 ndisc_vacuum(link);
 
                 if (link->network->ipv6_accept_ra_use_dns && link->ndisc_rdnss) {
                         NDiscRDNSS *dd;
 
-                        SET_FOREACH(dd, link->ndisc_rdnss, i) {
-                                if (space)
-                                        fputc(' ', f);
-
-                                serialize_in6_addrs(f, &dd->address, 1);
-                                space = true;
-                        }
+                        SET_FOREACH(dd, link->ndisc_rdnss, i)
+                                serialize_in6_addrs(f, &dd->address, 1, &space);
                 }
 
                 fputc('\n', f);
 
-                fputs("NTP=", f);
-                space = false;
-                fputstrv(f, link->ntp ?: link->network->ntp, NULL, &space);
-
-                if (link->network->dhcp_use_ntp &&
-                    link->dhcp_lease) {
-                        const struct in_addr *addresses;
-
-                        r = sd_dhcp_lease_get_ntp(link->dhcp_lease, &addresses);
-                        if (r > 0)
-                                if (serialize_in_addrs(f, addresses, r, space, in4_addr_is_non_local) > 0)
-                                        space = true;
-                }
-
-                fputc('\n', f);
-
-                fputs("SIP=", f);
-                space = false;
-                fputstrv(f, link->network->sip, NULL, &space);
-
-                if (link->network->dhcp_use_sip &&
-                    link->dhcp_lease) {
-                        const struct in_addr *addresses;
-
-                        r = sd_dhcp_lease_get_sip(link->dhcp_lease, &addresses);
-                        if (r > 0)
-                                if (serialize_in_addrs(f, addresses, r, space, in4_addr_is_non_local) > 0)
-                                        space = true;
-                }
+                /************************************************************/
 
-                if (link->network->dhcp6_use_ntp && dhcp6_lease) {
-                        struct in6_addr *in6_addrs;
-                        char **hosts;
-
-                        r = sd_dhcp6_lease_get_ntp_addrs(dhcp6_lease,
-                                                         &in6_addrs);
-                        if (r > 0) {
-                                if (space)
-                                        fputc(' ', f);
-                                serialize_in6_addrs(f, in6_addrs, r);
-                                space = true;
-                        }
+                serialize_addresses(f, "NTP", NULL,
+                                    link->ntp ?: link->network->ntp,
+                                    link->dhcp_lease,
+                                    link->network->dhcp_use_ntp,
+                                    SD_DHCP_LEASE_NTP,
+                                    link->dhcp6_lease,
+                                    link->network->dhcp6_use_ntp,
+                                    sd_dhcp6_lease_get_ntp_addrs,
+                                    sd_dhcp6_lease_get_ntp_fqdn);
 
-                        r = sd_dhcp6_lease_get_ntp_fqdn(dhcp6_lease, &hosts);
-                        if (r > 0)
-                                fputstrv(f, hosts, NULL, &space);
-                }
+                serialize_addresses(f, "SIP", NULL,
+                                    NULL,
+                                    link->dhcp_lease,
+                                    link->network->dhcp_use_sip,
+                                    SD_DHCP_LEASE_SIP,
+                                    NULL, false, NULL, NULL);
 
-                fputc('\n', f);
+                /************************************************************/
 
                 if (link->network->dhcp_use_domains != DHCP_USE_DOMAINS_NO) {
                         if (link->dhcp_lease) {
                                 (void) sd_dhcp_lease_get_domainname(link->dhcp_lease, &dhcp_domainname);
                                 (void) sd_dhcp_lease_get_search_domains(link->dhcp_lease, &dhcp_domains);
                         }
-                        if (dhcp6_lease)
-                                (void) sd_dhcp6_lease_get_domains(dhcp6_lease, &dhcp6_domains);
+                        if (link->dhcp6_lease)
+                                (void) sd_dhcp6_lease_get_domains(link->dhcp6_lease, &dhcp6_domains);
                 }
 
                 fputs("DOMAINS=", f);
@@ -4102,6 +4417,8 @@ int link_save(Link *link) {
 
                 fputc('\n', f);
 
+                /************************************************************/
+
                 fputs("ROUTE_DOMAINS=", f);
                 space = false;
                 ORDERED_SET_FOREACH(p, link->route_domains ?: link->network->route_domains, i)
@@ -4125,47 +4442,58 @@ int link_save(Link *link) {
 
                 fputc('\n', f);
 
+                /************************************************************/
+
                 fprintf(f, "LLMNR=%s\n",
                         resolve_support_to_string(link->llmnr >= 0 ? link->llmnr : link->network->llmnr));
+
+                /************************************************************/
+
                 fprintf(f, "MDNS=%s\n",
                         resolve_support_to_string(link->mdns >= 0 ? link->mdns : link->network->mdns));
-                if (link->dns_default_route >= 0)
-                        fprintf(f, "DNS_DEFAULT_ROUTE=%s\n", yes_no(link->dns_default_route));
-                else if (link->network->dns_default_route >= 0)
-                        fprintf(f, "DNS_DEFAULT_ROUTE=%s\n", yes_no(link->network->dns_default_route));
-
-                if (link->dns_over_tls_mode != _DNS_OVER_TLS_MODE_INVALID)
-                        fprintf(f, "DNS_OVER_TLS=%s\n",
-                                dns_over_tls_mode_to_string(link->dns_over_tls_mode));
-                else if (link->network->dns_over_tls_mode != _DNS_OVER_TLS_MODE_INVALID)
-                        fprintf(f, "DNS_OVER_TLS=%s\n",
-                                dns_over_tls_mode_to_string(link->network->dns_over_tls_mode));
-
-                if (link->dnssec_mode != _DNSSEC_MODE_INVALID)
-                        fprintf(f, "DNSSEC=%s\n",
-                                dnssec_mode_to_string(link->dnssec_mode));
-                else if (link->network->dnssec_mode != _DNSSEC_MODE_INVALID)
-                        fprintf(f, "DNSSEC=%s\n",
-                                dnssec_mode_to_string(link->network->dnssec_mode));
-
-                if (!set_isempty(link->dnssec_negative_trust_anchors)) {
-                        const char *n;
 
-                        fputs("DNSSEC_NTA=", f);
-                        space = false;
-                        SET_FOREACH(n, link->dnssec_negative_trust_anchors, i)
-                                fputs_with_space(f, n, NULL, &space);
-                        fputc('\n', f);
-                } else if (!set_isempty(link->network->dnssec_negative_trust_anchors)) {
+                /************************************************************/
+
+                int dns_default_route =
+                        link->dns_default_route >= 0 ? link->dns_default_route :
+                        link->network->dns_default_route;
+                if (dns_default_route >= 0)
+                        fprintf(f, "DNS_DEFAULT_ROUTE=%s\n", yes_no(dns_default_route));
+
+                /************************************************************/
+
+                DnsOverTlsMode dns_over_tls_mode =
+                        link->dns_over_tls_mode != _DNS_OVER_TLS_MODE_INVALID ? link->dns_over_tls_mode :
+                        link->network->dns_over_tls_mode;
+                if (dns_over_tls_mode != _DNS_OVER_TLS_MODE_INVALID)
+                        fprintf(f, "DNS_OVER_TLS=%s\n", dns_over_tls_mode_to_string(dns_over_tls_mode));
+
+                /************************************************************/
+
+                DnssecMode dnssec_mode =
+                        link->dnssec_mode != _DNSSEC_MODE_INVALID ? link->dnssec_mode :
+                        link->network->dnssec_mode;
+                if (dnssec_mode != _DNSSEC_MODE_INVALID)
+                        fprintf(f, "DNSSEC=%s\n", dnssec_mode_to_string(dnssec_mode));
+
+                /************************************************************/
+
+                Set *nta_anchors = link->dnssec_negative_trust_anchors;
+                if (set_isempty(nta_anchors))
+                        nta_anchors = link->network->dnssec_negative_trust_anchors;
+
+                if (!set_isempty(nta_anchors)) {
                         const char *n;
 
                         fputs("DNSSEC_NTA=", f);
                         space = false;
-                        SET_FOREACH(n, link->network->dnssec_negative_trust_anchors, i)
+                        SET_FOREACH(n, nta_anchors, i)
                                 fputs_with_space(f, n, NULL, &space);
                         fputc('\n', f);
                 }
 
+                /************************************************************/
+
                 fputs("ADDRESSES=", f);
                 space = false;
                 SET_FOREACH(a, link->addresses, i) {
@@ -4180,6 +4508,8 @@ int link_save(Link *link) {
                 }
                 fputc('\n', f);
 
+                /************************************************************/
+
                 fputs("ROUTES=", f);
                 space = false;
                 SET_FOREACH(route, link->routes, i) {
@@ -4202,22 +4532,6 @@ int link_save(Link *link) {
         print_link_hashmap(f, "CARRIER_BOUND_BY=", link->bound_by_links);
 
         if (link->dhcp_lease) {
-                struct in_addr address;
-                const char *tz = NULL;
-
-                assert(link->network);
-
-                r = sd_dhcp_lease_get_timezone(link->dhcp_lease, &tz);
-                if (r >= 0)
-                        fprintf(f, "TIMEZONE=%s\n", tz);
-
-                r = sd_dhcp_lease_get_address(link->dhcp_lease, &address);
-                if (r >= 0) {
-                        fputs("DHCP4_ADDRESS=", f);
-                        serialize_in_addrs(f, &address, 1, false, NULL);
-                        fputc('\n', f);
-                }
-
                 r = dhcp_lease_save(link->dhcp_lease, link->lease_file);
                 if (r < 0)
                         goto fail;
@@ -4239,6 +4553,19 @@ int link_save(Link *link) {
                 }
         }
 
+        if (link->dhcp6_client) {
+                _cleanup_free_ char *duid = NULL;
+                uint32_t iaid;
+
+                r = sd_dhcp6_client_get_iaid(link->dhcp6_client, &iaid);
+                if (r >= 0)
+                        fprintf(f, "DHCP6_CLIENT_IAID=0x%x\n", iaid);
+
+                r = sd_dhcp6_client_duid_as_string(link->dhcp6_client, &duid);
+                if (r >= 0)
+                        fprintf(f, "DHCP6_CLIENT_DUID=%s\n", duid);
+        }
+
         r = fflush_and_check(f);
         if (r < 0)
                 goto fail;
@@ -4267,16 +4594,10 @@ void link_dirty(Link *link) {
         /* mark manager dirty as link is dirty */
         manager_dirty(link->manager);
 
-        r = set_ensure_allocated(&link->manager->dirty_links, NULL);
-        if (r < 0)
-                /* allocation errors are ignored */
-                return;
-
-        r = set_put(link->manager->dirty_links, link);
+        r = set_ensure_put(&link->manager->dirty_links, NULL, link);
         if (r <= 0)
-                /* don't take another ref if the link was already dirty */
+                /* Ignore allocation errors and don't take another ref if the link was already dirty */
                 return;
-
         link_ref(link);
 }
 
@@ -4288,6 +4609,17 @@ void link_clean(Link *link) {
         link_unref(set_remove(link->manager->dirty_links, link));
 }
 
+int link_save_and_clean(Link *link) {
+        int r;
+
+        r = link_save(link);
+        if (r < 0)
+                return r;
+
+        link_clean(link);
+        return 0;
+}
+
 static const char* const link_state_table[_LINK_STATE_MAX] = {
         [LINK_STATE_PENDING] = "pending",
         [LINK_STATE_INITIALIZED] = "initialized",
index 7494f88b8c852058c529bd061c9b630b48a10c78..7f99c0f47b2ac4932fbedf4a262da3c684f4b609 100644 (file)
@@ -81,11 +81,13 @@ typedef struct Link {
         unsigned nexthop_messages;
         unsigned routing_policy_rule_messages;
         unsigned routing_policy_rule_remove_messages;
-        unsigned qdisc_messages;
+        unsigned tc_messages;
+        unsigned sr_iov_messages;
         unsigned enslaving;
 
         Set *addresses;
         Set *addresses_foreign;
+        Set *static_addresses;
         Set *neighbors;
         Set *neighbors_foreign;
         Set *routes;
@@ -94,30 +96,29 @@ typedef struct Link {
         Set *nexthops_foreign;
 
         sd_dhcp_client *dhcp_client;
-        sd_dhcp_lease *dhcp_lease, *dhcp_lease_old;
-        Set *dhcp_routes;
+        sd_dhcp_lease *dhcp_lease;
+        Address *dhcp_address, *dhcp_address_old;
+        Set *dhcp_routes, *dhcp_routes_old;
         char *lease_file;
         uint32_t original_mtu;
         unsigned dhcp4_messages;
+        unsigned dhcp4_remove_messages;
         bool dhcp4_route_failed:1;
         bool dhcp4_route_retrying:1;
         bool dhcp4_configured:1;
-        bool dhcp6_configured:1;
-
-        unsigned ndisc_messages;
-        bool ndisc_configured;
+        bool dhcp4_address_bind:1;
 
         sd_ipv4ll *ipv4ll;
-        bool ipv4ll_address:1;
+        bool ipv4ll_address_configured:1;
 
         bool addresses_configured:1;
         bool addresses_ready:1;
         bool neighbors_configured:1;
         bool static_routes_configured:1;
-        bool static_routes_ready:1;
         bool static_nexthops_configured:1;
         bool routing_policy_rules_configured:1;
-        bool qdiscs_configured:1;
+        bool tc_configured:1;
+        bool sr_iov_configured:1;
         bool setting_mtu:1;
         bool setting_genmode:1;
         bool ipv6_mtu_set:1;
@@ -129,10 +130,30 @@ typedef struct Link {
         sd_ndisc *ndisc;
         Set *ndisc_rdnss;
         Set *ndisc_dnssl;
+        Set *ndisc_addresses, *ndisc_addresses_old;
+        Set *ndisc_routes, *ndisc_routes_old;
+        unsigned ndisc_addresses_messages;
+        unsigned ndisc_routes_messages;
+        bool ndisc_addresses_configured:1;
+        bool ndisc_routes_configured:1;
 
         sd_radv *radv;
 
         sd_dhcp6_client *dhcp6_client;
+        sd_dhcp6_lease *dhcp6_lease;
+        Set *dhcp6_addresses, *dhcp6_addresses_old;
+        Set *dhcp6_routes, *dhcp6_routes_old;
+        Set *dhcp6_pd_addresses, *dhcp6_pd_addresses_old;
+        Set *dhcp6_pd_routes, *dhcp6_pd_routes_old;
+        unsigned dhcp6_address_messages;
+        unsigned dhcp6_route_messages;
+        unsigned dhcp6_pd_address_messages;
+        unsigned dhcp6_pd_route_messages;
+        bool dhcp6_address_configured:1;
+        bool dhcp6_route_configured:1;
+        bool dhcp6_pd_address_configured:1;
+        bool dhcp6_pd_route_configured:1;
+        bool dhcp6_pd_prefixes_assigned:1;
 
         /* This is about LLDP reception */
         sd_lldp *lldp;
@@ -150,8 +171,8 @@ typedef struct Link {
         struct rtnl_link_stats64 stats_old, stats_new;
         bool stats_updated;
 
-        /* All kinds of DNS configuration */
-        struct in_addr_data *dns;
+        /* All kinds of DNS configuration the user configured via D-Bus */
+        struct in_addr_full **dns;
         unsigned n_dns;
         OrderedSet *search_domains, *route_domains;
 
@@ -162,6 +183,7 @@ typedef struct Link {
         DnsOverTlsMode dns_over_tls_mode;
         Set *dnssec_negative_trust_anchors;
 
+        /* Similar, but NTP server configuration */
         char **ntp;
 } Link;
 
@@ -195,6 +217,7 @@ int link_update(Link *link, sd_netlink_message *message);
 void link_dirty(Link *link);
 void link_clean(Link *link);
 int link_save(Link *link);
+int link_save_and_clean(Link *link);
 
 int link_carrier_reset(Link *link);
 bool link_has_carrier(Link *link);
@@ -214,7 +237,6 @@ uint32_t link_get_vrf_table(Link *link);
 uint32_t link_get_dhcp_route_table(Link *link);
 uint32_t link_get_ipv6_accept_ra_route_table(Link *link);
 int link_request_set_routes(Link *link);
-int link_request_set_nexthop(Link *link);
 
 int link_reconfigure(Link *link, bool force);
 
index 93ab2144a9b15f581f26c066bd1a4f4edea7e2ad..9e0b4475240be697a2390cdefe733933d8af6456 100644 (file)
@@ -20,6 +20,7 @@
 #include "string-util.h"
 #include "strv.h"
 #include "unaligned.h"
+#include "web-util.h"
 
 /* The LLDP spec calls this "txFastInit", see 9.2.5.19 */
 #define LLDP_TX_FAST_INIT 4U
@@ -86,9 +87,11 @@ static int lldp_make_packet(
                 const char *pretty_hostname,
                 uint16_t system_capabilities,
                 uint16_t enabled_capabilities,
+                char *mud,
                 void **ret, size_t *sz) {
 
-        size_t machine_id_length, ifname_length, port_description_length = 0, hostname_length = 0, pretty_hostname_length = 0;
+        size_t machine_id_length, ifname_length, port_description_length = 0, hostname_length = 0,
+                pretty_hostname_length = 0, mud_length = 0;
         _cleanup_free_ void *packet = NULL;
         struct ether_header *h;
         uint8_t *p;
@@ -115,6 +118,9 @@ static int lldp_make_packet(
         if (pretty_hostname)
                 pretty_hostname_length = strlen(pretty_hostname);
 
+        if (mud)
+                mud_length = strlen(mud);
+
         l = sizeof(struct ether_header) +
                 /* Chassis ID */
                 2 + 1 + machine_id_length +
@@ -139,6 +145,10 @@ static int lldp_make_packet(
         if (pretty_hostname)
                 l += 2 + pretty_hostname_length;
 
+        /* MUD URL */
+        if (mud)
+                l += 2 + sizeof(SD_LLDP_OUI_MUD) + 1 + mud_length;
+
         packet = malloc(l);
         if (!packet)
                 return -ENOMEM;
@@ -189,6 +199,32 @@ static int lldp_make_packet(
                 p = mempcpy(p, pretty_hostname, pretty_hostname_length);
         }
 
+        if (mud) {
+                uint8_t oui_mud[sizeof(SD_LLDP_OUI_MUD)] = {0x00, 0x00, 0x5E};
+                /*
+                 * +--------+--------+----------+---------+--------------
+                 * |TLV Type|  len   |   OUI    |subtype  | MUDString
+                 * |  =127  |        |= 00 00 5E|  = 1    |
+                 * |(7 bits)|(9 bits)|(3 octets)|(1 octet)|(1-255 octets)
+                 * +--------+--------+----------+---------+--------------
+                 * where:
+
+                 * o  TLV Type = 127 indicates a vendor-specific TLV
+                 * o  len = indicates the TLV string length
+                 * o  OUI = 00 00 5E is the organizationally unique identifier of IANA
+                 * o  subtype = 1 (as assigned by IANA for the MUDstring)
+                 * o  MUDstring = the length MUST NOT exceed 255 octets
+                 */
+
+                r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_PRIVATE, sizeof(SD_LLDP_OUI_MUD) + 1 + mud_length);
+                if (r < 0)
+                        return r;
+
+                p = mempcpy(p, &oui_mud, sizeof(SD_LLDP_OUI_MUD));
+                *(p++) = SD_LLDP_OUI_SUBTYPE_MUD_USAGE_DESCRIPTION;
+                p = mempcpy(p, mud, mud_length);
+        }
+
         r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_SYSTEM_CAPABILITIES, 4);
         if (r < 0)
                 return r;
@@ -286,6 +322,7 @@ static int link_send_lldp(Link *link) {
                              pretty_hostname,
                              SD_LLDP_SYSTEM_CAPABILITIES_STATION|SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE|SD_LLDP_SYSTEM_CAPABILITIES_ROUTER,
                              caps,
+                             link->network ? link->network->lldp_mud : NULL,
                              &packet, &packet_size);
         if (r < 0)
                 return r;
@@ -410,7 +447,7 @@ int config_parse_lldp_emit(
         else {
                 r = parse_boolean(rvalue);
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse LLDP emission setting, ignoring: %s", rvalue);
+                        log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse LLDP emission setting, ignoring: %s", rvalue);
                         return 0;
                 }
 
@@ -419,3 +456,40 @@ int config_parse_lldp_emit(
 
         return 0;
 }
+
+int config_parse_lldp_mud(
+                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_ char *unescaped = NULL;
+        Network *n = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+
+        r = cunescape(rvalue, 0, &unescaped);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to Failed to unescape LLDP MUD URL, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        if (!http_url_is_valid(unescaped) || strlen(unescaped) > 255) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "Failed to parse LLDP MUD URL '%s', ignoring: %m", rvalue);
+
+                return 0;
+        }
+
+        return free_and_replace(n->lldp_mud, unescaped);
+}
index 561becda41c84fec4d967165e0fe181009e9c637..1409984ac02fda3f0bcf80897694f3089a727ccf 100644 (file)
@@ -20,3 +20,4 @@ int link_lldp_emit_start(Link *link);
 void link_lldp_emit_stop(Link *link);
 
 CONFIG_PARSER_PROTOTYPE(config_parse_lldp_emit);
+CONFIG_PARSER_PROTOTYPE(config_parse_lldp_mud);
index b7279a582ce4389d92146aeae34dd10536c2cb8e..9db59d93f8106f87b6e643e57e068d34bae5fe7a 100644 (file)
@@ -6,6 +6,7 @@
 
 #include "alloc-util.h"
 #include "bus-common-errors.h"
+#include "bus-message-util.h"
 #include "bus-polkit.h"
 #include "networkd-link-bus.h"
 #include "networkd-link.h"
@@ -93,17 +94,16 @@ static int method_get_link_by_index(sd_bus_message *message, void *userdata, sd_
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
         _cleanup_free_ char *path = NULL;
         Manager *manager = userdata;
-        int32_t index;
+        int ifindex, r;
         Link *link;
-        int r;
 
-        r = sd_bus_message_read(message, "i", &index);
+        r = bus_message_read_ifindex(message, error, &ifindex);
         if (r < 0)
                 return r;
 
-        link = hashmap_get(manager->links, INT_TO_PTR((int) index));
+        link = hashmap_get(manager->links, INT_TO_PTR(ifindex));
         if (!link)
-                return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_LINK, "Link %" PRIi32 " not known", index);
+                return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_LINK, "Link %i not known", ifindex);
 
         r = sd_bus_message_new_method_return(message, &reply);
         if (r < 0)
@@ -128,14 +128,10 @@ static int call_link_method(Manager *m, sd_bus_message *message, sd_bus_message_
         assert(message);
         assert(handler);
 
-        assert_cc(sizeof(int) == sizeof(int32_t));
-        r = sd_bus_message_read(message, "i", &ifindex);
+        r = bus_message_read_ifindex(message, error, &ifindex);
         if (r < 0)
                 return r;
 
-        if (ifindex <= 0)
-                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
-
         l = hashmap_get(m->links, INT_TO_PTR(ifindex));
         if (!l)
                 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_LINK, "Link %i not known", ifindex);
@@ -151,6 +147,10 @@ static int bus_method_set_link_dns_servers(sd_bus_message *message, void *userda
         return call_link_method(userdata, message, bus_link_method_set_dns_servers, error);
 }
 
+static int bus_method_set_link_dns_servers_ex(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        return call_link_method(userdata, message, bus_link_method_set_dns_servers_ex, error);
+}
+
 static int bus_method_set_link_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         return call_link_method(userdata, message, bus_link_method_set_domains, error);
 }
@@ -191,6 +191,10 @@ static int bus_method_renew_link(sd_bus_message *message, void *userdata, sd_bus
         return call_link_method(userdata, message, bus_link_method_renew, error);
 }
 
+static int bus_method_force_renew_link(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        return call_link_method(userdata, message, bus_link_method_force_renew, error);
+}
+
 static int bus_method_reconfigure_link(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         return call_link_method(userdata, message, bus_link_method_reconfigure, error);
 }
@@ -239,6 +243,7 @@ const sd_bus_vtable manager_vtable[] = {
         SD_BUS_METHOD("GetLinkByIndex", "i", "so", method_get_link_by_index, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("SetLinkNTP", "ias", NULL, bus_method_set_link_ntp_servers, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("SetLinkDNS", "ia(iay)", NULL, bus_method_set_link_dns_servers, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("SetLinkDNSEx", "ia(iayqs)", NULL, bus_method_set_link_dns_servers_ex, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("SetLinkDomains", "ia(sb)", NULL, bus_method_set_link_domains, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("SetLinkDefaultRoute", "ib", NULL, bus_method_set_link_default_route, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("SetLinkLLMNR", "is", NULL, bus_method_set_link_llmnr, SD_BUS_VTABLE_UNPRIVILEGED),
@@ -249,6 +254,7 @@ const sd_bus_vtable manager_vtable[] = {
         SD_BUS_METHOD("RevertLinkNTP", "i", NULL, bus_method_revert_link_ntp, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("RevertLinkDNS", "i", NULL, bus_method_revert_link_dns, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("RenewLink", "i", NULL, bus_method_renew_link, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("ForceRenewLink", "i", NULL, bus_method_force_renew_link, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("ReconfigureLink", "i", NULL, bus_method_reconfigure_link, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("Reload", NULL, NULL, bus_method_reload, SD_BUS_VTABLE_UNPRIVILEGED),
 
index 24bb72aeec418ab112b6061434bce4e0879f4203..a6c1a39e2384064a448bdf7eb9a28b4807ec6940 100644 (file)
@@ -11,6 +11,7 @@
 #include "sd-netlink.h"
 
 #include "alloc-util.h"
+#include "bus-log-control-api.h"
 #include "bus-polkit.h"
 #include "bus-util.h"
 #include "conf-parser.h"
@@ -23,6 +24,7 @@
 #include "local-addresses.h"
 #include "netlink-util.h"
 #include "network-internal.h"
+#include "networkd-dhcp-server-bus.h"
 #include "networkd-dhcp6.h"
 #include "networkd-link-bus.h"
 #include "networkd-manager-bus.h"
@@ -30,6 +32,7 @@
 #include "networkd-network-bus.h"
 #include "networkd-speed-meter.h"
 #include "ordered-set.h"
+#include "path-lookup.h"
 #include "path-util.h"
 #include "set.h"
 #include "signal-util.h"
@@ -101,7 +104,7 @@ static int match_prepare_for_sleep(sd_bus_message *message, void *userdata, sd_b
 
         r = sd_bus_message_read(message, "b", &b);
         if (r < 0) {
-                log_debug_errno(r, "Failed to parse PrepareForSleep signal: %m");
+                bus_log_parse_error(r);
                 return 0;
         }
 
@@ -152,6 +155,10 @@ int manager_connect_bus(Manager *m) {
         if (r < 0)
                return log_error_errno(r, "Failed to add link object vtable: %m");
 
+        r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/network1/link", "org.freedesktop.network1.DHCPServer", dhcp_server_vtable, link_object_find, m);
+        if (r < 0)
+               return log_error_errno(r, "Failed to add link object vtable: %m");
+
         r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/network1/link", link_node_enumerator, m);
         if (r < 0)
                 return log_error_errno(r, "Failed to add link enumerator: %m");
@@ -164,6 +171,10 @@ int manager_connect_bus(Manager *m) {
         if (r < 0)
                 return log_error_errno(r, "Failed to add network enumerator: %m");
 
+        r = bus_log_control_api_register(m->bus);
+        if (r < 0)
+                return r;
+
         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 request name: %m");
@@ -230,7 +241,7 @@ static int manager_udev_process_link(sd_device_monitor *monitor, sd_device *devi
                 return 0;
         }
         if (r > 0) {
-                log_device_debug(device, "Interface is under renaming, wait for the interface to be renamed: %m");
+                log_device_debug(device, "Interface is under renaming, wait for the interface to be renamed.");
                 return 0;
         }
 
@@ -494,7 +505,9 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo
 
                 log_link_debug(link,
                                "%s route: dst: %s%s, src: %s, gw: %s, prefsrc: %s, scope: %s, table: %s, proto: %s, type: %s",
-                               type == RTM_DELROUTE ? "Forgetting" : route ? "Received remembered" : "Remembering",
+                               (!route && !link->manager->manage_foreign_routes) ? "Ignoring received foreign" :
+                               type == RTM_DELROUTE ? "Forgetting" :
+                               route ? "Received remembered" : "Remembering",
                                strna(buf_dst), strempty(buf_dst_prefixlen),
                                strna(buf_src), strna(buf_gw), strna(buf_prefsrc),
                                format_route_scope(tmp->scope, buf_scope, sizeof buf_scope),
@@ -505,7 +518,7 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo
 
         switch (type) {
         case RTM_NEWROUTE:
-                if (!route) {
+                if (!route && link->manager->manage_foreign_routes) {
                         /* A route appeared that we did not request */
                         r = route_add_foreign(link, tmp, &route);
                         if (r < 0) {
@@ -844,8 +857,10 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
                                                valid_str ? "for " : "forever", strempty(valid_str));
                 }
 
-                /* address_update() logs internally, so we don't need to. */
-                (void) address_update(address, flags, scope, &cinfo);
+                /* address_update() logs internally, so we don't need to here. */
+                r = address_update(address, flags, scope, &cinfo);
+                if (r < 0)
+                        link_enter_failed(link);
 
                 break;
 
@@ -1260,6 +1275,9 @@ int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message,
         if (r < 0 && r != -ENODATA) {
                 log_warning_errno(r, "rtnl: could not get NHA_OIF attribute, ignoring: %m");
                 return 0;
+        } else if (tmp->oif <= 0) {
+                log_warning("rtnl: received nexthop message with invalid ifindex %d, ignoring.", tmp->oif);
+                return 0;
         }
 
         r = link_get(m, tmp->oif, &link);
@@ -1409,33 +1427,36 @@ static int manager_connect_rtnl(Manager *m) {
         return 0;
 }
 
-static int ordered_set_put_in_addr_data(OrderedSet *s, const struct in_addr_data *address) {
-        char *p;
+static int ordered_set_put_dns_server(OrderedSet *s, int ifindex, struct in_addr_full *dns) {
+        const char *p;
         int r;
 
         assert(s);
-        assert(address);
+        assert(dns);
 
-        r = in_addr_to_string(address->family, &address->address, &p);
-        if (r < 0)
-                return r;
+        if (dns->ifindex != 0 && dns->ifindex != ifindex)
+                return 0;
 
-        r = ordered_set_consume(s, p);
+        p = in_addr_full_to_string(dns);
+        if (!p)
+                return 0;
+
+        r = ordered_set_put_strdup(s, p);
         if (r == -EEXIST)
                 return 0;
 
         return r;
 }
 
-static int ordered_set_put_in_addr_datav(OrderedSet *s, const struct in_addr_data *addresses, unsigned n) {
+static int ordered_set_put_dns_servers(OrderedSet *s, int ifindex, struct in_addr_full **dns, unsigned n) {
         int r, c = 0;
         unsigned i;
 
         assert(s);
-        assert(addresses || n == 0);
+        assert(dns || n == 0);
 
         for (i = 0; i < n; i++) {
-                r = ordered_set_put_in_addr_data(s, addresses+i);
+                r = ordered_set_put_dns_server(s, ifindex, dns[i]);
                 if (r < 0)
                         return r;
 
@@ -1511,8 +1532,8 @@ static int manager_save(Manager *m) {
         if (!ntp)
                 return -ENOMEM;
 
-       sip = ordered_set_new(&string_hash_ops);
-       if (!sip)
+        sip = ordered_set_new(&string_hash_ops);
+        if (!sip)
                 return -ENOMEM;
 
         search_domains = ordered_set_new(&dns_name_hash_ops);
@@ -1524,6 +1545,8 @@ static int manager_save(Manager *m) {
                 return -ENOMEM;
 
         HASHMAP_FOREACH(link, m->links, i) {
+                const struct in_addr *addresses;
+
                 if (link->flags & IFF_LOOPBACK)
                         continue;
 
@@ -1540,7 +1563,10 @@ static int manager_save(Manager *m) {
                         continue;
 
                 /* First add the static configured entries */
-                r = ordered_set_put_in_addr_datav(dns, link->network->dns, link->network->n_dns);
+                if (link->n_dns != (unsigned) -1)
+                        r = ordered_set_put_dns_servers(dns, link->ifindex, link->dns, link->n_dns);
+                else
+                        r = ordered_set_put_dns_servers(dns, link->ifindex, link->network->dns, link->network->n_dns);
                 if (r < 0)
                         return r;
 
@@ -1561,8 +1587,6 @@ static int manager_save(Manager *m) {
 
                 /* Secondly, add the entries acquired via DHCP */
                 if (link->network->dhcp_use_dns) {
-                        const struct in_addr *addresses;
-
                         r = sd_dhcp_lease_get_dns(link->dhcp_lease, &addresses);
                         if (r > 0) {
                                 r = ordered_set_put_in4_addrv(dns, addresses, r, in4_addr_is_non_local);
@@ -1573,8 +1597,6 @@ static int manager_save(Manager *m) {
                 }
 
                 if (link->network->dhcp_use_ntp) {
-                        const struct in_addr *addresses;
-
                         r = sd_dhcp_lease_get_ntp(link->dhcp_lease, &addresses);
                         if (r > 0) {
                                 r = ordered_set_put_in4_addrv(ntp, addresses, r, in4_addr_is_non_local);
@@ -1585,8 +1607,6 @@ static int manager_save(Manager *m) {
                 }
 
                 if (link->network->dhcp_use_sip) {
-                        const struct in_addr *addresses;
-
                         r = sd_dhcp_lease_get_sip(link->dhcp_lease, &addresses);
                         if (r > 0) {
                                 r = ordered_set_put_in4_addrv(sip, addresses, r, in4_addr_is_non_local);
@@ -1709,8 +1729,7 @@ static int manager_dirty_handler(sd_event_source *s, void *userdata) {
                 manager_save(m);
 
         SET_FOREACH(link, m->dirty_links, i)
-                if (link_save(link) >= 0)
-                        link_clean(link);
+                (void) link_save_and_clean(link);
 
         return 1;
 }
@@ -1747,6 +1766,7 @@ int manager_new(Manager **ret) {
 
         *m = (Manager) {
                 .speed_meter_interval_usec = SPEED_METER_DEFAULT_TIME_INTERVAL,
+                .manage_foreign_routes = true,
                 .ethtool_fd = -1,
         };
 
@@ -1803,27 +1823,20 @@ int manager_new(Manager **ret) {
 }
 
 void manager_free(Manager *m) {
-        struct in6_addr *a;
         AddressPool *pool;
         Link *link;
+        Iterator i;
 
         if (!m)
                 return;
 
         free(m->state_file);
 
-        while ((a = hashmap_first_key(m->dhcp6_prefixes)))
-                (void) dhcp6_prefix_remove(m, a);
-        m->dhcp6_prefixes = hashmap_free(m->dhcp6_prefixes);
-
-        while ((link = hashmap_steal_first(m->links))) {
-                if (link->dhcp6_client)
-                        (void) dhcp6_lease_pd_prefix_lost(link->dhcp6_client, link);
-
+        HASHMAP_FOREACH(link, m->links, i)
                 (void) link_stop_clients(link, true);
 
-                link_unref(link);
-        }
+        m->dhcp6_prefixes = hashmap_free_with_destructor(m->dhcp6_prefixes, dhcp6_pd_free);
+        m->dhcp6_pd_prefixes = set_free_with_destructor(m->dhcp6_pd_prefixes, dhcp6_pd_free);
 
         m->dirty_links = set_free_with_destructor(m->dirty_links, link_unref);
         m->links_requesting_uuid = set_free_with_destructor(m->links_requesting_uuid, link_unref);
@@ -1839,9 +1852,9 @@ void manager_free(Manager *m) {
 
         /* routing_policy_rule_free() access m->rules and m->rules_foreign.
          * So, it is necessary to set NULL after the sets are freed. */
-        m->rules = set_free_with_destructor(m->rules, routing_policy_rule_free);
-        m->rules_foreign = set_free_with_destructor(m->rules_foreign, routing_policy_rule_free);
-        set_free_with_destructor(m->rules_saved, routing_policy_rule_free);
+        m->rules = set_free(m->rules);
+        m->rules_foreign = set_free(m->rules_foreign);
+        set_free(m->rules_saved);
 
         sd_netlink_unref(m->rtnl);
         sd_netlink_unref(m->genl);
@@ -1880,7 +1893,7 @@ int manager_start(Manager *m) {
         manager_save(m);
 
         HASHMAP_FOREACH(link, m->links, i)
-                link_save(link);
+                (void) link_save(link);
 
         return 0;
 }
@@ -2019,6 +2032,9 @@ int manager_rtnl_enumerate_routes(Manager *m) {
         assert(m);
         assert(m->rtnl);
 
+        if (!m->manage_foreign_routes)
+                return 0;
+
         r = sd_rtnl_message_new_route(m->rtnl, &req, RTM_GETROUTE, 0, 0);
         if (r < 0)
                 return r;
@@ -2300,23 +2316,15 @@ int manager_request_product_uuid(Manager *m, Link *link) {
 
                 assert_se(duid = link_get_duid(link));
 
-                r = set_ensure_allocated(&m->links_requesting_uuid, NULL);
-                if (r < 0)
-                        return log_oom();
-
-                r = set_ensure_allocated(&m->duids_requesting_uuid, NULL);
+                r = set_ensure_put(&m->links_requesting_uuid, NULL, link);
                 if (r < 0)
                         return log_oom();
+                if (r > 0)
+                        link_ref(link);
 
-                r = set_put(m->links_requesting_uuid, link);
+                r = set_ensure_put(&m->duids_requesting_uuid, NULL, duid);
                 if (r < 0)
                         return log_oom();
-
-                r = set_put(m->duids_requesting_uuid, duid);
-                if (r < 0)
-                        return log_oom();
-
-                link_ref(link);
         }
 
         if (!m->bus || sd_bus_is_ready(m->bus) <= 0) {
index 8fdb38480052dced00156609e25c87306598fbd9..f4b37bd6a9577f4c3510b437f3f4781ef81de1cc 100644 (file)
@@ -31,6 +31,7 @@ struct Manager {
         bool enumerating:1;
         bool dirty:1;
         bool restarting:1;
+        bool manage_foreign_routes;
 
         Set *dirty_links;
 
@@ -43,6 +44,7 @@ struct Manager {
         Hashmap *netdevs;
         OrderedHashmap *networks;
         Hashmap *dhcp6_prefixes;
+        Set *dhcp6_pd_prefixes;
         LIST_HEAD(AddressPool, address_pools);
 
         usec_t network_dirs_ts_usec;
index c45fec5430508bd550e184281c84998648ea67c2..349f0548afd7d645d41e674721dbf445850f1b59 100644 (file)
@@ -13,6 +13,7 @@
 #include "networkd-manager.h"
 #include "networkd-ndisc.h"
 #include "networkd-route.h"
+#include "string-table.h"
 #include "string-util.h"
 #include "strv.h"
 
 
 #define NDISC_APP_ID SD_ID128_MAKE(13,ac,81,a7,d5,3f,49,78,92,79,5d,0c,29,3a,bc,7e)
 
-static bool stableprivate_address_is_valid(const struct in6_addr *addr) {
-        assert(addr);
-
-        /* According to rfc4291, generated address should not be in the following ranges. */
+static int ndisc_remove_old(Link *link, bool force);
 
-        if (memcmp(addr, &SUBNET_ROUTER_ANYCAST_ADDRESS_RFC4291, SUBNET_ROUTER_ANYCAST_PREFIXLEN) == 0)
-                return false;
+static int ndisc_address_callback(Address *address) {
+        Address *a;
+        Iterator i;
 
-        if (memcmp(addr, &RESERVED_IPV6_INTERFACE_IDENTIFIERS_ADDRESS_RFC4291, RESERVED_IPV6_INTERFACE_IDENTIFIERS_PREFIXLEN) == 0)
-                return false;
+        assert(address);
+        assert(address->link);
 
-        if (memcmp(addr, &RESERVED_SUBNET_ANYCAST_ADDRESSES_RFC4291, RESERVED_SUBNET_ANYCAST_PREFIXLEN) == 0)
-                return false;
+        /* Make this called only once */
+        SET_FOREACH(a, address->link->ndisc_addresses, i)
+                a->callback = NULL;
 
-        return true;
+        return ndisc_remove_old(address->link, true);
 }
 
-static int make_stableprivate_address(Link *link, const struct in6_addr *prefix, uint8_t prefix_len, uint8_t dad_counter, struct in6_addr *addr) {
-        sd_id128_t secret_key;
-        struct siphash state;
-        uint64_t rid;
-        size_t l;
-        int r;
+static int ndisc_remove_old(Link *link, bool force) {
+        Address *address;
+        Route *route;
+        NDiscDNSSL *dnssl;
+        NDiscRDNSS *rdnss;
+        Iterator i;
+        int k, r = 0;
 
-        /* According to rfc7217 section 5.1
-         * RID = F(Prefix, Net_Iface, Network_ID, DAD_Counter, secret_key) */
+        assert(link);
 
-        r = sd_id128_get_machine_app_specific(NDISC_APP_ID, &secret_key);
-        if (r < 0)
-                return log_error_errno(r, "Failed to generate key: %m");
+        if (!force) {
+                bool set_callback = !set_isempty(link->ndisc_addresses);
 
-        siphash24_init(&state, secret_key.bytes);
+                if (!link->ndisc_addresses_configured || !link->ndisc_routes_configured)
+                        return 0;
 
-        l = MAX(DIV_ROUND_UP(prefix_len, 8), 8);
-        siphash24_compress(prefix, l, &state);
-        siphash24_compress(link->ifname, strlen(link->ifname), &state);
-        siphash24_compress(&link->mac, sizeof(struct ether_addr), &state);
-        siphash24_compress(&dad_counter, sizeof(uint8_t), &state);
+                SET_FOREACH(address, link->ndisc_addresses, i)
+                        if (address_is_ready(address)) {
+                                set_callback = false;
+                                break;
+                        }
 
-        rid = htole64(siphash24_finalize(&state));
+                if (set_callback) {
+                        SET_FOREACH(address, link->ndisc_addresses, i)
+                                address->callback = ndisc_address_callback;
+                        return 0;
+                }
+        }
 
-        memcpy(addr->s6_addr, prefix->s6_addr, l);
-        memcpy((uint8_t *) &addr->s6_addr + l, &rid, 16 - l);
+        if (!set_isempty(link->ndisc_addresses_old) || !set_isempty(link->ndisc_routes_old))
+                log_link_debug(link, "Removing old NDisc addresses and routes.");
 
-        return 0;
+        link_dirty(link);
+
+        SET_FOREACH(address, link->ndisc_addresses_old, i) {
+                k = address_remove(address, link, NULL);
+                if (k < 0)
+                        r = k;
+        }
+
+        SET_FOREACH(route, link->ndisc_routes_old, i) {
+                k = route_remove(route, link, NULL);
+                if (k < 0)
+                        r = k;
+        }
+
+        SET_FOREACH(rdnss, link->ndisc_rdnss, i)
+                if (rdnss->marked)
+                        free(set_remove(link->ndisc_rdnss, rdnss));
+
+        SET_FOREACH(dnssl, link->ndisc_dnssl, i)
+                if (dnssl->marked)
+                        free(set_remove(link->ndisc_dnssl, dnssl));
+
+        return r;
 }
 
-static int ndisc_netlink_route_message_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+static int ndisc_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
         int r;
 
         assert(link);
-        assert(link->ndisc_messages > 0);
+        assert(link->ndisc_routes_messages > 0);
 
-        link->ndisc_messages--;
+        link->ndisc_routes_messages--;
 
         if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
                 return 1;
 
         r = sd_netlink_message_get_errno(m);
         if (r < 0 && r != -EEXIST) {
-                log_link_message_error_errno(link, m, r, "Could not set NDisc route or address");
+                log_link_message_error_errno(link, m, r, "Could not set NDisc route");
                 link_enter_failed(link);
                 return 1;
         }
 
-        if (link->ndisc_messages == 0) {
-                link->ndisc_configured = true;
-                r = link_request_set_routes(link);
+        if (link->ndisc_routes_messages == 0) {
+                log_link_debug(link, "NDisc routes set.");
+                link->ndisc_routes_configured = true;
+
+                r = ndisc_remove_old(link, false);
                 if (r < 0) {
                         link_enter_failed(link);
                         return 1;
                 }
+
                 link_check_ready(link);
         }
 
         return 1;
 }
 
-static int ndisc_netlink_address_message_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+static int ndisc_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
         int r;
 
         assert(link);
-        assert(link->ndisc_messages > 0);
+        assert(link->ndisc_addresses_messages > 0);
 
-        link->ndisc_messages--;
+        link->ndisc_addresses_messages--;
 
         if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
                 return 1;
 
         r = sd_netlink_message_get_errno(m);
         if (r < 0 && r != -EEXIST) {
-                log_link_message_error_errno(link, m, r, "Could not set NDisc route or address");
+                log_link_message_error_errno(link, m, r, "Could not set NDisc address");
                 link_enter_failed(link);
                 return 1;
         } else if (r >= 0)
                 (void) manager_rtnl_process_address(rtnl, m, link->manager);
 
-        if (link->ndisc_messages == 0) {
-                link->ndisc_configured = true;
+        if (link->ndisc_addresses_messages == 0) {
+                log_link_debug(link, "NDisc SLAAC addresses set.");
+                link->ndisc_addresses_configured = true;
+
+                r = ndisc_remove_old(link, false);
+                if (r < 0) {
+                        link_enter_failed(link);
+                        return 1;
+                }
+
                 r = link_request_set_routes(link);
                 if (r < 0) {
                         link_enter_failed(link);
                         return 1;
                 }
-                link_check_ready(link);
         }
 
         return 1;
 }
 
+static int ndisc_route_configure(Route *route, Link *link) {
+        Route *ret;
+        int r;
+
+        assert(route);
+        assert(link);
+
+        r = route_configure(route, link, ndisc_route_handler, &ret);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Failed to set NDisc route: %m");
+
+        link->ndisc_routes_messages++;
+
+        r = set_ensure_put(&link->ndisc_routes, &route_hash_ops, ret);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Failed to store NDisc route: %m");
+
+        (void) set_remove(link->ndisc_routes_old, ret);
+
+        return 0;
+}
+
+static int ndisc_address_configure(Address *address, Link *link) {
+        Address *ret;
+        int r;
+
+        assert(address);
+        assert(link);
+
+        r = address_configure(address, link, ndisc_address_handler, true, &ret);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Failed to set NDisc SLAAC address: %m");
+
+        link->ndisc_addresses_messages++;
+
+        r = set_ensure_put(&link->ndisc_addresses, &address_hash_ops, ret);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Failed to store NDisc SLAAC address: %m");
+
+        (void) set_remove(link->ndisc_addresses_old, ret);
+
+        return 0;
+}
+
 static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
         _cleanup_(route_freep) Route *route = NULL;
         union in_addr_union gateway;
@@ -152,84 +233,62 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
         uint32_t mtu;
         usec_t time_now;
         int r;
-        Address *address;
-        Iterator i;
 
         assert(link);
         assert(rt);
 
         r = sd_ndisc_router_get_lifetime(rt, &lifetime);
         if (r < 0)
-                return log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m");
+                return log_link_error_errno(link, r, "Failed to get gateway lifetime from RA: %m");
 
         if (lifetime == 0) /* not a default router */
                 return 0;
 
         r = sd_ndisc_router_get_address(rt, &gateway.in6);
         if (r < 0)
-                return log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m");
+                return log_link_error_errno(link, r, "Failed to get gateway address from RA: %m");
 
-        SET_FOREACH(address, link->addresses, i) {
-                if (address->family != AF_INET6)
-                        continue;
-                if (in_addr_equal(AF_INET6, &gateway, &address->in_addr)) {
+        if (address_exists(link, AF_INET6, &gateway)) {
+                if (DEBUG_LOGGING) {
                         _cleanup_free_ char *buffer = NULL;
 
-                        (void) in_addr_to_string(AF_INET6, &address->in_addr, &buffer);
+                        (void) in_addr_to_string(AF_INET6, &gateway, &buffer);
                         log_link_debug(link, "No NDisc route added, gateway %s matches local address",
                                        strnull(buffer));
-                        return 0;
-                }
-        }
-
-        SET_FOREACH(address, link->addresses_foreign, i) {
-                if (address->family != AF_INET6)
-                        continue;
-                if (in_addr_equal(AF_INET6, &gateway, &address->in_addr)) {
-                        _cleanup_free_ char *buffer = NULL;
-
-                        (void) in_addr_to_string(AF_INET6, &address->in_addr, &buffer);
-                        log_link_debug(link, "No NDisc route added, gateway %s matches local address",
-                                       strnull(buffer));
-                        return 0;
                 }
+                return 0;
         }
 
         r = sd_ndisc_router_get_preference(rt, &preference);
         if (r < 0)
-                return log_link_warning_errno(link, r, "Failed to get default router preference from RA: %m");
+                return log_link_error_errno(link, r, "Failed to get default router preference from RA: %m");
 
         r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
         if (r < 0)
-                return log_link_warning_errno(link, r, "Failed to get RA timestamp: %m");
+                return log_link_error_errno(link, r, "Failed to get RA timestamp: %m");
 
         r = sd_ndisc_router_get_mtu(rt, &mtu);
         if (r == -ENODATA)
                 mtu = 0;
         else if (r < 0)
-                return log_link_warning_errno(link, r, "Failed to get default router MTU from RA: %m");
+                return log_link_error_errno(link, r, "Failed to get default router MTU from RA: %m");
 
         r = route_new(&route);
         if (r < 0)
-                return log_link_error_errno(link, r, "Could not allocate route: %m");
+                return log_oom();
 
         route->family = AF_INET6;
         route->table = link_get_ipv6_accept_ra_route_table(link);
-        route->priority = link->network->dhcp_route_metric;
+        route->priority = link->network->dhcp6_route_metric;
         route->protocol = RTPROT_RA;
         route->pref = preference;
         route->gw = gateway;
         route->lifetime = time_now + lifetime * USEC_PER_SEC;
         route->mtu = mtu;
 
-        r = route_configure(route, link, ndisc_netlink_route_message_handler);
-        if (r < 0) {
-                log_link_warning_errno(link, r, "Could not set default route: %m");
-                link_enter_failed(link);
-                return r;
-        }
-        if (r > 0)
-                link->ndisc_messages++;
+        r = ndisc_route_configure(route, link);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not set default route: %m");
 
         Route *route_gw;
         LIST_FOREACH(routes, route_gw, link->network->static_routes) {
@@ -241,22 +300,74 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
 
                 route_gw->gw = gateway;
 
-                r = route_configure(route_gw, link, ndisc_netlink_route_message_handler);
-                if (r < 0) {
-                        log_link_error_errno(link, r, "Could not set gateway: %m");
-                        link_enter_failed(link);
-                        return r;
-                }
-                if (r > 0)
-                        link->ndisc_messages++;
+                r = ndisc_route_configure(route_gw, link);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not set gateway: %m");
         }
 
         return 0;
 }
 
-static int ndisc_router_generate_addresses(Link *link, unsigned prefixlen, uint32_t lifetime_preferred, Address *address, Set **ret) {
+static bool stableprivate_address_is_valid(const struct in6_addr *addr) {
+        assert(addr);
+
+        /* According to rfc4291, generated address should not be in the following ranges. */
+
+        if (memcmp(addr, &SUBNET_ROUTER_ANYCAST_ADDRESS_RFC4291, SUBNET_ROUTER_ANYCAST_PREFIXLEN) == 0)
+                return false;
+
+        if (memcmp(addr, &RESERVED_IPV6_INTERFACE_IDENTIFIERS_ADDRESS_RFC4291, RESERVED_IPV6_INTERFACE_IDENTIFIERS_PREFIXLEN) == 0)
+                return false;
+
+        if (memcmp(addr, &RESERVED_SUBNET_ANYCAST_ADDRESSES_RFC4291, RESERVED_SUBNET_ANYCAST_PREFIXLEN) == 0)
+                return false;
+
+        return true;
+}
+
+static int make_stableprivate_address(Link *link, const struct in6_addr *prefix, uint8_t prefix_len, uint8_t dad_counter, struct in6_addr **ret) {
+        _cleanup_free_ struct in6_addr *addr = NULL;
+        sd_id128_t secret_key;
+        struct siphash state;
+        uint64_t rid;
+        size_t l;
+        int r;
+
+        /* According to rfc7217 section 5.1
+         * RID = F(Prefix, Net_Iface, Network_ID, DAD_Counter, secret_key) */
+
+        r = sd_id128_get_machine_app_specific(NDISC_APP_ID, &secret_key);
+        if (r < 0)
+                return log_error_errno(r, "Failed to generate key: %m");
+
+        siphash24_init(&state, secret_key.bytes);
+
+        l = MAX(DIV_ROUND_UP(prefix_len, 8), 8);
+        siphash24_compress(prefix, l, &state);
+        siphash24_compress_string(link->ifname, &state);
+        siphash24_compress(&link->mac, sizeof(struct ether_addr), &state);
+        siphash24_compress(&dad_counter, sizeof(uint8_t), &state);
+
+        rid = htole64(siphash24_finalize(&state));
+
+        addr = new(struct in6_addr, 1);
+        if (!addr)
+                return log_oom();
+
+        memcpy(addr->s6_addr, prefix->s6_addr, l);
+        memcpy(addr->s6_addr + l, &rid, 16 - l);
+
+        if (!stableprivate_address_is_valid(addr)) {
+                *ret = NULL;
+                return 0;
+        }
+
+        *ret = TAKE_PTR(addr);
+        return 1;
+}
+
+static int ndisc_router_generate_addresses(Link *link, struct in6_addr *address, uint8_t prefixlen, Set **ret) {
         _cleanup_set_free_free_ Set *addresses = NULL;
-        struct in6_addr addr;
         IPv6Token *j;
         Iterator i;
         int r;
@@ -265,82 +376,63 @@ static int ndisc_router_generate_addresses(Link *link, unsigned prefixlen, uint3
         assert(address);
         assert(ret);
 
-        addresses = set_new(&address_hash_ops);
+        addresses = set_new(&in6_addr_hash_ops);
         if (!addresses)
                 return log_oom();
 
-        addr = address->in_addr.in6;
-        ORDERED_HASHMAP_FOREACH(j, link->network->ipv6_tokens, i) {
-                bool have_address = false;
-                _cleanup_(address_freep) Address *new_address = NULL;
-
-                r = address_new(&new_address);
-                if (r < 0)
-                        return log_oom();
-
-                *new_address = *address;
+        ORDERED_SET_FOREACH(j, link->network->ipv6_tokens, i) {
+                _cleanup_free_ struct in6_addr *new_address = NULL;
 
                 if (j->address_generation_type == IPV6_TOKEN_ADDRESS_GENERATION_PREFIXSTABLE
-                    && memcmp(&j->prefix, &addr, FAMILY_ADDRESS_SIZE(address->family)) == 0) {
+                    && IN6_ARE_ADDR_EQUAL(&j->prefix, address)) {
                         /* While this loop uses dad_counter and a retry limit as specified in RFC 7217, the loop
                            does not actually attempt Duplicate Address Detection; the counter will be incremented
                            only when the address generation algorithm produces an invalid address, and the loop
                            may exit with an address which ends up being unusable due to duplication on the link.
                         */
                         for (; j->dad_counter < DAD_CONFLICTS_IDGEN_RETRIES_RFC7217; j->dad_counter++) {
-                                r = make_stableprivate_address(link, &j->prefix, prefixlen, j->dad_counter, &new_address->in_addr.in6);
+                                r = make_stableprivate_address(link, &j->prefix, prefixlen, j->dad_counter, &new_address);
                                 if (r < 0)
+                                        return r;
+                                if (r > 0)
                                         break;
-
-                                if (stableprivate_address_is_valid(&new_address->in_addr.in6)) {
-                                        have_address = true;
-                                        break;
-                                }
                         }
                 } else if (j->address_generation_type == IPV6_TOKEN_ADDRESS_GENERATION_STATIC) {
-                        memcpy(((uint8_t *)&new_address->in_addr.in6) + 8, ((uint8_t *) &j->prefix) + 8, 8);
-                        have_address = true;
-                }
+                        new_address = new(struct in6_addr, 1);
+                        if (!new_address)
+                                return log_oom();
 
-                if (have_address) {
-                        new_address->prefixlen = prefixlen;
-                        new_address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR;
-                        new_address->cinfo.ifa_prefered = lifetime_preferred;
+                        memcpy(new_address->s6_addr, address->s6_addr, 8);
+                        memcpy(new_address->s6_addr + 8, j->prefix.s6_addr + 8, 8);
+                }
 
+                if (new_address) {
                         r = set_put(addresses, new_address);
                         if (r < 0)
-                                return log_link_warning_errno(link, r, "Failed to store address: %m");
-                        TAKE_PTR(new_address);
+                                return log_link_error_errno(link, r, "Failed to store SLAAC address: %m");
+                        else if (r == 0)
+                                log_link_debug_errno(link, r, "Generated SLAAC address is duplicated, ignoring.");
+                        else
+                                TAKE_PTR(new_address);
                 }
         }
 
         /* fall back to EUI-64 if no tokens provided addresses */
         if (set_isempty(addresses)) {
-                _cleanup_(address_freep) Address *new_address = NULL;
+                _cleanup_free_ struct in6_addr *new_address = NULL;
 
-                r = address_new(&new_address);
-                if (r < 0)
+                new_address = newdup(struct in6_addr, address, 1);
+                if (!new_address)
                         return log_oom();
 
-                *new_address = *address;
-
-                /* see RFC4291 section 2.5.1 */
-                new_address->in_addr.in6.s6_addr[8]  = link->mac.ether_addr_octet[0];
-                new_address->in_addr.in6.s6_addr[8] ^= 1 << 1;
-                new_address->in_addr.in6.s6_addr[9]  = link->mac.ether_addr_octet[1];
-                new_address->in_addr.in6.s6_addr[10] = link->mac.ether_addr_octet[2];
-                new_address->in_addr.in6.s6_addr[11] = 0xff;
-                new_address->in_addr.in6.s6_addr[12] = 0xfe;
-                new_address->in_addr.in6.s6_addr[13] = link->mac.ether_addr_octet[3];
-                new_address->in_addr.in6.s6_addr[14] = link->mac.ether_addr_octet[4];
-                new_address->in_addr.in6.s6_addr[15] = link->mac.ether_addr_octet[5];
-                new_address->prefixlen = prefixlen;
-                new_address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR;
-                new_address->cinfo.ifa_prefered = lifetime_preferred;
+                r = generate_ipv6_eui_64_address(link, new_address);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Failed to generate EUI64 address: %m");
 
                 r = set_put(addresses, new_address);
                 if (r < 0)
-                        return log_link_warning_errno(link, r, "Failed to store address: %m");
+                        return log_link_error_errno(link, r, "Failed to store SLAAC address: %m");
+
                 TAKE_PTR(new_address);
         }
 
@@ -353,9 +445,9 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r
         uint32_t lifetime_valid, lifetime_preferred, lifetime_remaining;
         _cleanup_set_free_free_ Set *addresses = NULL;
         _cleanup_(address_freep) Address *address = NULL;
+        struct in6_addr addr, *a;
         unsigned prefixlen;
         usec_t time_now;
-        Address *existing_address, *a;
         Iterator i;
         int r;
 
@@ -364,7 +456,7 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r
 
         r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
         if (r < 0)
-                return log_link_warning_errno(link, r, "Failed to get RA timestamp: %m");
+                return log_link_error_errno(link, r, "Failed to get RA timestamp: %m");
 
         r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen);
         if (r < 0)
@@ -382,47 +474,49 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r
         if (lifetime_preferred > lifetime_valid)
                 return 0;
 
-        r = address_new(&address);
+        r = sd_ndisc_router_prefix_get_address(rt, &addr);
         if (r < 0)
-                return log_link_error_errno(link, r, "Could not allocate address: %m");
+                return log_link_error_errno(link, r, "Failed to get prefix address: %m");
 
-        address->family = AF_INET6;
-        r = sd_ndisc_router_prefix_get_address(rt, &address->in_addr.in6);
+        r = ndisc_router_generate_addresses(link, &addr, prefixlen, &addresses);
         if (r < 0)
-                return log_link_error_errno(link, r, "Failed to get prefix address: %m");
+                return r;
 
-        r = ndisc_router_generate_addresses(link, prefixlen, lifetime_preferred, address, &addresses);
+        r = address_new(&address);
         if (r < 0)
-                return log_link_error_errno(link, r, "Failed to generate SLAAC addresses: %m");
+                return log_oom();
+
+        address->family = AF_INET6;
+        address->prefixlen = prefixlen;
+        address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR;
+        address->cinfo.ifa_prefered = lifetime_preferred;
 
         SET_FOREACH(a, addresses, i) {
+                Address *existing_address;
+
                 /* see RFC4862 section 5.5.3.e */
-                r = address_get(link, a->family, &a->in_addr, a->prefixlen, &existing_address);
+                r = address_get(link, AF_INET6, (union in_addr_union *) a, prefixlen, &existing_address);
                 if (r > 0) {
                         lifetime_remaining = existing_address->cinfo.tstamp / 100 + existing_address->cinfo.ifa_valid - time_now / USEC_PER_SEC;
                         if (lifetime_valid > NDISC_PREFIX_LFT_MIN || lifetime_valid > lifetime_remaining)
-                                a->cinfo.ifa_valid = lifetime_valid;
+                                address->cinfo.ifa_valid = lifetime_valid;
                         else if (lifetime_remaining <= NDISC_PREFIX_LFT_MIN)
-                                a->cinfo.ifa_valid = lifetime_remaining;
+                                address->cinfo.ifa_valid = lifetime_remaining;
                         else
-                                a->cinfo.ifa_valid = NDISC_PREFIX_LFT_MIN;
+                                address->cinfo.ifa_valid = NDISC_PREFIX_LFT_MIN;
                 } else if (lifetime_valid > 0)
-                        a->cinfo.ifa_valid = lifetime_valid;
+                        address->cinfo.ifa_valid = lifetime_valid;
                 else
-                        return 0; /* see RFC4862 section 5.5.3.d */
+                        continue; /* see RFC4862 section 5.5.3.d */
 
-                if (a->cinfo.ifa_valid == 0)
+                if (address->cinfo.ifa_valid == 0)
                         continue;
 
-                r = address_configure(a, link, ndisc_netlink_address_message_handler, true);
-                if (r < 0) {
-                        log_link_warning_errno(link, r, "Could not set SLAAC address: %m");
-                        link_enter_failed(link);
-                        return r;
-                }
+                address->in_addr.in6 = *a;
 
-                if (r > 0)
-                        link->ndisc_messages++;
+                r = ndisc_address_configure(address, link);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not set SLAAC address: %m");
         }
 
         return 0;
@@ -440,7 +534,7 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) {
 
         r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
         if (r < 0)
-                return log_link_warning_errno(link, r, "Failed to get RA timestamp: %m");
+                return log_link_error_errno(link, r, "Failed to get RA timestamp: %m");
 
         r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen);
         if (r < 0)
@@ -452,11 +546,11 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) {
 
         r = route_new(&route);
         if (r < 0)
-                return log_link_error_errno(link, r, "Could not allocate route: %m");
+                return log_oom();
 
         route->family = AF_INET6;
         route->table = link_get_ipv6_accept_ra_route_table(link);
-        route->priority = link->network->dhcp_route_metric;
+        route->priority = link->network->dhcp6_route_metric;
         route->protocol = RTPROT_RA;
         route->flags = RTM_F_PREFIX;
         route->dst_prefixlen = prefixlen;
@@ -466,14 +560,9 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) {
         if (r < 0)
                 return log_link_error_errno(link, r, "Failed to get prefix address: %m");
 
-        r = route_configure(route, link, ndisc_netlink_route_message_handler);
-        if (r < 0) {
-                log_link_warning_errno(link, r, "Could not set prefix route: %m");
-                link_enter_failed(link);
-                return r;
-        }
-        if (r > 0)
-                link->ndisc_messages++;
+        r = ndisc_route_configure(route, link);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not set prefix route: %m");;
 
         return 0;
 }
@@ -490,33 +579,34 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
 
         r = sd_ndisc_router_route_get_lifetime(rt, &lifetime);
         if (r < 0)
-                return log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m");
+                return log_link_error_errno(link, r, "Failed to get gateway lifetime from RA: %m");
 
         if (lifetime == 0)
                 return 0;
 
         r = sd_ndisc_router_get_address(rt, &gateway);
         if (r < 0)
-                return log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m");
+                return log_link_error_errno(link, r, "Failed to get gateway address from RA: %m");
 
         r = sd_ndisc_router_route_get_prefixlen(rt, &prefixlen);
         if (r < 0)
-                return log_link_warning_errno(link, r, "Failed to get route prefix length: %m");
+                return log_link_error_errno(link, r, "Failed to get route prefix length: %m");
 
         r = sd_ndisc_router_route_get_preference(rt, &preference);
         if (r < 0)
-                return log_link_warning_errno(link, r, "Failed to get default router preference from RA: %m");
+                return log_link_error_errno(link, r, "Failed to get default router preference from RA: %m");
 
         r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
         if (r < 0)
-                return log_link_warning_errno(link, r, "Failed to get RA timestamp: %m");
+                return log_link_error_errno(link, r, "Failed to get RA timestamp: %m");
 
         r = route_new(&route);
         if (r < 0)
-                return log_link_error_errno(link, r, "Could not allocate route: %m");
+                return log_oom();
 
         route->family = AF_INET6;
         route->table = link_get_ipv6_accept_ra_route_table(link);
+        route->priority = link->network->dhcp6_route_metric;
         route->protocol = RTPROT_RA;
         route->pref = preference;
         route->gw.in6 = gateway;
@@ -527,14 +617,9 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
         if (r < 0)
                 return log_link_error_errno(link, r, "Failed to get route address: %m");
 
-        r = route_configure(route, link, ndisc_netlink_route_message_handler);
-        if (r < 0) {
-                log_link_warning_errno(link, r, "Could not set additional route: %m");
-                link_enter_failed(link);
-                return r;
-        }
-        if (r > 0)
-                link->ndisc_messages++;
+        r = ndisc_route_configure(route, link);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not set additional route: %m");
 
         return 0;
 }
@@ -547,188 +632,172 @@ static int ndisc_rdnss_compare_func(const NDiscRDNSS *a, const NDiscRDNSS *b) {
         return memcmp(&a->address, &b->address, sizeof(a->address));
 }
 
-DEFINE_PRIVATE_HASH_OPS(ndisc_rdnss_hash_ops, NDiscRDNSS, ndisc_rdnss_hash_func, ndisc_rdnss_compare_func);
+DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
+                ndisc_rdnss_hash_ops,
+                NDiscRDNSS,
+                ndisc_rdnss_hash_func,
+                ndisc_rdnss_compare_func,
+                free);
 
 static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) {
         uint32_t lifetime;
         const struct in6_addr *a;
+        NDiscRDNSS *rdnss;
         usec_t time_now;
-        int i, n, r;
+        Iterator i;
+        int n, r;
 
         assert(link);
         assert(rt);
 
         r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
         if (r < 0)
-                return log_link_warning_errno(link, r, "Failed to get RA timestamp: %m");
+                return log_link_error_errno(link, r, "Failed to get RA timestamp: %m");
 
         r = sd_ndisc_router_rdnss_get_lifetime(rt, &lifetime);
         if (r < 0)
-                return log_link_warning_errno(link, r, "Failed to get RDNSS lifetime: %m");
+                return log_link_error_errno(link, r, "Failed to get RDNSS lifetime: %m");
 
         n = sd_ndisc_router_rdnss_get_addresses(rt, &a);
         if (n < 0)
-                return log_link_warning_errno(link, n, "Failed to get RDNSS addresses: %m");
+                return log_link_error_errno(link, n, "Failed to get RDNSS addresses: %m");
 
-        for (i = 0; i < n; i++) {
-                _cleanup_free_ NDiscRDNSS *x = NULL;
-                NDiscRDNSS d = {
-                        .address = a[i],
-                }, *y;
+        SET_FOREACH(rdnss, link->ndisc_rdnss, i)
+                rdnss->marked = true;
 
-                if (lifetime == 0) {
-                        (void) set_remove(link->ndisc_rdnss, &d);
-                        link_dirty(link);
-                        continue;
-                }
+        if (lifetime == 0)
+                return 0;
 
-                y = set_get(link->ndisc_rdnss, &d);
-                if (y) {
-                        y->valid_until = time_now + lifetime * USEC_PER_SEC;
-                        continue;
-                }
+        if (n >= (int) NDISC_RDNSS_MAX) {
+                log_link_warning(link, "Too many RDNSS records per link. Only first %i records will be used.", NDISC_RDNSS_MAX);
+                n = NDISC_RDNSS_MAX;
+        }
 
-                ndisc_vacuum(link);
+        for (int j = 0; j < n; j++) {
+                _cleanup_free_ NDiscRDNSS *x = NULL;
+                NDiscRDNSS d = {
+                        .address = a[j],
+                };
 
-                if (set_size(link->ndisc_rdnss) >= NDISC_RDNSS_MAX) {
-                        log_link_warning(link, "Too many RDNSS records per link, ignoring.");
+                rdnss = set_get(link->ndisc_rdnss, &d);
+                if (rdnss) {
+                        rdnss->marked = false;
+                        rdnss->valid_until = time_now + lifetime * USEC_PER_SEC;
                         continue;
                 }
 
-                r = set_ensure_allocated(&link->ndisc_rdnss, &ndisc_rdnss_hash_ops);
-                if (r < 0)
-                        return log_oom();
-
                 x = new(NDiscRDNSS, 1);
                 if (!x)
                         return log_oom();
 
                 *x = (NDiscRDNSS) {
-                        .address = a[i],
+                        .address = a[j],
                         .valid_until = time_now + lifetime * USEC_PER_SEC,
                 };
 
-                r = set_put(link->ndisc_rdnss, x);
+                r = set_ensure_consume(&link->ndisc_rdnss, &ndisc_rdnss_hash_ops, TAKE_PTR(x));
                 if (r < 0)
                         return log_oom();
-
-                TAKE_PTR(x);
-
                 assert(r > 0);
-                link_dirty(link);
         }
 
         return 0;
 }
 
 static void ndisc_dnssl_hash_func(const NDiscDNSSL *x, struct siphash *state) {
-        siphash24_compress(NDISC_DNSSL_DOMAIN(x), strlen(NDISC_DNSSL_DOMAIN(x)), state);
+        siphash24_compress_string(NDISC_DNSSL_DOMAIN(x), state);
 }
 
 static int ndisc_dnssl_compare_func(const NDiscDNSSL *a, const NDiscDNSSL *b) {
         return strcmp(NDISC_DNSSL_DOMAIN(a), NDISC_DNSSL_DOMAIN(b));
 }
 
-DEFINE_PRIVATE_HASH_OPS(ndisc_dnssl_hash_ops, NDiscDNSSL, ndisc_dnssl_hash_func, ndisc_dnssl_compare_func);
+DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
+                ndisc_dnssl_hash_ops,
+                NDiscDNSSL,
+                ndisc_dnssl_hash_func,
+                ndisc_dnssl_compare_func,
+                free);
 
-static void ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) {
+static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) {
         _cleanup_strv_free_ char **l = NULL;
         uint32_t lifetime;
         usec_t time_now;
-        char **i;
+        NDiscDNSSL *dnssl;
+        Iterator i;
+        char **j;
         int r;
 
         assert(link);
         assert(rt);
 
         r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
-        if (r < 0) {
-                log_link_warning_errno(link, r, "Failed to get RA timestamp: %m");
-                return;
-        }
+        if (r < 0)
+                return log_link_error_errno(link, r, "Failed to get RA timestamp: %m");
 
         r = sd_ndisc_router_dnssl_get_lifetime(rt, &lifetime);
-        if (r < 0) {
-                log_link_warning_errno(link, r, "Failed to get RDNSS lifetime: %m");
-                return;
-        }
+        if (r < 0)
+                return log_link_error_errno(link, r, "Failed to get DNSSL lifetime: %m");
 
         r = sd_ndisc_router_dnssl_get_domains(rt, &l);
-        if (r < 0) {
-                log_link_warning_errno(link, r, "Failed to get RDNSS addresses: %m");
-                return;
-        }
+        if (r < 0)
+                return log_link_error_errno(link, r, "Failed to get DNSSL addresses: %m");
 
-        STRV_FOREACH(i, l) {
-                _cleanup_free_ NDiscDNSSL *s;
-                NDiscDNSSL *x;
+        SET_FOREACH(dnssl, link->ndisc_dnssl, i)
+                dnssl->marked = true;
 
-                s = malloc0(ALIGN(sizeof(NDiscDNSSL)) + strlen(*i) + 1);
-                if (!s) {
-                        log_oom();
-                        return;
-                }
+        if (lifetime == 0)
+                return 0;
 
-                strcpy(NDISC_DNSSL_DOMAIN(s), *i);
+        if (strv_length(l) >= NDISC_DNSSL_MAX) {
+                log_link_warning(link, "Too many DNSSL records per link. Only first %i records will be used.", NDISC_DNSSL_MAX);
+                STRV_FOREACH(j, l + NDISC_DNSSL_MAX)
+                        *j = mfree(*j);
+        }
 
-                if (lifetime == 0) {
-                        (void) set_remove(link->ndisc_dnssl, s);
-                        link_dirty(link);
-                        continue;
-                }
+        STRV_FOREACH(j, l) {
+                _cleanup_free_ NDiscDNSSL *s = NULL;
 
-                x = set_get(link->ndisc_dnssl, s);
-                if (x) {
-                        x->valid_until = time_now + lifetime * USEC_PER_SEC;
-                        continue;
-                }
+                s = malloc0(ALIGN(sizeof(NDiscDNSSL)) + strlen(*j) + 1);
+                if (!s)
+                        return log_oom();
 
-                ndisc_vacuum(link);
+                strcpy(NDISC_DNSSL_DOMAIN(s), *j);
 
-                if (set_size(link->ndisc_dnssl) >= NDISC_DNSSL_MAX) {
-                        log_link_warning(link, "Too many DNSSL records per link, ignoring.");
+                dnssl = set_get(link->ndisc_dnssl, s);
+                if (dnssl) {
+                        dnssl->marked = false;
+                        dnssl->valid_until = time_now + lifetime * USEC_PER_SEC;
                         continue;
                 }
 
-                r = set_ensure_allocated(&link->ndisc_dnssl, &ndisc_dnssl_hash_ops);
-                if (r < 0) {
-                        log_oom();
-                        return;
-                }
-
                 s->valid_until = time_now + lifetime * USEC_PER_SEC;
 
-                r = set_put(link->ndisc_dnssl, s);
-                if (r < 0) {
-                        log_oom();
-                        return;
-                }
-
-                s = NULL;
+                r = set_ensure_consume(&link->ndisc_dnssl, &ndisc_dnssl_hash_ops, TAKE_PTR(s));
+                if (r < 0)
+                        return log_oom();
                 assert(r > 0);
-                link_dirty(link);
         }
+
+        return 0;
 }
 
 static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) {
-        int r;
-
         assert(link);
         assert(link->network);
         assert(rt);
 
-        r = sd_ndisc_router_option_rewind(rt);
-        for (;;) {
+        for (int r = sd_ndisc_router_option_rewind(rt); ; r = sd_ndisc_router_option_next(rt)) {
                 uint8_t type;
 
                 if (r < 0)
-                        return log_link_warning_errno(link, r, "Failed to iterate through options: %m");
+                        return log_link_error_errno(link, r, "Failed to iterate through options: %m");
                 if (r == 0) /* EOF */
-                        break;
+                        return 0;
 
                 r = sd_ndisc_router_option_get_type(rt, &type);
                 if (r < 0)
-                        return log_link_warning_errno(link, r, "Failed to get RA option type: %m");
+                        return log_link_error_errno(link, r, "Failed to get RA option type: %m");
 
                 switch (type) {
 
@@ -740,54 +809,64 @@ static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) {
                         if (r < 0)
                                 return log_link_error_errno(link, r, "Failed to get prefix address: %m");
 
-                        if (set_contains(link->network->ndisc_black_listed_prefix, &a.in6)) {
+                        if (set_contains(link->network->ndisc_deny_listed_prefix, &a.in6)) {
                                 if (DEBUG_LOGGING) {
                                         _cleanup_free_ char *b = NULL;
 
                                         (void) in_addr_to_string(AF_INET6, &a, &b);
-                                        log_link_debug(link, "Prefix '%s' is black listed, ignoring", strna(b));
+                                        log_link_debug(link, "Prefix '%s' is deny-listed, ignoring", strna(b));
                                 }
-
                                 break;
                         }
 
                         r = sd_ndisc_router_prefix_get_flags(rt, &flags);
                         if (r < 0)
-                                return log_link_warning_errno(link, r, "Failed to get RA prefix flags: %m");
+                                return log_link_error_errno(link, r, "Failed to get RA prefix flags: %m");
 
                         if (link->network->ipv6_accept_ra_use_onlink_prefix &&
-                            FLAGS_SET(flags, ND_OPT_PI_FLAG_ONLINK))
-                                (void) ndisc_router_process_onlink_prefix(link, rt);
+                            FLAGS_SET(flags, ND_OPT_PI_FLAG_ONLINK)) {
+                                r = ndisc_router_process_onlink_prefix(link, rt);
+                                if (r < 0)
+                                        return r;
+                        }
 
                         if (link->network->ipv6_accept_ra_use_autonomous_prefix &&
-                            FLAGS_SET(flags, ND_OPT_PI_FLAG_AUTO))
-                                (void) ndisc_router_process_autonomous_prefix(link, rt);
-
+                            FLAGS_SET(flags, ND_OPT_PI_FLAG_AUTO)) {
+                                r = ndisc_router_process_autonomous_prefix(link, rt);
+                                if (r < 0)
+                                        return r;
+                        }
                         break;
                 }
 
                 case SD_NDISC_OPTION_ROUTE_INFORMATION:
-                        (void) ndisc_router_process_route(link, rt);
+                        r = ndisc_router_process_route(link, rt);
+                        if (r < 0)
+                                return r;
                         break;
 
                 case SD_NDISC_OPTION_RDNSS:
-                        if (link->network->ipv6_accept_ra_use_dns)
-                                (void) ndisc_router_process_rdnss(link, rt);
+                        if (link->network->ipv6_accept_ra_use_dns) {
+                                r = ndisc_router_process_rdnss(link, rt);
+                                if (r < 0)
+                                        return r;
+                        }
                         break;
 
                 case SD_NDISC_OPTION_DNSSL:
-                        if (link->network->ipv6_accept_ra_use_dns)
-                                (void) ndisc_router_process_dnssl(link, rt);
+                        if (link->network->ipv6_accept_ra_use_dns) {
+                                r = ndisc_router_process_dnssl(link, rt);
+                                if (r < 0)
+                                        return r;
+                        }
                         break;
                 }
-
-                r = sd_ndisc_router_option_next(rt);
         }
-
-        return 0;
 }
 
 static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
+        Address *address;
+        Route *route;
         uint64_t flags;
         int r;
 
@@ -796,29 +875,79 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
         assert(link->manager);
         assert(rt);
 
+        link->ndisc_addresses_configured = false;
+        link->ndisc_routes_configured = false;
+
+        link_dirty(link);
+
+        while ((address = set_steal_first(link->ndisc_addresses))) {
+                r = set_ensure_put(&link->ndisc_addresses_old, &address_hash_ops, address);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Failed to store old NDisc SLAAC address: %m");
+        }
+
+        while ((route = set_steal_first(link->ndisc_routes))) {
+                r = set_ensure_put(&link->ndisc_routes_old, &route_hash_ops, route);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Failed to store old NDisc route: %m");
+        }
+
         r = sd_ndisc_router_get_flags(rt, &flags);
         if (r < 0)
-                return log_link_warning_errno(link, r, "Failed to get RA flags: %m");
+                return log_link_error_errno(link, r, "Failed to get RA flags: %m");
 
-        if (flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER)) {
-                /* (re)start DHCPv6 client in stateful or stateless mode according to RA flags */
-                r = dhcp6_request_address(link, !(flags & ND_RA_FLAG_MANAGED));
+        if ((flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER) && link->network->ipv6_accept_ra_start_dhcp6_client)) {
+
+                if (link->network->ipv6_accept_ra_start_dhcp6_client == IPV6_ACCEPT_RA_START_DHCP6_CLIENT_ALWAYS)
+                        r = dhcp6_request_address(link, false);
+                else
+                        /* (re)start DHCPv6 client in stateful or stateless mode according to RA flags */
+                        r = dhcp6_request_address(link, !(flags & ND_RA_FLAG_MANAGED));
                 if (r < 0 && r != -EBUSY)
-                        log_link_warning_errno(link, r, "Could not acquire DHCPv6 lease on NDisc request: %m");
-                else {
+                        return log_link_error_errno(link, r, "Could not acquire DHCPv6 lease on NDisc request: %m");
+                else
                         log_link_debug(link, "Acquiring DHCPv6 lease on NDisc request");
-                        r = 0;
-                }
         }
 
-        (void) ndisc_router_process_default(link, rt);
-        (void) ndisc_router_process_options(link, rt);
+        r = ndisc_router_process_default(link, rt);
+        if (r < 0)
+                return r;
+        r = ndisc_router_process_options(link, rt);
+        if (r < 0)
+                return r;
 
-        return r;
+        if (link->ndisc_addresses_messages == 0)
+                link->ndisc_addresses_configured = true;
+        else {
+                log_link_debug(link, "Setting SLAAC addresses.");
+
+                /* address_handler calls link_request_set_routes() and link_request_set_nexthop().
+                 * Before they are called, the related flags must be cleared. Otherwise, the link
+                 * becomes configured state before routes are configured. */
+                link->static_routes_configured = false;
+                link->static_nexthops_configured = false;
+        }
+
+        if (link->ndisc_routes_messages == 0)
+                link->ndisc_routes_configured = true;
+        else
+                log_link_debug(link, "Setting NDisc routes.");
+
+        r = ndisc_remove_old(link, false);
+        if (r < 0)
+                return r;
+
+        if (link->ndisc_addresses_configured && link->ndisc_routes_configured)
+                link_check_ready(link);
+        else
+                link_set_state(link, LINK_STATE_CONFIGURING);
+
+        return 0;
 }
 
 static void ndisc_handler(sd_ndisc *nd, sd_ndisc_event event, sd_ndisc_router *rt, void *userdata) {
         Link *link = userdata;
+        int r;
 
         assert(link);
 
@@ -828,16 +957,23 @@ static void ndisc_handler(sd_ndisc *nd, sd_ndisc_event event, sd_ndisc_router *r
         switch (event) {
 
         case SD_NDISC_EVENT_ROUTER:
-                (void) ndisc_router_handler(link, rt);
+                r = ndisc_router_handler(link, rt);
+                if (r < 0) {
+                        link_enter_failed(link);
+                        return;
+                }
                 break;
 
         case SD_NDISC_EVENT_TIMEOUT:
-                link->ndisc_configured = true;
-                link_check_ready(link);
-
+                log_link_debug(link, "NDisc handler get timeout event");
+                if (link->ndisc_addresses_messages == 0 && link->ndisc_routes_messages == 0) {
+                        link->ndisc_addresses_configured = true;
+                        link->ndisc_routes_configured = true;
+                        link_check_ready(link);
+                }
                 break;
         default:
-                log_link_warning(link, "IPv6 Neighbor Discovery unknown event: %d", event);
+                assert_not_reached("Unknown NDisc event");
         }
 }
 
@@ -874,6 +1010,7 @@ void ndisc_vacuum(Link *link) {
         NDiscDNSSL *d;
         Iterator i;
         usec_t time_now;
+        bool updated = false;
 
         assert(link);
 
@@ -884,14 +1021,17 @@ void ndisc_vacuum(Link *link) {
         SET_FOREACH(r, link->ndisc_rdnss, i)
                 if (r->valid_until < time_now) {
                         free(set_remove(link->ndisc_rdnss, r));
-                        link_dirty(link);
+                        updated = true;
                 }
 
         SET_FOREACH(d, link->ndisc_dnssl, i)
                 if (d->valid_until < time_now) {
                         free(set_remove(link->ndisc_dnssl, d));
-                        link_dirty(link);
+                        updated = true;
                 }
+
+        if (updated)
+                link_dirty(link);
 }
 
 void ndisc_flush(Link *link) {
@@ -899,8 +1039,8 @@ void ndisc_flush(Link *link) {
 
         /* Removes all RDNSS and DNSSL entries, without exception */
 
-        link->ndisc_rdnss = set_free_free(link->ndisc_rdnss);
-        link->ndisc_dnssl = set_free_free(link->ndisc_dnssl);
+        link->ndisc_rdnss = set_free(link->ndisc_rdnss);
+        link->ndisc_dnssl = set_free(link->ndisc_dnssl);
 }
 
 int ipv6token_new(IPv6Token **ret) {
@@ -919,15 +1059,29 @@ int ipv6token_new(IPv6Token **ret) {
         return 0;
 }
 
-DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+static void ipv6_token_hash_func(const IPv6Token *p, struct siphash *state) {
+        siphash24_compress(&p->address_generation_type, sizeof(p->address_generation_type), state);
+        siphash24_compress(&p->prefix, sizeof(p->prefix), state);
+}
+
+static int ipv6_token_compare_func(const IPv6Token *a, const IPv6Token *b) {
+        int r;
+
+        r = CMP(a->address_generation_type, b->address_generation_type);
+        if (r != 0)
+                return r;
+
+        return memcmp(&a->prefix, &b->prefix, sizeof(struct in6_addr));
+}
+
+DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
                 ipv6_token_hash_ops,
-                void,
-                trivial_hash_func,
-                trivial_compare_func,
                 IPv6Token,
+                ipv6_token_hash_func,
+                ipv6_token_compare_func,
                 free);
 
-int config_parse_ndisc_black_listed_prefix(
+int config_parse_ndisc_deny_listed_prefix(
                 const char *unit,
                 const char *filename,
                 unsigned line,
@@ -949,7 +1103,7 @@ int config_parse_ndisc_black_listed_prefix(
         assert(data);
 
         if (isempty(rvalue)) {
-                network->ndisc_black_listed_prefix = set_free_free(network->ndisc_black_listed_prefix);
+                network->ndisc_deny_listed_prefix = set_free_free(network->ndisc_deny_listed_prefix);
                 return 0;
         }
 
@@ -959,9 +1113,11 @@ int config_parse_ndisc_black_listed_prefix(
                 union in_addr_union ip;
 
                 r = extract_first_word(&p, &n, NULL, 0);
+                if (r == -ENOMEM)
+                        return log_oom();
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
-                                   "Failed to parse NDISC black listed prefix, ignoring assignment: %s",
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to parse NDisc deny-listed prefix, ignoring assignment: %s",
                                    rvalue);
                         return 0;
                 }
@@ -970,33 +1126,22 @@ int config_parse_ndisc_black_listed_prefix(
 
                 r = in_addr_from_string(AF_INET6, n, &ip);
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
-                                   "NDISC black listed prefix is invalid, ignoring assignment: %s", n);
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "NDisc deny-listed prefix is invalid, ignoring assignment: %s", n);
                         continue;
                 }
 
-                if (set_contains(network->ndisc_black_listed_prefix, &ip.in6))
+                if (set_contains(network->ndisc_deny_listed_prefix, &ip.in6))
                         continue;
 
-                r = set_ensure_allocated(&network->ndisc_black_listed_prefix, &in6_addr_hash_ops);
-                if (r < 0)
-                        return log_oom();
-
                 a = newdup(struct in6_addr, &ip.in6, 1);
                 if (!a)
                         return log_oom();
 
-                r = set_put(network->ndisc_black_listed_prefix, a);
-                if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
-                                   "Failed to store NDISC black listed prefix '%s', ignoring assignment: %m", n);
-                        continue;
-                }
-
-                TAKE_PTR(a);
+                r = set_ensure_consume(&network->ndisc_deny_listed_prefix, &in6_addr_hash_ops, TAKE_PTR(a));
+                if (r < 0)
+                        return log_oom();
         }
-
-        return 0;
 }
 
 int config_parse_address_generation_type(
@@ -1023,7 +1168,7 @@ int config_parse_address_generation_type(
         assert(data);
 
         if (isempty(rvalue)) {
-                network->ipv6_tokens = ordered_hashmap_free(network->ipv6_tokens);
+                network->ipv6_tokens = ordered_set_free(network->ipv6_tokens);
                 return 0;
         }
 
@@ -1042,31 +1187,42 @@ int config_parse_address_generation_type(
 
         r = in_addr_from_string(AF_INET6, p, &buffer);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to parse IPv6 %s, ignoring: %s", lvalue, rvalue);
                 return 0;
         }
 
         if (in_addr_is_null(AF_INET6, &buffer)) {
-                log_syntax(unit, LOG_ERR, filename, line, 0,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "IPv6 %s cannot be the ANY address, ignoring: %s", lvalue, rvalue);
                 return 0;
         }
 
         token->prefix = buffer.in6;
 
-        r = ordered_hashmap_ensure_allocated(&network->ipv6_tokens, &ipv6_token_hash_ops);
+        r = ordered_set_ensure_allocated(&network->ipv6_tokens, &ipv6_token_hash_ops);
         if (r < 0)
                 return log_oom();
 
-        r = ordered_hashmap_put(network->ipv6_tokens, &token->prefix, token);
-        if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
-                           "Failed to store IPv6 token '%s'", rvalue);
-                return 0;
-        }
-
-        TAKE_PTR(token);
+        r = ordered_set_put(network->ipv6_tokens, token);
+        if (r == -EEXIST)
+                log_syntax(unit, LOG_DEBUG, filename, line, r,
+                           "IPv6 token '%s' is duplicated, ignoring: %m", rvalue);
+        else if (r < 0)
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to store IPv6 token '%s', ignoring: %m", rvalue);
+        else
+                TAKE_PTR(token);
 
         return 0;
 }
+
+DEFINE_CONFIG_PARSE_ENUM(config_parse_ipv6_accept_ra_start_dhcp6_client, ipv6_accept_ra_start_dhcp6_client, IPv6AcceptRAStartDHCP6Client,
+                         "Failed to parse DHCPv6Client= setting")
+static const char* const ipv6_accept_ra_start_dhcp6_client_table[_IPV6_ACCEPT_RA_START_DHCP6_CLIENT_MAX] = {
+        [IPV6_ACCEPT_RA_START_DHCP6_CLIENT_NO]     = "no",
+        [IPV6_ACCEPT_RA_START_DHCP6_CLIENT_ALWAYS] = "always",
+        [IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES]    = "yes",
+};
+
+DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(ipv6_accept_ra_start_dhcp6_client, IPv6AcceptRAStartDHCP6Client, IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES);
index 02c2f8afda5432a3f96c0ffed94b5a2bcfaaa7b8..c459f42456f9251e0a3e7e5d56fac835ac6b612b 100644 (file)
@@ -15,12 +15,24 @@ typedef enum IPv6TokenAddressGeneration {
         _IPV6_TOKEN_ADDRESS_GENERATION_INVALID = -1,
 } IPv6TokenAddressGeneration;
 
+typedef enum IPv6AcceptRAStartDHCP6Client {
+        IPV6_ACCEPT_RA_START_DHCP6_CLIENT_NO,
+        IPV6_ACCEPT_RA_START_DHCP6_CLIENT_ALWAYS,
+        IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES,
+        _IPV6_ACCEPT_RA_START_DHCP6_CLIENT_MAX,
+        _IPV6_ACCEPT_RA_START_DHCP6_CLIENT_INVALID = -1,
+} IPv6AcceptRAStartDHCP6Client;
+
 typedef struct NDiscRDNSS {
+        /* Used when GC'ing old DNS servers when configuration changes. */
+        bool marked;
         usec_t valid_until;
         struct in6_addr address;
 } NDiscRDNSS;
 
 typedef struct NDiscDNSSL {
+        /* Used when GC'ing old domains when configuration changes. */
+        bool marked;
         usec_t valid_until;
         /* The domain name follows immediately. */
 } NDiscDNSSL;
@@ -43,5 +55,9 @@ int ndisc_configure(Link *link);
 void ndisc_vacuum(Link *link);
 void ndisc_flush(Link *link);
 
-CONFIG_PARSER_PROTOTYPE(config_parse_ndisc_black_listed_prefix);
+CONFIG_PARSER_PROTOTYPE(config_parse_ndisc_deny_listed_prefix);
 CONFIG_PARSER_PROTOTYPE(config_parse_address_generation_type);
+CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_accept_ra_start_dhcp6_client);
+
+const char* ipv6_accept_ra_start_dhcp6_client_to_string(IPv6AcceptRAStartDHCP6Client i) _const_;
+IPv6AcceptRAStartDHCP6Client ipv6_accept_ra_start_dhcp6_client_from_string(const char *s) _pure_;
index fd6219fccea59c5dcefdfd5408a03786e2d486d4..09ddb9c8a56b809db09ba702d404efc5cac8cfed 100644 (file)
@@ -132,7 +132,7 @@ int neighbor_configure(Neighbor *neighbor, Link *link, link_netlink_message_hand
         if (r < 0)
                 return log_link_error_errno(link, r, "Could not set state: %m");
 
-        r = sd_netlink_message_set_flags(req, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE);
+        r = sd_netlink_message_set_flags(req, NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_REPLACE);
         if (r < 0)
                 return log_link_error_errno(link, r, "Could not set flags: %m");
 
@@ -247,7 +247,7 @@ static int neighbor_compare_func(const Neighbor *a, const Neighbor *b) {
         return memcmp(&a->lladdr, &b->lladdr, a->lladdr_size);
 }
 
-DEFINE_PRIVATE_HASH_OPS(neighbor_hash_ops, Neighbor, neighbor_hash_func, neighbor_compare_func);
+DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(neighbor_hash_ops, Neighbor, neighbor_hash_func, neighbor_compare_func, neighbor_free);
 
 int neighbor_get(Link *link, int family, const union in_addr_union *addr, const union lladdr_union *lladdr, size_t lladdr_size, Neighbor **ret) {
         Neighbor neighbor, *existing;
@@ -300,11 +300,7 @@ static int neighbor_add_internal(Link *link, Set **neighbors, int family, const
                 .lladdr_size = lladdr_size,
         };
 
-        r = set_ensure_allocated(neighbors, &neighbor_hash_ops);
-        if (r < 0)
-                return r;
-
-        r = set_put(*neighbors, neighbor);
+        r = set_ensure_put(neighbors, &neighbor_hash_ops, neighbor);
         if (r < 0)
                 return r;
         if (r == 0)
@@ -314,8 +310,7 @@ static int neighbor_add_internal(Link *link, Set **neighbors, int family, const
 
         if (ret)
                 *ret = neighbor;
-
-        neighbor = NULL;
+        TAKE_PTR(neighbor);
 
         return 0;
 }
@@ -332,11 +327,7 @@ int neighbor_add(Link *link, int family, const union in_addr_union *addr, const
                         return r;
         } else if (r == 0) {
                 /* Neighbor is foreign, claim it as recognized */
-                r = set_ensure_allocated(&link->neighbors, &neighbor_hash_ops);
-                if (r < 0)
-                        return r;
-
-                r = set_put(link->neighbors, neighbor);
+                r = set_ensure_put(&link->neighbors, &neighbor_hash_ops, neighbor);
                 if (r < 0)
                         return r;
 
@@ -408,11 +399,12 @@ int config_parse_neighbor_address(
 
         r = neighbor_new_static(network, filename, section_line, &n);
         if (r < 0)
-                return r;
+                return log_oom();
 
         r = in_addr_from_string_auto(rvalue, &n->family, &n->in_addr);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "Neighbor Address is invalid, ignoring assignment: %s", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Neighbor Address is invalid, ignoring assignment: %s", rvalue);
                 return 0;
         }
 
@@ -445,7 +437,7 @@ int config_parse_neighbor_lladdr(
 
         r = neighbor_new_static(network, filename, section_line, &n);
         if (r < 0)
-                return r;
+                return log_oom();
 
         r = ether_addr_from_string(rvalue, &n->lladdr.mac);
         if (r >= 0)
@@ -453,7 +445,7 @@ int config_parse_neighbor_lladdr(
         else {
                 r = in_addr_from_string_auto(rvalue, &family, &n->lladdr.ip);
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
                                    "Neighbor LinkLayerAddress= is invalid, ignoring assignment: %s",
                                    rvalue);
                         return 0;
@@ -490,11 +482,11 @@ int config_parse_neighbor_hwaddr(
 
         r = neighbor_new_static(network, filename, section_line, &n);
         if (r < 0)
-                return r;
+                return log_oom();
 
         r = ether_addr_from_string(rvalue, &n->lladdr.mac);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Neighbor MACAddress= is invalid, ignoring assignment: %s", rvalue);
                 return 0;
         }
index 920ac41b43bbcf5f9287283b94e28271e0950d40..3f1652b1904ffa622973dd91f29a97701d347880 100644 (file)
@@ -6,14 +6,18 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"")
 #include "conf-parser.h"
 #include "netem.h"
 #include "network-internal.h"
+#include "networkd-can.h"
 #include "networkd-conf.h"
 #include "networkd-dhcp-common.h"
 #include "networkd-dhcp-server.h"
 #include "networkd-dhcp4.h"
+#include "networkd-dhcp6.h"
 #include "networkd-ipv4ll.h"
 #include "networkd-ndisc.h"
 #include "networkd-network.h"
+#include "networkd-sriov.h"
 #include "qdisc.h"
+#include "tclass.h"
 #include "vlan-util.h"
 %}
 struct ConfigPerfItem;
@@ -44,11 +48,21 @@ Match.KernelVersion,                         config_parse_net_condition,
 Match.Architecture,                          config_parse_net_condition,                               CONDITION_ARCHITECTURE,        offsetof(Network, conditions)
 Link.MACAddress,                             config_parse_hwaddr,                                      0,                             offsetof(Network, mac)
 Link.MTUBytes,                               config_parse_mtu,                                         AF_UNSPEC,                     offsetof(Network, mtu)
+Link.Group,                                  config_parse_uint32,                                      0,                             offsetof(Network, group)
 Link.ARP,                                    config_parse_tristate,                                    0,                             offsetof(Network, arp)
 Link.Multicast,                              config_parse_tristate,                                    0,                             offsetof(Network, multicast)
 Link.AllMulticast,                           config_parse_tristate,                                    0,                             offsetof(Network, allmulticast)
 Link.Unmanaged,                              config_parse_bool,                                        0,                             offsetof(Network, unmanaged)
 Link.RequiredForOnline,                      config_parse_required_for_online,                         0,                             0
+SR-IOV.VirtualFunction,                      config_parse_sr_iov_uint32,                               0,                             0
+SR-IOV.VLANId,                               config_parse_sr_iov_uint32,                               0,                             0
+SR-IOV.QualityOfService,                     config_parse_sr_iov_uint32,                               0,                             0
+SR-IOV.VLANProtocol,                         config_parse_sr_iov_vlan_proto,                           0,                             0
+SR-IOV.MACSpoofCheck,                        config_parse_sr_iov_boolean,                              0,                             0
+SR-IOV.QueryReceiveSideScaling,              config_parse_sr_iov_boolean,                              0,                             0
+SR-IOV.Trust,                                config_parse_sr_iov_boolean,                              0,                             0
+SR-IOV.LinkState,                            config_parse_sr_iov_link_state,                           0,                             0
+SR-IOV.MACAddress,                           config_parse_sr_iov_mac,                                  0,                             0
 Network.Description,                         config_parse_string,                                      0,                             offsetof(Network, description)
 Network.Bridge,                              config_parse_ifname,                                      0,                             offsetof(Network, bridge_name)
 Network.Bond,                                config_parse_ifname,                                      0,                             offsetof(Network, bond_name)
@@ -66,6 +80,7 @@ Network.VRF,                                 config_parse_ifname,
 Network.DHCP,                                config_parse_dhcp,                                        0,                             offsetof(Network, dhcp)
 Network.DHCPServer,                          config_parse_bool,                                        0,                             offsetof(Network, dhcp_server)
 Network.LinkLocalAddressing,                 config_parse_link_local_address_family,                   0,                             offsetof(Network, link_local)
+Network.IPv6LinkLocalAddressGenerationMode,  config_parse_ipv6_link_local_address_gen_mode,            0,                             offsetof(Network, ipv6ll_address_gen_mode)
 Network.IPv4LLRoute,                         config_parse_bool,                                        0,                             offsetof(Network, ipv4ll_route)
 Network.DefaultRouteOnDevice,                config_parse_bool,                                        0,                             offsetof(Network, default_route_on_device)
 Network.IPv6Token,                           config_parse_address_generation_type,                     0,                             0
@@ -91,6 +106,7 @@ Network.IPv6DuplicateAddressDetection,       config_parse_int,
 Network.IPv6HopLimit,                        config_parse_int,                                         0,                             offsetof(Network, ipv6_hop_limit)
 Network.IPv6ProxyNDP,                        config_parse_tristate,                                    0,                             offsetof(Network, ipv6_proxy_ndp)
 Network.IPv6MTUBytes,                        config_parse_mtu,                                         AF_INET6,                      offsetof(Network, ipv6_mtu)
+Network.IPv4AcceptLocal,                     config_parse_tristate,                                    0,                             offsetof(Network, ipv4_accept_local)
 Network.ActiveSlave,                         config_parse_bool,                                        0,                             offsetof(Network, active_slave)
 Network.PrimarySlave,                        config_parse_bool,                                        0,                             offsetof(Network, primary_slave)
 Network.IPv4ProxyARP,                        config_parse_tristate,                                    0,                             offsetof(Network, proxy_arp)
@@ -98,7 +114,7 @@ Network.ProxyARP,                            config_parse_tristate,
 Network.IPv6ProxyNDPAddress,                 config_parse_ipv6_proxy_ndp_address,                      0,                             0
 Network.BindCarrier,                         config_parse_strv,                                        0,                             offsetof(Network, bind_carrier)
 Network.ConfigureWithoutCarrier,             config_parse_bool,                                        0,                             offsetof(Network, configure_without_carrier)
-Network.IgnoreCarrierLoss,                   config_parse_bool,                                        0,                             offsetof(Network, ignore_carrier_loss)
+Network.IgnoreCarrierLoss,                   config_parse_tristate,                                    0,                             offsetof(Network, ignore_carrier_loss)
 Network.KeepConfiguration,                   config_parse_keep_configuration,                          0,                             offsetof(Network, keep_configuration)
 Address.Address,                             config_parse_address,                                     0,                             0
 Address.Peer,                                config_parse_address,                                     0,                             0
@@ -140,73 +156,95 @@ Route.Scope,                                 config_parse_route_scope,
 Route.PreferredSource,                       config_parse_preferred_src,                               0,                             0
 Route.Table,                                 config_parse_route_table,                                 0,                             0
 Route.MTUBytes,                              config_parse_route_mtu,                                   AF_UNSPEC,                     0
-Route.GatewayOnLink,                         config_parse_gateway_onlink,                              0,                             0
-Route.GatewayOnlink,                         config_parse_gateway_onlink,                              0,                             0
+Route.GatewayOnLink,                         config_parse_route_boolean,                               0,                             0
+Route.GatewayOnlink,                         config_parse_route_boolean,                               0,                             0
 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
-Route.FastOpenNoCookie,                      config_parse_fast_open_no_cookie,                         0,                             0
-Route.TTLPropagate,                          config_parse_route_ttl_propagate,                         0,                             0
+Route.QuickAck,                              config_parse_route_boolean,                               0,                             0
+Route.FastOpenNoCookie,                      config_parse_route_boolean,                               0,                             0
+Route.TTLPropagate,                          config_parse_route_boolean,                               0,                             0
 Route.MultiPathRoute,                        config_parse_multipath_route,                             0,                             0
 NextHop.Id,                                  config_parse_nexthop_id,                                  0,                             0
 NextHop.Gateway,                             config_parse_nexthop_gateway,                             0,                             0
 DHCPv4.ClientIdentifier,                     config_parse_dhcp_client_identifier,                      0,                             offsetof(Network, dhcp_client_identifier)
-DHCPv4.UseDNS,                               config_parse_bool,                                        0,                             offsetof(Network, dhcp_use_dns)
+DHCPv4.UseDNS,                               config_parse_dhcp_use_dns,                                0,                             0
 DHCPv4.RoutesToDNS,                          config_parse_bool,                                        0,                             offsetof(Network, dhcp_routes_to_dns)
-DHCPv4.UseNTP,                               config_parse_bool,                                        0,                             offsetof(Network, dhcp_use_ntp)
+DHCPv4.UseNTP,                               config_parse_dhcp_use_ntp,                                0,                             0
 DHCPv4.UseSIP,                               config_parse_bool,                                        0,                             offsetof(Network, dhcp_use_sip)
 DHCPv4.UseMTU,                               config_parse_bool,                                        0,                             offsetof(Network, dhcp_use_mtu)
 DHCPv4.UseHostname,                          config_parse_bool,                                        0,                             offsetof(Network, dhcp_use_hostname)
 DHCPv4.UseDomains,                           config_parse_dhcp_use_domains,                            0,                             offsetof(Network, dhcp_use_domains)
 DHCPv4.UseRoutes,                            config_parse_bool,                                        0,                             offsetof(Network, dhcp_use_routes)
-DHCPv4.RequestOptions,                       config_parse_dhcp_request_options,                        0,                             0
+DHCPv4.UseGateway,                           config_parse_tristate,                                    0,                             offsetof(Network, dhcp_use_gateway)
+DHCPv4.RequestOptions,                       config_parse_dhcp_request_options,                        AF_INET,                       0
 DHCPv4.Anonymize,                            config_parse_bool,                                        0,                             offsetof(Network, dhcp_anonymize)
 DHCPv4.SendHostname,                         config_parse_bool,                                        0,                             offsetof(Network, dhcp_send_hostname)
 DHCPv4.Hostname,                             config_parse_hostname,                                    0,                             offsetof(Network, dhcp_hostname)
 DHCPv4.RequestBroadcast,                     config_parse_bool,                                        0,                             offsetof(Network, dhcp_broadcast)
 DHCPv4.VendorClassIdentifier,                config_parse_string,                                      0,                             offsetof(Network, dhcp_vendor_class_identifier)
+DHCPv4.MUDURL,                               config_parse_dhcp_mud_url,                                0,                             0
 DHCPv4.MaxAttempts,                          config_parse_dhcp_max_attempts,                           0,                             0
-DHCPv4.UserClass,                            config_parse_dhcp_user_class,                             0,                             offsetof(Network, dhcp_user_class)
+DHCPv4.UserClass,                            config_parse_dhcp_user_class,                             AF_INET,                       offsetof(Network, dhcp_user_class)
 DHCPv4.DUIDType,                             config_parse_duid_type,                                   0,                             offsetof(Network, duid)
 DHCPv4.DUIDRawData,                          config_parse_duid_rawdata,                                0,                             offsetof(Network, duid)
-DHCPv4.RouteMetric,                          config_parse_uint32,                                      0,                             offsetof(Network, dhcp_route_metric)
+DHCPv4.RouteMetric,                          config_parse_dhcp_route_metric,                           0,                             0
 DHCPv4.RouteTable,                           config_parse_section_route_table,                         0,                             0
 DHCPv4.UseTimezone,                          config_parse_bool,                                        0,                             offsetof(Network, dhcp_use_timezone)
 DHCPv4.IAID,                                 config_parse_iaid,                                        0,                             0
 DHCPv4.ListenPort,                           config_parse_uint16,                                      0,                             offsetof(Network, dhcp_client_port)
 DHCPv4.SendRelease,                          config_parse_bool,                                        0,                             offsetof(Network, dhcp_send_release)
 DHCPv4.SendDecline,                          config_parse_bool,                                        0,                             offsetof(Network, dhcp_send_decline)
-DHCPv4.BlackList,                            config_parse_dhcp_black_listed_ip_address,                0,                             0
+DHCPv4.DenyList,                             config_parse_dhcp_acl_ip_address,                         0,                             0
+DHCPv4.AllowList,                            config_parse_dhcp_acl_ip_address,                         0,                             0
 DHCPv4.IPServiceType,                        config_parse_dhcp_ip_service_type,                        0,                             offsetof(Network, ip_service_type)
-DHCPv4.SendOption,                           config_parse_dhcp_send_option,                            0,                             offsetof(Network, dhcp_client_send_options)
+DHCPv4.SendOption,                           config_parse_dhcp_send_option,                            AF_INET,                       offsetof(Network, dhcp_client_send_options)
+DHCPv4.SendVendorOption,                     config_parse_dhcp_send_option,                            0,                             offsetof(Network, dhcp_client_send_vendor_options)
 DHCPv4.RouteMTUBytes,                        config_parse_mtu,                                         AF_INET,                       offsetof(Network, dhcp_route_mtu)
-DHCPv6.UseDNS,                               config_parse_bool,                                        0,                             offsetof(Network, dhcp6_use_dns)
-DHCPv6.UseNTP,                               config_parse_bool,                                        0,                             offsetof(Network, dhcp6_use_ntp)
+DHCPv4.FallbackLeaseLifetimeSec,             config_parse_dhcp_fallback_lease_lifetime,                0,                             0
+DHCPv6.UseDNS,                               config_parse_dhcp_use_dns,                                0,                             0
+DHCPv6.UseNTP,                               config_parse_dhcp_use_ntp,                                0,                             0
 DHCPv6.RapidCommit,                          config_parse_bool,                                        0,                             offsetof(Network, rapid_commit)
+DHCPv6.MUDURL,                               config_parse_dhcp6_mud_url,                               0,                             0
+DHCPv6.RequestOptions,                       config_parse_dhcp_request_options,                        AF_INET6,                      0
+DHCPv6.UserClass,                            config_parse_dhcp_user_class,                             AF_INET6,                      offsetof(Network, dhcp6_user_class)
+DHCPv6.VendorClass,                          config_parse_dhcp_vendor_class,                           0,                             offsetof(Network, dhcp6_vendor_class)
+DHCPv6.SendVendorOption,                     config_parse_dhcp_send_option,                            AF_INET6,                      offsetof(Network, dhcp6_client_send_vendor_options)
 DHCPv6.ForceDHCPv6PDOtherInformation,        config_parse_bool,                                        0,                             offsetof(Network, dhcp6_force_pd_other_information)
 DHCPv6.PrefixDelegationHint,                 config_parse_dhcp6_pd_hint,                               0,                             0
+DHCPv6.WithoutRA,                            config_parse_dhcp6_client_start_mode,                     0,                             offsetof(Network, dhcp6_without_ra)
+DHCPv6.SendOption,                           config_parse_dhcp_send_option,                            AF_INET6,                      offsetof(Network, dhcp6_client_send_options)
+DHCPv6.RouteMetric,                          config_parse_dhcp_route_metric,                           0,                             0
 IPv6AcceptRA.UseAutonomousPrefix,            config_parse_bool,                                        0,                             offsetof(Network, ipv6_accept_ra_use_autonomous_prefix)
 IPv6AcceptRA.UseOnLinkPrefix,                config_parse_bool,                                        0,                             offsetof(Network, ipv6_accept_ra_use_onlink_prefix)
 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.DHCPv6Client,                   config_parse_ipv6_accept_ra_start_dhcp6_client,           0,                             offsetof(Network, ipv6_accept_ra_start_dhcp6_client)
 IPv6AcceptRA.RouteTable,                     config_parse_section_route_table,                         0,                             0
-IPv6AcceptRA.BlackList,                      config_parse_ndisc_black_listed_prefix,                   0,                             0
+IPv6AcceptRA.DenyList,                       config_parse_ndisc_deny_listed_prefix,                    0,                             0
+IPv6AcceptRA.BlackList,                      config_parse_ndisc_deny_listed_prefix,                    0,                             0
 DHCPServer.MaxLeaseTimeSec,                  config_parse_sec,                                         0,                             offsetof(Network, dhcp_server_max_lease_time_usec)
 DHCPServer.DefaultLeaseTimeSec,              config_parse_sec,                                         0,                             offsetof(Network, dhcp_server_default_lease_time_usec)
-DHCPServer.EmitDNS,                          config_parse_bool,                                        0,                             offsetof(Network, dhcp_server_emit_dns)
-DHCPServer.DNS,                              config_parse_dhcp_server_dns,                             0,                             0
-DHCPServer.EmitNTP,                          config_parse_bool,                                        0,                             offsetof(Network, dhcp_server_emit_ntp)
-DHCPServer.NTP,                              config_parse_dhcp_server_ntp,                             0,                             0
-DHCPServer.EmitSIP,                          config_parse_bool,                                        0,                             offsetof(Network, dhcp_server_emit_sip)
-DHCPServer.SIP,                              config_parse_dhcp_server_sip,                             0,                             0
+DHCPServer.EmitDNS,                          config_parse_bool,                                        0,                             offsetof(Network, dhcp_server_emit[SD_DHCP_LEASE_DNS].emit)
+DHCPServer.DNS,                              config_parse_dhcp_server_emit,                            0,                             offsetof(Network, dhcp_server_emit[SD_DHCP_LEASE_DNS])
+DHCPServer.EmitNTP,                          config_parse_bool,                                        0,                             offsetof(Network, dhcp_server_emit[SD_DHCP_LEASE_NTP].emit)
+DHCPServer.NTP,                              config_parse_dhcp_server_emit,                            0,                             offsetof(Network, dhcp_server_emit[SD_DHCP_LEASE_NTP])
+DHCPServer.EmitSIP,                          config_parse_bool,                                        0,                             offsetof(Network, dhcp_server_emit[SD_DHCP_LEASE_SIP].emit)
+DHCPServer.SIP,                              config_parse_dhcp_server_emit,                            0,                             offsetof(Network, dhcp_server_emit[SD_DHCP_LEASE_SIP])
+DHCPServer.EmitPOP3,                         config_parse_bool,                                        0,                             offsetof(Network, dhcp_server_emit[SD_DHCP_LEASE_POP3].emit)
+DHCPServer.POP3,                             config_parse_dhcp_server_emit,                            0,                             offsetof(Network, dhcp_server_emit[SD_DHCP_LEASE_POP3])
+DHCPServer.EmitSMTP,                         config_parse_bool,                                        0,                             offsetof(Network, dhcp_server_emit[SD_DHCP_LEASE_SMTP].emit)
+DHCPServer.SMTP,                             config_parse_dhcp_server_emit,                            0,                             offsetof(Network, dhcp_server_emit[SD_DHCP_LEASE_SMTP])
+DHCPServer.EmitLPR,                          config_parse_bool,                                        0,                             offsetof(Network, dhcp_server_emit[SD_DHCP_LEASE_LPR].emit)
+DHCPServer.LPR,                              config_parse_dhcp_server_emit,                            0,                             offsetof(Network, dhcp_server_emit[SD_DHCP_LEASE_LPR])
 DHCPServer.EmitRouter,                       config_parse_bool,                                        0,                             offsetof(Network, dhcp_server_emit_router)
 DHCPServer.EmitTimezone,                     config_parse_bool,                                        0,                             offsetof(Network, dhcp_server_emit_timezone)
 DHCPServer.Timezone,                         config_parse_timezone,                                    0,                             offsetof(Network, dhcp_server_timezone)
 DHCPServer.PoolOffset,                       config_parse_uint32,                                      0,                             offsetof(Network, dhcp_server_pool_offset)
 DHCPServer.PoolSize,                         config_parse_uint32,                                      0,                             offsetof(Network, dhcp_server_pool_size)
+DHCPServer.SendVendorOption,                 config_parse_dhcp_send_option,                            0,                             offsetof(Network, dhcp_server_send_vendor_options)
 DHCPServer.SendOption,                       config_parse_dhcp_send_option,                            0,                             offsetof(Network, dhcp_server_send_options)
 Bridge.Cost,                                 config_parse_uint32,                                      0,                             offsetof(Network, cost)
 Bridge.UseBPDU,                              config_parse_tristate,                                    0,                             offsetof(Network, use_bpdu)
@@ -230,7 +268,10 @@ BridgeFDB.AssociatedWith,                    config_parse_fdb_ntf_flags,
 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_router_prefix_delegation,                    0,                             0
+Network.IPv6PrefixDelegation,                config_parse_router_prefix_delegation,                    0,                             offsetof(Network, router_prefix_delegation)
+DHCPv6PrefixDelegation.SubnetId,             config_parse_dhcp6_pd_subnet_id,                          0,                             offsetof(Network, dhcp6_pd_subnet_id)
+DHCPv6PrefixDelegation.Assign,               config_parse_bool,                                        0,                             offsetof(Network, dhcp6_pd_assign)
+DHCPv6PrefixDelegation.Token,                config_parse_dhcp6_pd_token,                              0,                             offsetof(Network, dhcp6_pd_token)
 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)
@@ -245,14 +286,29 @@ IPv6Prefix.OnLink,                           config_parse_prefix_flags,
 IPv6Prefix.AddressAutoconfiguration,         config_parse_prefix_flags,                                0,                             0
 IPv6Prefix.ValidLifetimeSec,                 config_parse_prefix_lifetime,                             0,                             0
 IPv6Prefix.PreferredLifetimeSec,             config_parse_prefix_lifetime,                             0,                             0
+IPv6Prefix.Assign,                           config_parse_prefix_assign,                               0,                             0
 IPv6RoutePrefix.Route,                       config_parse_route_prefix,                                0,                             0
 IPv6RoutePrefix.LifetimeSec,                 config_parse_route_prefix_lifetime,                       0,                             0
-CAN.BitRate,                                 config_parse_si_uint64,                                   0,                             offsetof(Network, can_bitrate)
+LLDP.MUDURL,                                 config_parse_lldp_mud,                                    0,                             0
+CAN.BitRate,                                 config_parse_can_bitrate,                                 0,                             offsetof(Network, can_bitrate)
 CAN.SamplePoint,                             config_parse_permille,                                    0,                             offsetof(Network, can_sample_point)
+CAN.DataBitRate,                             config_parse_can_bitrate,                                 0,                             offsetof(Network, can_data_bitrate)
+CAN.DataSamplePoint,                         config_parse_permille,                                    0,                             offsetof(Network, can_data_sample_point)
+CAN.FDMode,                                  config_parse_tristate,                                    0,                             offsetof(Network, can_fd_mode)
+CAN.FDNonISO,                                config_parse_tristate,                                    0,                             offsetof(Network, can_non_iso)
 CAN.RestartSec,                              config_parse_sec,                                         0,                             offsetof(Network, can_restart_us)
 CAN.TripleSampling,                          config_parse_tristate,                                    0,                             offsetof(Network, can_triple_sampling)
+CAN.Termination,                             config_parse_tristate,                                    0,                             offsetof(Network, can_termination)
+CAN.ListenOnly,                              config_parse_tristate,                                    0,                             offsetof(Network, can_listen_only)
 QDisc.Parent,                                config_parse_qdisc_parent,                                _QDISC_KIND_INVALID,           0
 QDisc.Handle,                                config_parse_qdisc_handle,                                _QDISC_KIND_INVALID,           0
+BFIFO.Parent,                                config_parse_qdisc_parent,                                QDISC_KIND_BFIFO,              0
+BFIFO.Handle,                                config_parse_qdisc_handle,                                QDISC_KIND_BFIFO,              0
+BFIFO.LimitBytes,                            config_parse_bfifo_size,                                  QDISC_KIND_BFIFO,              0
+CAKE.Parent,                                 config_parse_qdisc_parent,                                QDISC_KIND_CAKE,               0
+CAKE.Handle,                                 config_parse_qdisc_handle,                                QDISC_KIND_CAKE,               0
+CAKE.Bandwidth,                              config_parse_cake_bandwidth,                              QDISC_KIND_CAKE,               0
+CAKE.OverheadBytes,                          config_parse_cake_overhead,                               QDISC_KIND_CAKE,               0
 ControlledDelay.Parent,                      config_parse_qdisc_parent,                                QDISC_KIND_CODEL,              0
 ControlledDelay.Handle,                      config_parse_qdisc_handle,                                QDISC_KIND_CODEL,              0
 ControlledDelay.PacketLimit,                 config_parse_controlled_delay_u32,                        QDISC_KIND_CODEL,              0
@@ -260,12 +316,37 @@ ControlledDelay.TargetSec,                   config_parse_controlled_delay_usec,
 ControlledDelay.IntervalSec,                 config_parse_controlled_delay_usec,                       QDISC_KIND_CODEL,              0
 ControlledDelay.CEThresholdSec,              config_parse_controlled_delay_usec,                       QDISC_KIND_CODEL,              0
 ControlledDelay.ECN,                         config_parse_controlled_delay_bool,                       QDISC_KIND_CODEL,              0
+DeficitRoundRobinScheduler.Parent,           config_parse_qdisc_parent,                                QDISC_KIND_DRR,                0
+DeficitRoundRobinScheduler.Handle,           config_parse_qdisc_handle,                                QDISC_KIND_DRR,                0
+DeficitRoundRobinSchedulerClass.Parent,      config_parse_tclass_parent,                               TCLASS_KIND_DRR,               0
+DeficitRoundRobinSchedulerClass.ClassId,     config_parse_tclass_classid,                              TCLASS_KIND_DRR,               0
+DeficitRoundRobinSchedulerClass.QuantumBytes, config_parse_drr_size,                                   TCLASS_KIND_DRR,               0
+EnhancedTransmissionSelection.Parent,        config_parse_qdisc_parent,                                QDISC_KIND_ETS,                0
+EnhancedTransmissionSelection.Handle,        config_parse_qdisc_handle,                                QDISC_KIND_ETS,                0
+EnhancedTransmissionSelection.Bands,         config_parse_ets_u8,                                      QDISC_KIND_ETS,                0
+EnhancedTransmissionSelection.StrictBands,   config_parse_ets_u8,                                      QDISC_KIND_ETS,                0
+EnhancedTransmissionSelection.QuantumBytes,  config_parse_ets_quanta,                                  QDISC_KIND_ETS,                0
+EnhancedTransmissionSelection.PriorityMap,   config_parse_ets_prio,                                    QDISC_KIND_ETS,                0
+PFIFO.Parent,                                config_parse_qdisc_parent,                                QDISC_KIND_PFIFO,              0
+PFIFO.Handle,                                config_parse_qdisc_handle,                                QDISC_KIND_PFIFO,              0
+PFIFO.PacketLimit,                           config_parse_pfifo_size,                                  QDISC_KIND_PFIFO,              0
+PFIFOFast.Parent,                            config_parse_qdisc_parent,                                QDISC_KIND_PFIFO_FAST,         0
+PFIFOFast.Handle,                            config_parse_qdisc_handle,                                QDISC_KIND_PFIFO_FAST,         0
+PFIFOHeadDrop.Parent,                        config_parse_qdisc_parent,                                QDISC_KIND_PFIFO_HEAD_DROP,    0
+PFIFOHeadDrop.Handle,                        config_parse_qdisc_handle,                                QDISC_KIND_PFIFO_HEAD_DROP,    0
+PFIFOHeadDrop.PacketLimit,                   config_parse_pfifo_size,                                  QDISC_KIND_PFIFO_HEAD_DROP,    0
+QuickFairQueueing.Parent,                    config_parse_qdisc_parent,                                QDISC_KIND_QFQ,                0
+QuickFairQueueing.Handle,                    config_parse_qdisc_handle,                                QDISC_KIND_QFQ,                0
+QuickFairQueueingClass.Parent,               config_parse_tclass_parent,                               TCLASS_KIND_QFQ,               0
+QuickFairQueueingClass.ClassId,              config_parse_tclass_classid,                              TCLASS_KIND_QFQ,               0
+QuickFairQueueingClass.Weight,               config_parse_quick_fair_queueing_weight,                  TCLASS_KIND_QFQ,               0
+QuickFairQueueingClass.MaxPacketBytes,       config_parse_quick_fair_queueing_max_packet,              TCLASS_KIND_QFQ,               0
 FairQueueing.Parent,                         config_parse_qdisc_parent,                                QDISC_KIND_FQ,                 0
 FairQueueing.Handle,                         config_parse_qdisc_handle,                                QDISC_KIND_FQ,                 0
 FairQueueing.PacketLimit,                    config_parse_fair_queueing_u32,                           QDISC_KIND_FQ,                 0
 FairQueueing.FlowLimit,                      config_parse_fair_queueing_u32,                           QDISC_KIND_FQ,                 0
-FairQueueing.Quantum,                        config_parse_fair_queueing_size,                          QDISC_KIND_FQ,                 0
-FairQueueing.InitialQuantum,                 config_parse_fair_queueing_size,                          QDISC_KIND_FQ,                 0
+FairQueueing.QuantumBytes,                   config_parse_fair_queueing_size,                          QDISC_KIND_FQ,                 0
+FairQueueing.InitialQuantumBytes,            config_parse_fair_queueing_size,                          QDISC_KIND_FQ,                 0
 FairQueueing.MaximumRate,                    config_parse_fair_queueing_max_rate,                      QDISC_KIND_FQ,                 0
 FairQueueing.Buckets,                        config_parse_fair_queueing_u32,                           QDISC_KIND_FQ,                 0
 FairQueueing.OrphanMask,                     config_parse_fair_queueing_u32,                           QDISC_KIND_FQ,                 0
@@ -274,13 +355,35 @@ FairQueueing.CEThresholdSec,                 config_parse_fair_queueing_usec,
 FairQueueingControlledDelay.Parent,          config_parse_qdisc_parent,                                QDISC_KIND_FQ_CODEL,           0
 FairQueueingControlledDelay.Handle,          config_parse_qdisc_handle,                                QDISC_KIND_FQ_CODEL,           0
 FairQueueingControlledDelay.PacketLimit,     config_parse_fair_queueing_controlled_delay_u32,          QDISC_KIND_FQ_CODEL,           0
-FairQueueingControlledDelay.MemoryLimit,     config_parse_fair_queueing_controlled_delay_size,         QDISC_KIND_FQ_CODEL,           0
+FairQueueingControlledDelay.MemoryLimitBytes, config_parse_fair_queueing_controlled_delay_size,        QDISC_KIND_FQ_CODEL,           0
 FairQueueingControlledDelay.Flows,           config_parse_fair_queueing_controlled_delay_u32,          QDISC_KIND_FQ_CODEL,           0
-FairQueueingControlledDelay.Quantum,         config_parse_fair_queueing_controlled_delay_size,         QDISC_KIND_FQ_CODEL,           0
+FairQueueingControlledDelay.QuantumBytes,    config_parse_fair_queueing_controlled_delay_size,         QDISC_KIND_FQ_CODEL,           0
 FairQueueingControlledDelay.TargetSec,       config_parse_fair_queueing_controlled_delay_usec,         QDISC_KIND_FQ_CODEL,           0
 FairQueueingControlledDelay.IntervalSec,     config_parse_fair_queueing_controlled_delay_usec,         QDISC_KIND_FQ_CODEL,           0
 FairQueueingControlledDelay.CEThresholdSec,  config_parse_fair_queueing_controlled_delay_usec,         QDISC_KIND_FQ_CODEL,           0
 FairQueueingControlledDelay.ECN,             config_parse_fair_queueing_controlled_delay_bool,         QDISC_KIND_FQ_CODEL,           0
+GenericRandomEarlyDetection.Parent,          config_parse_qdisc_parent,                                QDISC_KIND_GRED,               0
+GenericRandomEarlyDetection.Handle,          config_parse_qdisc_handle,                                QDISC_KIND_GRED,               0
+GenericRandomEarlyDetection.VirtualQueues,   config_parse_generic_random_early_detection_u32,          QDISC_KIND_GRED,               0
+GenericRandomEarlyDetection.DefaultVirtualQueue, config_parse_generic_random_early_detection_u32,      QDISC_KIND_GRED,               0
+GenericRandomEarlyDetection.GenericRIO,      config_parse_generic_random_early_detection_bool,         QDISC_KIND_GRED,               0
+HeavyHitterFilter.Parent,                    config_parse_qdisc_parent,                                QDISC_KIND_HHF,                0
+HeavyHitterFilter.Handle,                    config_parse_qdisc_handle,                                QDISC_KIND_HHF,                0
+HeavyHitterFilter.PacketLimit,               config_parse_heavy_hitter_filter_packet_limit,            QDISC_KIND_HHF,                0
+HierarchyTokenBucket.Parent,                 config_parse_qdisc_parent,                                QDISC_KIND_HTB,                0
+HierarchyTokenBucket.Handle,                 config_parse_qdisc_handle,                                QDISC_KIND_HTB,                0
+HierarchyTokenBucket.DefaultClass,           config_parse_hierarchy_token_bucket_default_class,        QDISC_KIND_HTB,                0
+HierarchyTokenBucket.RateToQuantum,          config_parse_hierarchy_token_bucket_u32,                  QDISC_KIND_HTB,                0
+HierarchyTokenBucketClass.Parent,            config_parse_tclass_parent,                               TCLASS_KIND_HTB,               0
+HierarchyTokenBucketClass.ClassId,           config_parse_tclass_classid,                              TCLASS_KIND_HTB,               0
+HierarchyTokenBucketClass.Priority,          config_parse_hierarchy_token_bucket_class_u32,            TCLASS_KIND_HTB,               0
+HierarchyTokenBucketClass.QuantumBytes,      config_parse_hierarchy_token_bucket_class_size,           TCLASS_KIND_HTB,               0
+HierarchyTokenBucketClass.MTUBytes,          config_parse_hierarchy_token_bucket_class_size,           TCLASS_KIND_HTB,               0
+HierarchyTokenBucketClass.OverheadBytes,     config_parse_hierarchy_token_bucket_class_size,           TCLASS_KIND_HTB,               0
+HierarchyTokenBucketClass.Rate,              config_parse_hierarchy_token_bucket_class_rate,           TCLASS_KIND_HTB,               0
+HierarchyTokenBucketClass.CeilRate,          config_parse_hierarchy_token_bucket_class_rate,           TCLASS_KIND_HTB,               0
+HierarchyTokenBucketClass.BufferBytes,       config_parse_hierarchy_token_bucket_class_size,           TCLASS_KIND_HTB,               0
+HierarchyTokenBucketClass.CeilBufferBytes,   config_parse_hierarchy_token_bucket_class_size,           TCLASS_KIND_HTB,               0
 NetworkEmulator.Parent,                      config_parse_qdisc_parent,                                QDISC_KIND_NETEM,              0
 NetworkEmulator.Handle,                      config_parse_qdisc_handle,                                QDISC_KIND_NETEM,              0
 NetworkEmulator.DelaySec,                    config_parse_network_emulator_delay,                      QDISC_KIND_NETEM,              0
@@ -288,23 +391,30 @@ NetworkEmulator.DelayJitterSec,              config_parse_network_emulator_delay
 NetworkEmulator.LossRate,                    config_parse_network_emulator_rate,                       QDISC_KIND_NETEM,              0
 NetworkEmulator.DuplicateRate,               config_parse_network_emulator_rate,                       QDISC_KIND_NETEM,              0
 NetworkEmulator.PacketLimit,                 config_parse_network_emulator_packet_limit,               QDISC_KIND_NETEM,              0
+PIE.Parent,                                  config_parse_qdisc_parent,                                QDISC_KIND_PIE,                0
+PIE.Handle,                                  config_parse_qdisc_handle,                                QDISC_KIND_PIE,                0
+PIE.PacketLimit,                             config_parse_pie_packet_limit,                            QDISC_KIND_PIE,                0
+StochasticFairBlue.Parent,                   config_parse_qdisc_parent,                                QDISC_KIND_SFB,                0
+StochasticFairBlue.Handle,                   config_parse_qdisc_handle,                                QDISC_KIND_SFB,                0
+StochasticFairBlue.PacketLimit,              config_parse_stochastic_fair_blue_u32,                    QDISC_KIND_SFB,                0
 StochasticFairnessQueueing.Parent,           config_parse_qdisc_parent,                                QDISC_KIND_SFQ,                0
 StochasticFairnessQueueing.Handle,           config_parse_qdisc_handle,                                QDISC_KIND_SFQ,                0
 StochasticFairnessQueueing.PerturbPeriodSec, config_parse_stochastic_fairness_queueing_perturb_period, QDISC_KIND_SFQ,                0
 TokenBucketFilter.Parent,                    config_parse_qdisc_parent,                                QDISC_KIND_TBF,                0
 TokenBucketFilter.Handle,                    config_parse_qdisc_handle,                                QDISC_KIND_TBF,                0
-TokenBucketFilter.Rate,                      config_parse_token_bucket_filter_size,                    QDISC_KIND_TBF,                0
-TokenBucketFilter.Burst,                     config_parse_token_bucket_filter_size,                    QDISC_KIND_TBF,                0
-TokenBucketFilter.LimitSize,                 config_parse_token_bucket_filter_size,                    QDISC_KIND_TBF,                0
+TokenBucketFilter.Rate,                      config_parse_token_bucket_filter_rate,                    QDISC_KIND_TBF,                0
+TokenBucketFilter.BurstBytes,                config_parse_token_bucket_filter_size,                    QDISC_KIND_TBF,                0
+TokenBucketFilter.LimitBytes,                config_parse_token_bucket_filter_size,                    QDISC_KIND_TBF,                0
 TokenBucketFilter.MTUBytes,                  config_parse_token_bucket_filter_size,                    QDISC_KIND_TBF,                0
 TokenBucketFilter.MPUBytes,                  config_parse_token_bucket_filter_size,                    QDISC_KIND_TBF,                0
-TokenBucketFilter.PeakRate,                  config_parse_token_bucket_filter_size,                    QDISC_KIND_TBF,                0
+TokenBucketFilter.PeakRate,                  config_parse_token_bucket_filter_rate,                    QDISC_KIND_TBF,                0
 TokenBucketFilter.LatencySec,                config_parse_token_bucket_filter_latency,                 QDISC_KIND_TBF,                0
 TrivialLinkEqualizer.Parent,                 config_parse_qdisc_parent,                                QDISC_KIND_TEQL,               0
 TrivialLinkEqualizer.Handle,                 config_parse_qdisc_handle,                                QDISC_KIND_TEQL,               0
 TrivialLinkEqualizer.Id,                     config_parse_trivial_link_equalizer_id,                   QDISC_KIND_TEQL,               0
 /* backwards compatibility: do not add new entries to this section */
 Network.IPv4LL,                              config_parse_ipv4ll,                                      0,                             offsetof(Network, link_local)
+DHCPv4.BlackList,                            config_parse_dhcp_acl_ip_address,                         0,                             0
 DHCP.ClientIdentifier,                       config_parse_dhcp_client_identifier,                      0,                             offsetof(Network, dhcp_client_identifier)
 DHCP.UseDNS,                                 config_parse_dhcp_use_dns,                                0,                             0
 DHCP.UseNTP,                                 config_parse_dhcp_use_ntp,                                0,                             0
@@ -319,10 +429,10 @@ DHCP.Hostname,                               config_parse_hostname,
 DHCP.RequestBroadcast,                       config_parse_bool,                                        0,                             offsetof(Network, dhcp_broadcast)
 DHCP.CriticalConnection,                     config_parse_tristate,                                    0,                             offsetof(Network, dhcp_critical)
 DHCP.VendorClassIdentifier,                  config_parse_string,                                      0,                             offsetof(Network, dhcp_vendor_class_identifier)
-DHCP.UserClass,                              config_parse_dhcp_user_class,                             0,                             offsetof(Network, dhcp_user_class)
+DHCP.UserClass,                              config_parse_dhcp_user_class,                             AF_INET,                       offsetof(Network, dhcp_user_class)
 DHCP.DUIDType,                               config_parse_duid_type,                                   0,                             offsetof(Network, duid)
 DHCP.DUIDRawData,                            config_parse_duid_rawdata,                                0,                             offsetof(Network, duid)
-DHCP.RouteMetric,                            config_parse_uint32,                                      0,                             offsetof(Network, dhcp_route_metric)
+DHCP.RouteMetric,                            config_parse_dhcp_route_metric,                           0,                             0
 DHCP.RouteTable,                             config_parse_section_route_table,                         0,                             0
 DHCP.UseTimezone,                            config_parse_bool,                                        0,                             offsetof(Network, dhcp_use_timezone)
 DHCP.IAID,                                   config_parse_iaid,                                        0,                             0
@@ -337,3 +447,9 @@ TrafficControlQueueingDiscipline.NetworkEmulatorDelayJitterSec, config_parse_net
 TrafficControlQueueingDiscipline.NetworkEmulatorLossRate,       config_parse_network_emulator_rate,    0,                             0
 TrafficControlQueueingDiscipline.NetworkEmulatorDuplicateRate,  config_parse_network_emulator_rate,    0,                             0
 TrafficControlQueueingDiscipline.NetworkEmulatorPacketLimit,    config_parse_network_emulator_packet_limit, 0,                        0
+FairQueueing.Quantum,                        config_parse_fair_queueing_size,                          QDISC_KIND_FQ,                 0
+FairQueueing.InitialQuantum,                 config_parse_fair_queueing_size,                          QDISC_KIND_FQ,                 0
+FairQueueingControlledDelay.MemoryLimit,     config_parse_fair_queueing_controlled_delay_size,         QDISC_KIND_FQ_CODEL,           0
+FairQueueingControlledDelay.Quantum,         config_parse_fair_queueing_controlled_delay_size,         QDISC_KIND_FQ_CODEL,           0
+TokenBucketFilter.Burst,                     config_parse_token_bucket_filter_size,                    QDISC_KIND_TBF,                0
+TokenBucketFilter.LimitSize,                 config_parse_token_bucket_filter_size,                    QDISC_KIND_TBF,                0
index 255aaed51165240eb88d3caeb1a8f8d7f5cc34f2..d34155b19f4c3e1e35652c66d1d7717f8d4bb969 100644 (file)
@@ -3,6 +3,7 @@
 #include <net/if.h>
 #include <netinet/in.h>
 #include <linux/netdevice.h>
+#include <unistd.h>
 
 #include "alloc-util.h"
 #include "conf-files.h"
 #include "network-internal.h"
 #include "networkd-manager.h"
 #include "networkd-network.h"
+#include "networkd-sriov.h"
 #include "parse-util.h"
+#include "path-lookup.h"
 #include "set.h"
 #include "socket-util.h"
 #include "stat-util.h"
 #include "string-table.h"
 #include "string-util.h"
 #include "strv.h"
+#include "tc.h"
 #include "util.h"
 
 /* Let's assume that anything above this number is a user misconfiguration. */
@@ -48,7 +52,7 @@ void network_apply_anonymize_if_set(Network *network) {
         /* RFC7844 section 3.6.:
          The client intending to protect its privacy SHOULD only request a
          minimal number of options in the PRL and SHOULD also randomly shuffle
-         the ordering of option codes in the PRL.  If this random ordering
+         the ordering of option codes in the PRL. If this random ordering
          cannot be implemented, the client MAY order the option codes in the
          PRL by option code number (lowest to highest).
         */
@@ -154,7 +158,8 @@ int network_verify(Network *network) {
         Prefix *prefix, *prefix_next;
         Route *route, *route_next;
         FdbEntry *fdb, *fdb_next;
-        QDisc *qdisc;
+        TrafficControl *tc;
+        SRIOV *sr_iov;
         Iterator i;
 
         assert(network);
@@ -163,14 +168,15 @@ int network_verify(Network *network) {
         if (set_isempty(network->match_mac) && set_isempty(network->match_permanent_mac) &&
             strv_isempty(network->match_path) && strv_isempty(network->match_driver) &&
             strv_isempty(network->match_type) && strv_isempty(network->match_name) &&
-            strv_isempty(network->match_property) && strv_isempty(network->match_ssid) && !network->conditions)
+            strv_isempty(network->match_property) && strv_isempty(network->match_wlan_iftype) &&
+            strv_isempty(network->match_ssid) && !network->conditions)
                 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
                                          "%s: No valid settings found in the [Match] section, ignoring file. "
                                          "To match all interfaces, add Name=* in the [Match] section.",
                                          network->filename);
 
         /* skip out early if configuration does not match the environment */
-        if (!condition_test_list(network->conditions, NULL, NULL, NULL))
+        if (!condition_test_list(network->conditions, environ, NULL, NULL, NULL))
                 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
                                        "%s: Conditions in the file do not match the system environment, skipping.",
                                        network->filename);
@@ -265,6 +271,12 @@ int network_verify(Network *network) {
                 network->dhcp_use_mtu = false;
         }
 
+        if (network->dhcp_use_gateway < 0)
+                network->dhcp_use_gateway = network->dhcp_use_routes;
+
+        if (network->ignore_carrier_loss < 0)
+                network->ignore_carrier_loss = network->configure_without_carrier;
+
         if (network->dhcp_critical >= 0) {
                 if (network->keep_configuration >= 0)
                         log_warning("%s: Both KeepConfiguration= and deprecated CriticalConnection= are set. "
@@ -316,9 +328,13 @@ int network_verify(Network *network) {
                         routing_policy_rule_free(rule);
 
         bool has_root = false, has_clsact = false;
-        ORDERED_HASHMAP_FOREACH(qdisc, network->qdiscs_by_section, i)
-                if (qdisc_section_verify(qdisc, &has_root, &has_clsact) < 0)
-                        qdisc_free(qdisc);
+        ORDERED_HASHMAP_FOREACH(tc, network->tc_by_section, i)
+                if (traffic_control_section_verify(tc, &has_root, &has_clsact) < 0)
+                        traffic_control_free(tc);
+
+        ORDERED_HASHMAP_FOREACH(sr_iov, network->sr_iov_by_section, i)
+                if (sr_iov_section_verify(sr_iov) < 0)
+                        sr_iov_free(sr_iov);
 
         return 0;
 }
@@ -326,7 +342,6 @@ int network_verify(Network *network) {
 int network_load_one(Manager *manager, OrderedHashmap **networks, const char *filename) {
         _cleanup_free_ char *fname = NULL, *name = NULL;
         _cleanup_(network_unrefp) Network *network = NULL;
-        _cleanup_strv_free_ char **dropins = NULL;
         _cleanup_fclose_ FILE *file = NULL;
         const char *dropin_dirname;
         char *d;
@@ -384,6 +399,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
                 .dhcp_use_dns = true,
                 .dhcp_use_hostname = true,
                 .dhcp_use_routes = true,
+                .dhcp_use_gateway = -1,
                 /* NOTE: this var might be overwritten by network_apply_anonymize_if_set */
                 .dhcp_send_hostname = true,
                 .dhcp_send_release = true,
@@ -400,12 +416,17 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
                 .dhcp_use_timezone = false,
                 .rapid_commit = true,
 
+                .dhcp6_route_metric = DHCP_ROUTE_METRIC,
                 .dhcp6_use_ntp = true,
                 .dhcp6_use_dns = true,
 
-                .dhcp_server_emit_dns = true,
-                .dhcp_server_emit_ntp = true,
-                .dhcp_server_emit_sip = true,
+                .dhcp6_pd_subnet_id = -1,
+                .dhcp6_pd_assign = true,
+
+                .dhcp_server_emit[SD_DHCP_LEASE_DNS].emit = true,
+                .dhcp_server_emit[SD_DHCP_LEASE_NTP].emit = true,
+                .dhcp_server_emit[SD_DHCP_LEASE_SIP].emit = true,
+
                 .dhcp_server_emit_router = true,
                 .dhcp_server_emit_timezone = true,
 
@@ -436,6 +457,9 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
 
                 /* If LinkLocalAddressing= is not set, then set to ADDRESS_FAMILY_IPV6 later. */
                 .link_local = _ADDRESS_FAMILY_INVALID,
+                .ipv6ll_address_gen_mode = _IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_INVALID,
+
+                .ipv4_accept_local = -1,
 
                 .ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_NO,
                 .ipv6_accept_ra = -1,
@@ -452,47 +476,72 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
                 .ipv6_accept_ra_use_onlink_prefix = true,
                 .ipv6_accept_ra_route_table = RT_TABLE_MAIN,
                 .ipv6_accept_ra_route_table_set = false,
+                .ipv6_accept_ra_start_dhcp6_client = true,
 
+                .configure_without_carrier = false,
+                .ignore_carrier_loss = -1,
                 .keep_configuration = _KEEP_CONFIGURATION_INVALID,
-
                 .can_triple_sampling = -1,
+                .can_termination = -1,
                 .ip_service_type = -1,
         };
 
-        r = config_parse_many(filename, NETWORK_DIRS, dropin_dirname,
-                              "Match\0"
-                              "Link\0"
-                              "Network\0"
-                              "Address\0"
-                              "Neighbor\0"
-                              "IPv6AddressLabel\0"
-                              "RoutingPolicyRule\0"
-                              "Route\0"
-                              "NextHop\0"
-                              "DHCP\0" /* compat */
-                              "DHCPv4\0"
-                              "DHCPv6\0"
-                              "DHCPServer\0"
-                              "IPv6AcceptRA\0"
-                              "IPv6NDPProxyAddress\0"
-                              "Bridge\0"
-                              "BridgeFDB\0"
-                              "BridgeVLAN\0"
-                              "IPv6PrefixDelegation\0"
-                              "IPv6Prefix\0"
-                              "IPv6RoutePrefix\0"
-                              "TrafficControlQueueingDiscipline\0"
-                              "CAN\0"
-                              "QDisc\0"
-                              "ControlledDelay\0"
-                              "FairQueueing\0"
-                              "FairQueueingControlledDelay\0"
-                              "NetworkEmulator\0"
-                              "StochasticFairnessQueueing\0"
-                              "TokenBucketFilter\0"
-                              "TrivialLinkEqualizer\0",
-                              config_item_perf_lookup, network_network_gperf_lookup,
-                              CONFIG_PARSE_WARN, network, &dropins);
+        r = config_parse_many(
+                        filename, NETWORK_DIRS, dropin_dirname,
+                        "Match\0"
+                        "Link\0"
+                        "SR-IOV\0"
+                        "Network\0"
+                        "Address\0"
+                        "Neighbor\0"
+                        "IPv6AddressLabel\0"
+                        "RoutingPolicyRule\0"
+                        "Route\0"
+                        "NextHop\0"
+                        "DHCP\0" /* compat */
+                        "DHCPv4\0"
+                        "DHCPv6\0"
+                        "DHCPv6PrefixDelegation\0"
+                        "DHCPServer\0"
+                        "IPv6AcceptRA\0"
+                        "IPv6NDPProxyAddress\0"
+                        "Bridge\0"
+                        "BridgeFDB\0"
+                        "BridgeVLAN\0"
+                        "IPv6PrefixDelegation\0"
+                        "IPv6Prefix\0"
+                        "IPv6RoutePrefix\0"
+                        "LLDP\0"
+                        "TrafficControlQueueingDiscipline\0"
+                        "CAN\0"
+                        "QDisc\0"
+                        "BFIFO\0"
+                        "CAKE\0"
+                        "ControlledDelay\0"
+                        "DeficitRoundRobinScheduler\0"
+                        "DeficitRoundRobinSchedulerClass\0"
+                        "EnhancedTransmissionSelection\0"
+                        "FairQueueing\0"
+                        "FairQueueingControlledDelay\0"
+                        "GenericRandomEarlyDetection\0"
+                        "HeavyHitterFilter\0"
+                        "HierarchyTokenBucket\0"
+                        "HierarchyTokenBucketClass\0"
+                        "NetworkEmulator\0"
+                        "PFIFO\0"
+                        "PFIFOFast\0"
+                        "PFIFOHeadDrop\0"
+                        "PIE\0"
+                        "QuickFairQueueing\0"
+                        "QuickFairQueueingClass\0"
+                        "StochasticFairBlue\0"
+                        "StochasticFairnessQueueing\0"
+                        "TokenBucketFilter\0"
+                        "TrivialLinkEqualizer\0",
+                        config_item_perf_lookup, network_network_gperf_lookup,
+                        CONFIG_PARSE_WARN,
+                        network,
+                        &network->timestamp);
         if (r < 0)
                 return r;
 
@@ -507,24 +556,6 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi
                 log_warning_errno(r, "%s: Failed to add default route on device, ignoring: %m",
                                   network->filename);
 
-        struct stat stats;
-        if (stat(filename, &stats) >= 0)
-                network->timestamp = timespec_load(&stats.st_mtim);
-
-        char **f;
-        STRV_FOREACH(f, dropins) {
-                usec_t t;
-
-                if (stat(*f, &stats) < 0) {
-                        network->timestamp = 0;
-                        break;
-                }
-
-                t = timespec_load(&stats.st_mtim);
-                if (t > network->timestamp)
-                        network->timestamp = t;
-        }
-
         if (network_verify(network) < 0)
                 /* Ignore .network files that do not match the conditions. */
                 return 0;
@@ -636,25 +667,32 @@ static Network *network_free(Network *network) {
 
         free(network->description);
         free(network->dhcp_vendor_class_identifier);
+        free(network->dhcp_mudurl);
         strv_free(network->dhcp_user_class);
         free(network->dhcp_hostname);
-        set_free(network->dhcp_black_listed_ip);
+        set_free(network->dhcp_deny_listed_ip);
+        set_free(network->dhcp_allow_listed_ip);
         set_free(network->dhcp_request_options);
+        set_free(network->dhcp6_request_options);
         free(network->mac);
+        free(network->dhcp6_mudurl);
+        strv_free(network->dhcp6_user_class);
+        strv_free(network->dhcp6_vendor_class);
 
         if (network->dhcp_acd)
                 sd_ipv4acd_unref(network->dhcp_acd);
 
         strv_free(network->ntp);
+        for (unsigned i = 0; i < network->n_dns; i++)
+                in_addr_full_free(network->dns[i]);
         free(network->dns);
-        strv_free(network->sip);
         ordered_set_free_free(network->search_domains);
         ordered_set_free_free(network->route_domains);
         strv_free(network->bind_carrier);
 
         ordered_set_free_free(network->router_search_domains);
         free(network->router_dns);
-        set_free_free(network->ndisc_black_listed_prefix);
+        set_free_free(network->ndisc_deny_listed_prefix);
 
         free(network->bridge_name);
         free(network->bond_name);
@@ -704,7 +742,8 @@ static Network *network_free(Network *network) {
         hashmap_free(network->prefixes_by_section);
         hashmap_free(network->route_prefixes_by_section);
         hashmap_free(network->rules_by_section);
-        ordered_hashmap_free_with_destructor(network->qdiscs_by_section, qdisc_free);
+        ordered_hashmap_free_with_destructor(network->sr_iov_by_section, sr_iov_free);
+        ordered_hashmap_free_with_destructor(network->tc_by_section, traffic_control_free);
 
         if (network->manager &&
             network->manager->duids_requesting_uuid)
@@ -713,15 +752,21 @@ static Network *network_free(Network *network) {
         free(network->name);
 
         free(network->dhcp_server_timezone);
-        free(network->dhcp_server_dns);
-        free(network->dhcp_server_ntp);
-        free(network->dhcp_server_sip);
+
+        for (sd_dhcp_lease_server_type t = 0; t < _SD_DHCP_LEASE_SERVER_TYPE_MAX; t++)
+                free(network->dhcp_server_emit[t].addresses);
 
         set_free_free(network->dnssec_negative_trust_anchors);
 
+        free(network->lldp_mud);
+
         ordered_hashmap_free(network->dhcp_client_send_options);
+        ordered_hashmap_free(network->dhcp_client_send_vendor_options);
         ordered_hashmap_free(network->dhcp_server_send_options);
-        ordered_hashmap_free(network->ipv6_tokens);
+        ordered_hashmap_free(network->dhcp_server_send_vendor_options);
+        ordered_set_free(network->ipv6_tokens);
+        ordered_hashmap_free(network->dhcp6_client_send_options);
+        ordered_hashmap_free(network->dhcp6_client_send_vendor_options);
 
         return mfree(network);
 }
@@ -860,7 +905,7 @@ int config_parse_stacked_netdev(const char *unit,
                       NETDEV_KIND_XFRM));
 
         if (!ifname_valid(rvalue)) {
-                log_syntax(unit, LOG_ERR, filename, line, 0,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "Invalid netdev name in %s=, ignoring assignment: %s", lvalue, rvalue);
                 return 0;
         }
@@ -875,7 +920,7 @@ int config_parse_stacked_netdev(const char *unit,
 
         r = hashmap_put(*h, name, INT_TO_PTR(kind));
         if (r < 0)
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Cannot add NetDev '%s' to network, ignoring assignment: %m", name);
         else if (r == 0)
                 log_syntax(unit, LOG_DEBUG, filename, line, r,
@@ -898,7 +943,6 @@ int config_parse_domains(
                 void *data,
                 void *userdata) {
 
-        const char *p;
         Network *n = data;
         int r;
 
@@ -912,20 +956,21 @@ int config_parse_domains(
                 return 0;
         }
 
-        p = rvalue;
-        for (;;) {
+        for (const char *p = rvalue;;) {
                 _cleanup_free_ char *w = NULL, *normalized = NULL;
                 const char *domain;
                 bool is_route;
 
                 r = extract_first_word(&p, &w, NULL, 0);
+                if (r == -ENOMEM)
+                        return log_oom();
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
                                    "Failed to extract search or route domain, ignoring: %s", rvalue);
-                        break;
+                        return 0;
                 }
                 if (r == 0)
-                        break;
+                        return 0;
 
                 is_route = w[0] == '~';
                 domain = is_route ? w + 1 : w;
@@ -939,7 +984,7 @@ int config_parse_domains(
                 } else {
                         r = dns_name_normalize(domain, 0, &normalized);
                         if (r < 0) {
-                                log_syntax(unit, LOG_ERR, filename, line, r,
+                                log_syntax(unit, LOG_WARNING, filename, line, r,
                                            "'%s' is not a valid domain name, ignoring.", domain);
                                 continue;
                         }
@@ -947,7 +992,7 @@ int config_parse_domains(
                         domain = normalized;
 
                         if (is_localhost(domain)) {
-                                log_syntax(unit, LOG_ERR, filename, line, 0,
+                                log_syntax(unit, LOG_WARNING, filename, line, 0,
                                            "'localhost' domain may not be configured as search or route domain, ignoring assignment: %s",
                                            domain);
                                 continue;
@@ -957,14 +1002,12 @@ int config_parse_domains(
                 OrderedSet **set = is_route ? &n->route_domains : &n->search_domains;
                 r = ordered_set_ensure_allocated(set, &string_hash_ops);
                 if (r < 0)
-                        return r;
+                        return log_oom();
 
                 r = ordered_set_put_strdup(*set, domain);
                 if (r < 0)
                         return log_oom();
         }
-
-        return 0;
 }
 
 int config_parse_ipv6token(
@@ -990,19 +1033,19 @@ int config_parse_ipv6token(
 
         r = in_addr_from_string(AF_INET6, rvalue, &buffer);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to parse IPv6 token, ignoring: %s", rvalue);
                 return 0;
         }
 
         if (in_addr_is_null(AF_INET6, &buffer)) {
-                log_syntax(unit, LOG_ERR, filename, line, 0,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "IPv6 token cannot be the ANY address, ignoring: %s", rvalue);
                 return 0;
         }
 
         if ((buffer.in6.s6_addr32[0] | buffer.in6.s6_addr32[1]) != 0) {
-                log_syntax(unit, LOG_ERR, filename, line, 0,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "IPv6 token cannot be longer than 64 bits, ignoring: %s", rvalue);
                 return 0;
         }
@@ -1045,7 +1088,7 @@ int config_parse_ipv6_privacy_extensions(
                 if (streq(rvalue, "kernel"))
                         s = _IPV6_PRIVACY_EXTENSIONS_INVALID;
                 else {
-                        log_syntax(unit, LOG_ERR, filename, line, 0,
+                        log_syntax(unit, LOG_WARNING, filename, line, 0,
                                    "Failed to parse IPv6 privacy extensions option, ignoring: %s", rvalue);
                         return 0;
                 }
@@ -1081,19 +1124,19 @@ int config_parse_hostname(
                 return r;
 
         if (!hostname_is_valid(hn, false)) {
-                log_syntax(unit, LOG_ERR, filename, line, 0,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "Hostname is not valid, ignoring assignment: %s", rvalue);
                 return 0;
         }
 
         r = dns_name_is_valid(hn);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to check validity of hostname '%s', ignoring assignment: %m", rvalue);
                 return 0;
         }
         if (r == 0) {
-                log_syntax(unit, LOG_ERR, filename, line, 0,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "Hostname is not a valid DNS domain name, ignoring assignment: %s", rvalue);
                 return 0;
         }
@@ -1125,8 +1168,8 @@ int config_parse_timezone(
         if (r < 0)
                 return r;
 
-        if (!timezone_is_valid(tz, LOG_ERR)) {
-                log_syntax(unit, LOG_ERR, filename, line, 0,
+        if (!timezone_is_valid(tz, LOG_WARNING)) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "Timezone is not valid, ignoring assignment: %s", rvalue);
                 return 0;
         }
@@ -1153,43 +1196,47 @@ int config_parse_dns(
         assert(lvalue);
         assert(rvalue);
 
-        for (;;) {
+        if (isempty(rvalue)) {
+                for (unsigned i = 0; i < n->n_dns; i++)
+                        in_addr_full_free(n->dns[i]);
+                n->dns = mfree(n->dns);
+                n->n_dns = 0;
+                return 0;
+        }
+
+        for (const char *p = rvalue;;) {
+                _cleanup_(in_addr_full_freep) struct in_addr_full *dns = NULL;
                 _cleanup_free_ char *w = NULL;
-                union in_addr_union a;
-                struct in_addr_data *m;
-                int family;
+                struct in_addr_full **m;
 
-                r = extract_first_word(&rvalue, &w, NULL, 0);
+                r = extract_first_word(&p, &w, NULL, 0);
                 if (r == -ENOMEM)
                         return log_oom();
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
                                    "Invalid syntax, ignoring: %s", rvalue);
-                        break;
+                        return 0;
                 }
                 if (r == 0)
-                        break;
+                        return 0;
 
-                r = in_addr_from_string_auto(w, &family, &a);
+                r = in_addr_full_new_from_string(w, &dns);
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
                                    "Failed to parse dns server address, ignoring: %s", w);
                         continue;
                 }
 
-                m = reallocarray(n->dns, n->n_dns + 1, sizeof(struct in_addr_data));
+                if (IN_SET(dns->port, 53, 853))
+                        dns->port = 0;
+
+                m = reallocarray(n->dns, n->n_dns + 1, sizeof(struct in_addr_full*));
                 if (!m)
                         return log_oom();
 
-                m[n->n_dns++] = (struct in_addr_data) {
-                        .family = family,
-                        .address = a,
-                };
-
+                m[n->n_dns++] = TAKE_PTR(dns);
                 n->dns = m;
         }
-
-        return 0;
 }
 
 int config_parse_dnssec_negative_trust_anchors(
@@ -1204,7 +1251,6 @@ int config_parse_dnssec_negative_trust_anchors(
                 void *data,
                 void *userdata) {
 
-        const char *p = rvalue;
         Network *n = data;
         int r;
 
@@ -1217,37 +1263,31 @@ int config_parse_dnssec_negative_trust_anchors(
                 return 0;
         }
 
-        for (;;) {
+        for (const char *p = rvalue;;) {
                 _cleanup_free_ char *w = NULL;
 
                 r = extract_first_word(&p, &w, NULL, 0);
+                if (r == -ENOMEM)
+                        return log_oom();
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
                                    "Failed to extract negative trust anchor domain, ignoring: %s", rvalue);
-                        break;
+                        return 0;
                 }
                 if (r == 0)
-                        break;
+                        return 0;
 
                 r = dns_name_is_valid(w);
                 if (r <= 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
                                    "%s is not a valid domain name, ignoring.", w);
                         continue;
                 }
 
-                r = set_ensure_allocated(&n->dnssec_negative_trust_anchors, &dns_name_hash_ops);
-                if (r < 0)
-                        return log_oom();
-
-                r = set_put(n->dnssec_negative_trust_anchors, w);
+                r = set_ensure_consume(&n->dnssec_negative_trust_anchors, &dns_name_hash_ops, TAKE_PTR(w));
                 if (r < 0)
                         return log_oom();
-                if (r > 0)
-                        w = NULL;
         }
-
-        return 0;
 }
 
 int config_parse_ntp(
@@ -1274,23 +1314,23 @@ int config_parse_ntp(
                 return 0;
         }
 
-        for (;;) {
+        for (const char *p = rvalue;;) {
                 _cleanup_free_ char *w = NULL;
 
-                r = extract_first_word(&rvalue, &w, NULL, 0);
+                r = extract_first_word(&p, &w, NULL, 0);
                 if (r == -ENOMEM)
                         return log_oom();
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
                                    "Failed to extract NTP server name, ignoring: %s", rvalue);
-                        break;
+                        return 0;
                 }
                 if (r == 0)
-                        break;
+                        return 0;
 
                 r = dns_name_is_valid_or_address(w);
                 if (r <= 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
                                    "%s is not a valid domain name or IP address, ignoring.", w);
                         continue;
                 }
@@ -1299,15 +1339,13 @@ int config_parse_ntp(
                         log_syntax(unit, LOG_WARNING, filename, line, 0,
                                    "More than %u NTP servers specified, ignoring \"%s\" and any subsequent entries.",
                                    MAX_NTP_SERVERS, w);
-                        break;
+                        return 0;
                 }
 
                 r = strv_consume(l, TAKE_PTR(w));
                 if (r < 0)
                         return log_oom();
         }
-
-        return 0;
 }
 
 int config_parse_required_for_online(
@@ -1337,7 +1375,7 @@ int config_parse_required_for_online(
         if (r < 0) {
                 r = parse_boolean(rvalue);
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
                                    "Failed to parse %s= setting, ignoring assignment: %s",
                                    lvalue, rvalue);
                         return 0;
@@ -1365,3 +1403,13 @@ static const char* const keep_configuration_table[_KEEP_CONFIGURATION_MAX] = {
 };
 
 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(keep_configuration, KeepConfiguration, KEEP_CONFIGURATION_YES);
+
+static const char* const ipv6_link_local_address_gen_mode_table[_IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_MAX] = {
+        [IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_EUI64] = "eui64",
+        [IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_NONE] = "none",
+        [IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_STABLE_PRIVACY] = "stable-privacy",
+        [IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_RANDOM] = "random",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(ipv6_link_local_address_gen_mode, IPv6LinkLocalAddressGenMode);
+DEFINE_CONFIG_PARSE_ENUM(config_parse_ipv6_link_local_address_gen_mode, ipv6_link_local_address_gen_mode, IPv6LinkLocalAddressGenMode, "Failed to parse IPv6 link local address generation mode");
index 4738a18be6fab3d965fb23da13cdef438ac495a8..5dcb3c548bcc74c8ec59e82f0433c01c84bbc8c0 100644 (file)
@@ -17,6 +17,7 @@
 #include "networkd-brvlan.h"
 #include "networkd-dhcp-common.h"
 #include "networkd-dhcp4.h"
+#include "networkd-dhcp6.h"
 #include "networkd-dhcp-server.h"
 #include "networkd-fdb.h"
 #include "networkd-ipv6-proxy-ndp.h"
@@ -30,8 +31,8 @@
 #include "networkd-routing-policy-rule.h"
 #include "networkd-util.h"
 #include "ordered-set.h"
-#include "qdisc.h"
 #include "resolve-util.h"
+#include "socket-netlink.h"
 
 typedef enum IPv6PrivacyExtensions {
         /* The values map to the kernel's /proc/sys/net/ipv6/conf/xxx/use_tempaddr values */
@@ -53,8 +54,23 @@ typedef enum KeepConfiguration {
         _KEEP_CONFIGURATION_INVALID = -1,
 } KeepConfiguration;
 
+typedef enum IPv6LinkLocalAddressGenMode {
+       IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_EUI64          = IN6_ADDR_GEN_MODE_EUI64,
+       IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_NONE           = IN6_ADDR_GEN_MODE_NONE,
+       IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_STABLE_PRIVACY = IN6_ADDR_GEN_MODE_STABLE_PRIVACY,
+       IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_RANDOM         = IN6_ADDR_GEN_MODE_RANDOM,
+       _IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_MAX,
+       _IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_INVALID        = -1
+} IPv6LinkLocalAddressGenMode;
+
 typedef struct Manager Manager;
 
+typedef struct NetworkDHCPServerEmitAddress {
+        bool emit;
+        struct in_addr *addresses;
+        size_t n_addresses;
+} NetworkDHCPServerEmitAddress;
+
 struct Network {
         Manager *manager;
 
@@ -92,11 +108,14 @@ struct Network {
         AddressFamily dhcp;
         DHCPClientIdentifier dhcp_client_identifier;
         char *dhcp_vendor_class_identifier;
+        char *dhcp_mudurl;
         char **dhcp_user_class;
         char *dhcp_hostname;
         uint64_t dhcp_max_attempts;
         uint32_t dhcp_route_metric;
+        bool dhcp_route_metric_set;
         uint32_t dhcp_route_table;
+        uint32_t dhcp_fallback_lease_lifetime;
         uint32_t dhcp_route_mtu;
         uint16_t dhcp_client_port;
         int dhcp_critical;
@@ -105,11 +124,14 @@ struct Network {
         bool dhcp_send_hostname;
         bool dhcp_broadcast;
         bool dhcp_use_dns;
+        bool dhcp_use_dns_set;
         bool dhcp_routes_to_dns;
         bool dhcp_use_ntp;
+        bool dhcp_use_ntp_set;
         bool dhcp_use_sip;
         bool dhcp_use_mtu;
         bool dhcp_use_routes;
+        int dhcp_use_gateway;
         bool dhcp_use_timezone;
         bool rapid_commit;
         bool dhcp_use_hostname;
@@ -118,32 +140,34 @@ struct Network {
         bool dhcp_send_decline;
         DHCPUseDomains dhcp_use_domains;
         sd_ipv4acd *dhcp_acd;
-        Set *dhcp_black_listed_ip;
+        Set *dhcp_deny_listed_ip;
+        Set *dhcp_allow_listed_ip;
         Set *dhcp_request_options;
         OrderedHashmap *dhcp_client_send_options;
+        OrderedHashmap *dhcp_client_send_vendor_options;
         OrderedHashmap *dhcp_server_send_options;
+        OrderedHashmap *dhcp_server_send_vendor_options;
 
         /* DHCPv6 Client support*/
         bool dhcp6_use_dns;
+        bool dhcp6_use_dns_set;
         bool dhcp6_use_ntp;
+        bool dhcp6_use_ntp_set;
         uint8_t dhcp6_pd_length;
+        uint32_t dhcp6_route_metric;
+        bool dhcp6_route_metric_set;
+        char *dhcp6_mudurl;
+        char **dhcp6_user_class;
+        char **dhcp6_vendor_class;
         struct in6_addr dhcp6_pd_address;
+        DHCP6ClientStartMode dhcp6_without_ra;
+        OrderedHashmap *dhcp6_client_send_options;
+        OrderedHashmap *dhcp6_client_send_vendor_options;
+        Set *dhcp6_request_options;
 
         /* DHCP Server Support */
         bool dhcp_server;
-
-        bool dhcp_server_emit_dns;
-        struct in_addr *dhcp_server_dns;
-        unsigned n_dhcp_server_dns;
-
-        bool dhcp_server_emit_ntp;
-        struct in_addr *dhcp_server_ntp;
-        unsigned n_dhcp_server_ntp;
-
-        bool dhcp_server_emit_sip;
-        struct in_addr *dhcp_server_sip;
-        unsigned n_dhcp_server_sip;
-
+        NetworkDHCPServerEmitAddress dhcp_server_emit[_SD_DHCP_LEASE_SERVER_TYPE_MAX];
         bool dhcp_server_emit_router;
         bool dhcp_server_emit_timezone;
         char *dhcp_server_timezone;
@@ -151,8 +175,9 @@ struct Network {
         uint32_t dhcp_server_pool_offset;
         uint32_t dhcp_server_pool_size;
 
-        /* IPV4LL Support */
+        /* link local addressing support */
         AddressFamily link_local;
+        IPv6LinkLocalAddressGenMode ipv6ll_address_gen_mode;
         bool ipv4ll_route;
 
         bool default_route_on_device;
@@ -173,6 +198,11 @@ struct Network {
                                                   RA flag is set, see RFC 7084,
                                                   WPD-4 */
 
+        /* DHCPv6 Prefix Delegation support */
+        int64_t dhcp6_pd_subnet_id;
+        bool dhcp6_pd_assign;
+        union in_addr_union dhcp6_pd_token;
+
         /* Bridge Support */
         int use_bpdu;
         int hairpin;
@@ -195,13 +225,20 @@ struct Network {
         uint32_t br_untagged_bitmap[BRIDGE_VLAN_BITMAP_LEN];
 
         /* CAN support */
-        uint64_t can_bitrate;
+        uint32_t can_bitrate;
         unsigned can_sample_point;
+        uint32_t can_data_bitrate;
+        unsigned can_data_sample_point;
         usec_t can_restart_us;
         int can_triple_sampling;
+        int can_termination;
+        int can_listen_only;
+        int can_fd_mode;
+        int can_non_iso;
 
         AddressFamily ip_forward;
         bool ip_masquerade;
+        int ipv4_accept_local;
 
         int ipv6_accept_ra;
         int ipv6_dad_transmits;
@@ -215,22 +252,24 @@ struct Network {
         bool ipv6_accept_ra_use_onlink_prefix;
         bool active_slave;
         bool primary_slave;
+        bool ipv6_accept_ra_route_table_set;
         DHCPUseDomains ipv6_accept_ra_use_domains;
+        IPv6AcceptRAStartDHCP6Client ipv6_accept_ra_start_dhcp6_client;
         uint32_t ipv6_accept_ra_route_table;
-        bool ipv6_accept_ra_route_table_set;
-        Set *ndisc_black_listed_prefix;
-        OrderedHashmap *ipv6_tokens;
+        Set *ndisc_deny_listed_prefix;
+        OrderedSet *ipv6_tokens;
 
         IPv6PrivacyExtensions ipv6_privacy_extensions;
 
         struct ether_addr *mac;
         uint32_t mtu;
+        uint32_t group;
         int arp;
         int multicast;
         int allmulticast;
         bool unmanaged;
         bool configure_without_carrier;
-        bool ignore_carrier_loss;
+        int ignore_carrier_loss;
         KeepConfiguration keep_configuration;
         uint32_t iaid;
         DUID duid;
@@ -240,8 +279,10 @@ struct Network {
         bool required_for_online; /* Is this network required to be considered online? */
         LinkOperationalStateRange required_operstate_for_online;
 
+        /* LLDP support */
         LLDPMode lldp_mode; /* LLDP reception */
         LLDPEmit lldp_emit; /* LLDP transmission */
+        char *lldp_mud;    /* LLDP MUD URL */
 
         LIST_HEAD(Address, static_addresses);
         LIST_HEAD(Route, static_routes);
@@ -274,10 +315,11 @@ struct Network {
         Hashmap *prefixes_by_section;
         Hashmap *route_prefixes_by_section;
         Hashmap *rules_by_section;
-        OrderedHashmap *qdiscs_by_section;
+        OrderedHashmap *tc_by_section;
+        OrderedHashmap *sr_iov_by_section;
 
         /* All kinds of DNS configuration */
-        struct in_addr_data *dns;
+        struct in_addr_full **dns;
         unsigned n_dns;
         OrderedSet *search_domains, *route_domains;
 
@@ -289,7 +331,6 @@ struct Network {
         Set *dnssec_negative_trust_anchors;
 
         char **ntp;
-        char **sip;
         char **bind_carrier;
 };
 
@@ -325,6 +366,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_dnssec_negative_trust_anchors);
 CONFIG_PARSER_PROTOTYPE(config_parse_ntp);
 CONFIG_PARSER_PROTOTYPE(config_parse_required_for_online);
 CONFIG_PARSER_PROTOTYPE(config_parse_keep_configuration);
+CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_link_local_address_gen_mode);
 
 const struct ConfigPerfItem* network_network_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
 
@@ -333,3 +375,6 @@ IPv6PrivacyExtensions ipv6_privacy_extensions_from_string(const char *s) _pure_;
 
 const char* keep_configuration_to_string(KeepConfiguration i) _const_;
 KeepConfiguration keep_configuration_from_string(const char *s) _pure_;
+
+const char* ipv6_link_local_address_gen_mode_to_string(IPv6LinkLocalAddressGenMode s) _const_;
+IPv6LinkLocalAddressGenMode ipv6_link_local_address_gen_mode_from_string(const char *s) _pure_;
index 45c13ca88f3245e4caeef569c84f9e8ae5ac3515..6d89be1a25d55a48f48d682959289b9a9c26967d 100644 (file)
@@ -209,11 +209,7 @@ static int nexthop_add_internal(Link *link, Set **nexthops, NextHop *in, NextHop
         nexthop->family = in->family;
         nexthop->gw = in->gw;
 
-        r = set_ensure_allocated(nexthops, &nexthop_hash_ops);
-        if (r < 0)
-                return r;
-
-        r = set_put(*nexthops, nexthop);
+        r = set_ensure_put(nexthops, &nexthop_hash_ops, nexthop);
         if (r < 0)
                 return r;
         if (r == 0)
@@ -245,11 +241,7 @@ int nexthop_add(Link *link, NextHop *in, NextHop **ret) {
                         return r;
         } else if (r == 0) {
                 /* Take over a foreign nexthop */
-                r = set_ensure_allocated(&link->nexthops, &nexthop_hash_ops);
-                if (r < 0)
-                        return r;
-
-                r = set_put(link->nexthops, nexthop);
+                r = set_ensure_put(&link->nexthops, &nexthop_hash_ops, nexthop);
                 if (r < 0)
                         return r;
 
@@ -422,11 +414,11 @@ int config_parse_nexthop_id(
 
         r = nexthop_new_static(network, filename, section_line, &n);
         if (r < 0)
-                return r;
+                return log_oom();
 
         r = safe_atou32(rvalue, &n->id);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Could not parse nexthop id \"%s\", ignoring assignment: %m", rvalue);
                 return 0;
         }
@@ -459,11 +451,11 @@ int config_parse_nexthop_gateway(
 
         r = nexthop_new_static(network, filename, section_line, &n);
         if (r < 0)
-                return r;
+                return log_oom();
 
         r = in_addr_from_string_auto(rvalue, &n->family, &n->gw);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
                 return 0;
         }
index 620939e251839214dc1ffc2bf81fd4640678ce4b..e0c490babab6a1e6798f062d6bf0876912ca9b4c 100644 (file)
@@ -211,18 +211,19 @@ int config_parse_prefix(const char *unit,
 
         r = prefix_new_static(network, filename, section_line, &p);
         if (r < 0)
-                return r;
+                return log_oom();
 
         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);
+                log_syntax(unit, LOG_WARNING, 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);
+        r = sd_radv_prefix_set_prefix(p->radv_prefix, &in6addr.in6, prefixlen);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to set radv prefix, ignoring assignment: %s", rvalue);
+                return 0;
+        }
 
         p = NULL;
 
@@ -241,7 +242,7 @@ int config_parse_prefix_flags(const char *unit,
                               void *userdata) {
         Network *network = userdata;
         _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL;
-        int r, val;
+        int r;
 
         assert(filename);
         assert(section);
@@ -251,22 +252,22 @@ int config_parse_prefix_flags(const char *unit,
 
         r = prefix_new_static(network, filename, section_line, &p);
         if (r < 0)
-                return r;
+                return log_oom();
 
         r = parse_boolean(rvalue);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address flag, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue);
                 return 0;
         }
 
-        val = r;
-
         if (streq(lvalue, "OnLink"))
-                r = sd_radv_prefix_set_onlink(p->radv_prefix, val);
+                r = sd_radv_prefix_set_onlink(p->radv_prefix, r);
         else if (streq(lvalue, "AddressAutoconfiguration"))
-                r = sd_radv_prefix_set_address_autoconfiguration(p->radv_prefix, val);
-        if (r < 0)
-                return r;
+                r = sd_radv_prefix_set_address_autoconfiguration(p->radv_prefix, r);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to set %s=, ignoring assignment: %m", lvalue);
+                return 0;
+        }
 
         p = NULL;
 
@@ -296,11 +297,11 @@ int config_parse_prefix_lifetime(const char *unit,
 
         r = prefix_new_static(network, filename, section_line, &p);
         if (r < 0)
-                return r;
+                return log_oom();
 
         r = parse_sec(rvalue, &usec);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "Lifetime is invalid, ignoring assignment: %s", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Lifetime is invalid, ignoring assignment: %s", rvalue);
                 return 0;
         }
 
@@ -311,9 +312,51 @@ int config_parse_prefix_lifetime(const char *unit,
         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) {
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to set %s=, ignoring assignment: %m", lvalue);
+                return 0;
+        }
+
+        p = NULL;
+
+        return 0;
+}
+
+int config_parse_prefix_assign(
+                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_or_set_invalidp) Prefix *p = NULL;
+        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;
+                return log_oom();
+
+        r = parse_boolean(rvalue);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse %s=, ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
+        }
 
+        p->assign = r;
         p = NULL;
 
         return 0;
@@ -344,18 +387,19 @@ int config_parse_route_prefix(const char *unit,
 
         r = route_prefix_new_static(network, filename, section_line, &p);
         if (r < 0)
-                return r;
+                return log_oom();
 
         r = in_addr_prefix_from_string(rvalue, AF_INET6, &in6addr, &prefixlen);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "Route prefix is invalid, ignoring assignment: %s", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Route prefix is invalid, ignoring assignment: %s", rvalue);
                 return 0;
         }
 
-        if (sd_radv_prefix_set_route_prefix(p->radv_route_prefix, &in6addr.in6, prefixlen) < 0)
-                return -EADDRNOTAVAIL;
-
-        log_syntax(unit, LOG_INFO, filename, line, r, "Found route prefix %s", rvalue);
+        r = sd_radv_prefix_set_route_prefix(p->radv_route_prefix, &in6addr.in6, prefixlen);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to set route prefix, ignoring assignment: %m");
+                return 0;
+        }
 
         p = NULL;
 
@@ -385,19 +429,22 @@ int config_parse_route_prefix_lifetime(const char *unit,
 
         r = route_prefix_new_static(network, filename, section_line, &p);
         if (r < 0)
-                return r;
+                return log_oom();
 
         r = parse_sec(rvalue, &usec);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Route lifetime is invalid, ignoring assignment: %s", rvalue);
                 return 0;
         }
 
         /* a value of 0xffffffff represents infinity */
         r = sd_radv_route_prefix_set_lifetime(p->radv_route_prefix, DIV_ROUND_UP(usec, USEC_PER_SEC));
-        if (r < 0)
-                return r;
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to set route lifetime, ignoring assignment: %m");
+                return 0;
+        }
 
         p = NULL;
 
@@ -416,10 +463,10 @@ static int radv_get_ip6dns(Network *network, struct in6_addr **dns,
         for (i = 0; i < network->n_dns; i++) {
                 union in_addr_union *addr;
 
-                if (network->dns[i].family != AF_INET6)
+                if (network->dns[i]->family != AF_INET6)
                         continue;
 
-                addr = &network->dns[i].address;
+                addr = &network->dns[i]->address;
 
                 if (in_addr_is_null(AF_INET6, addr) ||
                     in_addr_is_link_local(AF_INET6, addr) ||
@@ -603,10 +650,7 @@ int radv_configure(Link *link) {
                         return r;
         }
 
-        if (IN_SET(link->network->router_prefix_delegation,
-                   RADV_PREFIX_DELEGATION_STATIC,
-                   RADV_PREFIX_DELEGATION_BOTH)) {
-
+        if (link->network->router_prefix_delegation & RADV_PREFIX_DELEGATION_STATIC) {
                 LIST_FOREACH(prefixes, p, link->network->static_prefixes) {
                         r = sd_radv_add_prefix(link->radv, p->radv_prefix, false);
                         if (r == -EEXIST)
@@ -626,12 +670,42 @@ int radv_configure(Link *link) {
                         if (r < 0)
                                 return r;
                 }
-
         }
 
         return 0;
 }
 
+int radv_add_prefix(Link *link, const struct in6_addr *prefix, uint8_t prefix_len,
+                    uint32_t lifetime_preferred, uint32_t lifetime_valid) {
+        _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
+        int r;
+
+        assert(link);
+        assert(link->radv);
+
+        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_add_prefix(link->radv, p, true);
+        if (r < 0 && r != -EEXIST)
+                return r;
+
+        return 0;
+}
+
 int config_parse_radv_dns(
                 const char *unit,
                 const char *filename,
@@ -645,14 +719,13 @@ int config_parse_radv_dns(
                 void *userdata) {
 
         Network *n = data;
-        const char *p = rvalue;
         int r;
 
         assert(filename);
         assert(lvalue);
         assert(rvalue);
 
-        for (;;) {
+        for (const char *p = rvalue;;) {
                 _cleanup_free_ char *w = NULL;
                 union in_addr_union a;
 
@@ -660,25 +733,25 @@ int config_parse_radv_dns(
                 if (r == -ENOMEM)
                         return log_oom();
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
                                    "Failed to extract word, ignoring: %s", rvalue);
                         return 0;
                 }
                 if (r == 0)
-                        break;
+                        return 0;
 
                 if (streq(w, "_link_local"))
                         a = IN_ADDR_NULL;
                 else {
                         r = in_addr_from_string(AF_INET6, w, &a);
                         if (r < 0) {
-                                log_syntax(unit, LOG_ERR, filename, line, r,
+                                log_syntax(unit, LOG_WARNING, filename, line, r,
                                            "Failed to parse DNS server address, ignoring: %s", w);
                                 continue;
                         }
 
                         if (in_addr_is_null(AF_INET6, &a)) {
-                                log_syntax(unit, LOG_ERR, filename, line, 0,
+                                log_syntax(unit, LOG_WARNING, filename, line, 0,
                                            "DNS server address is null, ignoring: %s", w);
                                 continue;
                         }
@@ -692,8 +765,6 @@ int config_parse_radv_dns(
                 m[n->n_router_dns++] = a.in6;
                 n->router_dns = m;
         }
-
-        return 0;
 }
 
 int config_parse_radv_search_domains(
@@ -709,30 +780,29 @@ int config_parse_radv_search_domains(
                 void *userdata) {
 
         Network *n = data;
-        const char *p = rvalue;
         int r;
 
         assert(filename);
         assert(lvalue);
         assert(rvalue);
 
-        for (;;) {
+        for (const char *p = rvalue;;) {
                 _cleanup_free_ char *w = NULL, *idna = NULL;
 
                 r = extract_first_word(&p, &w, NULL, 0);
                 if (r == -ENOMEM)
                         return log_oom();
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
                                    "Failed to extract word, ignoring: %s", rvalue);
                         return 0;
                 }
                 if (r == 0)
-                        break;
+                        return 0;
 
                 r = dns_name_apply_idna(w, &idna);
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
                                    "Failed to apply IDNA to domain name '%s', ignoring: %m", w);
                         continue;
                 } else if (r == 0)
@@ -741,14 +811,12 @@ int config_parse_radv_search_domains(
 
                 r = ordered_set_ensure_allocated(&n->router_search_domains, &string_hash_ops);
                 if (r < 0)
-                        return r;
+                        return log_oom();
 
                 r = ordered_set_consume(n->router_search_domains, TAKE_PTR(idna));
                 if (r < 0)
-                        return r;
+                        return log_oom();
         }
-
-        return 0;
 }
 
 static const char * const radv_prefix_delegation_table[_RADV_PREFIX_DELEGATION_MAX] = {
@@ -763,37 +831,10 @@ DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(
                 RADVPrefixDelegation,
                 RADV_PREFIX_DELEGATION_BOTH);
 
-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;
-        RADVPrefixDelegation d;
-
-        assert(filename);
-        assert(section);
-        assert(lvalue);
-        assert(rvalue);
-        assert(data);
-
-        d = radv_prefix_delegation_from_string(rvalue);
-        if (d < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, -EINVAL, "Invalid router prefix delegation '%s', ignoring assignment.", rvalue);
-                return 0;
-        }
-
-        network->router_prefix_delegation = d;
-
-        return 0;
-}
+DEFINE_CONFIG_PARSE_ENUM(config_parse_router_prefix_delegation,
+                         radv_prefix_delegation,
+                         RADVPrefixDelegation,
+                         "Invalid router prefix delegation");
 
 int config_parse_router_preference(const char *unit,
                                    const char *filename,
@@ -820,7 +861,8 @@ int config_parse_router_preference(const char *unit,
         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);
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "Invalid router preference, ignoring assignment: %s", rvalue);
 
         return 0;
 }
index 21b323e83e2b3574963c3f148bffec0a67ee22c2..496ef97adcf6cca844e2a0b821138d39e78ae243 100644 (file)
@@ -14,10 +14,10 @@ typedef struct Prefix Prefix;
 typedef struct RoutePrefix RoutePrefix;
 
 typedef enum RADVPrefixDelegation {
-        RADV_PREFIX_DELEGATION_NONE,
-        RADV_PREFIX_DELEGATION_STATIC,
-        RADV_PREFIX_DELEGATION_DHCP6,
-        RADV_PREFIX_DELEGATION_BOTH,
+        RADV_PREFIX_DELEGATION_NONE   = 0,
+        RADV_PREFIX_DELEGATION_STATIC = 1 << 0,
+        RADV_PREFIX_DELEGATION_DHCP6  = 1 << 1,
+        RADV_PREFIX_DELEGATION_BOTH   = RADV_PREFIX_DELEGATION_STATIC | RADV_PREFIX_DELEGATION_DHCP6,
         _RADV_PREFIX_DELEGATION_MAX,
         _RADV_PREFIX_DELEGATION_INVALID = -1,
 } RADVPrefixDelegation;
@@ -28,6 +28,8 @@ struct Prefix {
 
         sd_radv_prefix *radv_prefix;
 
+        bool assign;
+
         LIST_FIELDS(Prefix, prefixes);
 };
 
@@ -50,6 +52,8 @@ DEFINE_NETWORK_SECTION_FUNCTIONS(RoutePrefix, route_prefix_free);
 
 int radv_emit_dns(Link *link);
 int radv_configure(Link *link);
+int radv_add_prefix(Link *link, const struct in6_addr *prefix, uint8_t prefix_len,
+                    uint32_t lifetime_preferred, uint32_t lifetime_valid);
 
 const char* radv_prefix_delegation_to_string(RADVPrefixDelegation i) _const_;
 RADVPrefixDelegation radv_prefix_delegation_from_string(const char *s) _pure_;
@@ -59,6 +63,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_router_preference);
 CONFIG_PARSER_PROTOTYPE(config_parse_prefix);
 CONFIG_PARSER_PROTOTYPE(config_parse_prefix_flags);
 CONFIG_PARSER_PROTOTYPE(config_parse_prefix_lifetime);
+CONFIG_PARSER_PROTOTYPE(config_parse_prefix_assign);
 CONFIG_PARSER_PROTOTYPE(config_parse_radv_dns);
 CONFIG_PARSER_PROTOTYPE(config_parse_radv_search_domains);
 CONFIG_PARSER_PROTOTYPE(config_parse_route_prefix);
index de532eee7aa128bf7ff840512914694cc3e222f6..541bf1e793ea5c70fa9dc9f63578ee0012c8741e 100644 (file)
@@ -15,6 +15,7 @@
 #include "socket-netlink.h"
 #include "string-table.h"
 #include "string-util.h"
+#include "strv.h"
 #include "strxcpyx.h"
 #include "sysctl-util.h"
 #include "vrf.h"
@@ -143,6 +144,14 @@ void route_free(Route *route) {
         if (route->link) {
                 set_remove(route->link->routes, route);
                 set_remove(route->link->routes_foreign, route);
+                set_remove(route->link->dhcp_routes, route);
+                set_remove(route->link->dhcp_routes_old, route);
+                set_remove(route->link->dhcp6_routes, route);
+                set_remove(route->link->dhcp6_routes_old, route);
+                set_remove(route->link->dhcp6_pd_routes, route);
+                set_remove(route->link->dhcp6_pd_routes_old, route);
+                set_remove(route->link->ndisc_routes, route);
+                set_remove(route->link->ndisc_routes_old, route);
         }
 
         ordered_set_free_free(route->multipath_routes);
@@ -331,11 +340,7 @@ static int route_add_internal(Link *link, Set **routes, Route *in, Route **ret)
         route->initrwnd = in->initrwnd;
         route->lifetime = in->lifetime;
 
-        r = set_ensure_allocated(routes, &route_hash_ops);
-        if (r < 0)
-                return r;
-
-        r = set_put(*routes, route);
+        r = set_ensure_put(routes, &route_hash_ops, route);
         if (r < 0)
                 return r;
         if (r == 0)
@@ -368,11 +373,7 @@ int route_add(Link *link, Route *in, Route **ret) {
                         return r;
         } else if (r == 0) {
                 /* Take over a foreign route */
-                r = set_ensure_allocated(&link->routes, &route_hash_ops);
-                if (r < 0)
-                        return r;
-
-                r = set_put(link->routes, route);
+                r = set_ensure_put(&link->routes, &route_hash_ops, route);
                 if (r < 0)
                         return r;
 
@@ -604,7 +605,8 @@ static int append_nexthops(Route *route, sd_netlink_message *req) {
 int route_configure(
                 Route *route,
                 Link *link,
-                link_netlink_message_handler_t callback) {
+                link_netlink_message_handler_t callback,
+                Route **ret) {
 
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
         _cleanup_(sd_event_source_unrefp) sd_event_source *expire = NULL;
@@ -810,6 +812,9 @@ int route_configure(
         sd_event_source_unref(route->expire);
         route->expire = TAKE_PTR(expire);
 
+        if (ret)
+                *ret = route;
+
         return 1;
 }
 
@@ -999,12 +1004,22 @@ int config_parse_gateway(
                 /* we are not in an Route section, so treat
                  * this as the special '0' section */
                 r = route_new_static(network, NULL, 0, &n);
-                if (r < 0)
-                        return r;
+                if (r == -ENOMEM)
+                        return log_oom();
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to allocate route, ignoring assignment: %m");
+                        return 0;
+                }
         } else {
                 r = route_new_static(network, filename, section_line, &n);
-                if (r < 0)
-                        return r;
+                if (r == -ENOMEM)
+                        return log_oom();
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to allocate route, ignoring assignment: %m");
+                        return 0;
+                }
 
                 if (streq(rvalue, "_dhcp")) {
                         n->gateway_from_dhcp = true;
@@ -1018,7 +1033,7 @@ int config_parse_gateway(
         else
                 r = in_addr_from_string(n->family, rvalue, &n->gw);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
                 return 0;
         }
@@ -1050,15 +1065,20 @@ int config_parse_preferred_src(
         assert(data);
 
         r = route_new_static(network, filename, section_line, &n);
-        if (r < 0)
-                return r;
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to allocate route, ignoring assignment: %m");
+                return 0;
+        }
 
         if (n->family == AF_UNSPEC)
                 r = in_addr_from_string_auto(rvalue, &n->family, &n->prefsrc);
         else
                 r = in_addr_from_string(n->family, rvalue, &n->prefsrc);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+                log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
                            "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
                 return 0;
         }
@@ -1092,8 +1112,13 @@ int config_parse_destination(
         assert(data);
 
         r = route_new_static(network, filename, section_line, &n);
-        if (r < 0)
-                return r;
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to allocate route, ignoring assignment: %m");
+                return 0;
+        }
 
         if (streq(lvalue, "Destination")) {
                 buffer = &n->dst;
@@ -1109,7 +1134,7 @@ int config_parse_destination(
         else
                 r = in_addr_prefix_from_string(rvalue, n->family, buffer, prefixlen);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+                log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
                            "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue);
                 return 0;
         }
@@ -1141,12 +1166,17 @@ int config_parse_route_priority(
         assert(data);
 
         r = route_new_static(network, filename, section_line, &n);
-        if (r < 0)
-                return r;
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to allocate route, ignoring assignment: %m");
+                return 0;
+        }
 
         r = safe_atou32(rvalue, &n->priority);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Could not parse route priority \"%s\", ignoring assignment: %m", rvalue);
                 return 0;
         }
@@ -1178,12 +1208,17 @@ int config_parse_route_scope(
         assert(data);
 
         r = route_new_static(network, filename, section_line, &n);
-        if (r < 0)
-                return r;
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to allocate route, ignoring assignment: %m");
+                return 0;
+        }
 
         r = route_scope_from_string(rvalue);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, 0, "Unknown route scope: %s", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown route scope: %s", rvalue);
                 return 0;
         }
 
@@ -1216,8 +1251,13 @@ int config_parse_route_table(
         assert(data);
 
         r = route_new_static(network, filename, section_line, &n);
-        if (r < 0)
-                return r;
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to allocate route, ignoring assignment: %m");
+                return 0;
+        }
 
         r = route_table_from_string(rvalue);
         if (r >= 0)
@@ -1225,7 +1265,7 @@ int config_parse_route_table(
         else {
                 r = safe_atou32(rvalue, &n->table);
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
                                    "Could not parse route table number \"%s\", ignoring assignment: %m", rvalue);
                         return 0;
                 }
@@ -1236,7 +1276,7 @@ int config_parse_route_table(
         return 0;
 }
 
-int config_parse_gateway_onlink(
+int config_parse_route_boolean(
                 const char *unit,
                 const char *filename,
                 unsigned line,
@@ -1259,17 +1299,31 @@ int config_parse_gateway_onlink(
         assert(data);
 
         r = route_new_static(network, filename, section_line, &n);
-        if (r < 0)
-                return r;
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to allocate route, ignoring assignment: %m");
+                return 0;
+        }
 
         r = parse_boolean(rvalue);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Could not parse %s=\"%s\", ignoring assignment: %m", lvalue, rvalue);
                 return 0;
         }
 
-        n->gateway_onlink = r;
+        if (STR_IN_SET(lvalue, "GatewayOnLink", "GatewayOnlink"))
+                n->gateway_onlink = r;
+        else if (streq(lvalue, "QuickAck"))
+                n->quickack = r;
+        else if (streq(lvalue, "FastOpenNoCookie"))
+                n->fast_open_no_cookie = r;
+        else if (streq(lvalue, "TTLPropagate"))
+                n->ttl_propagate = r;
+        else
+                assert_not_reached("Invalid lvalue");
 
         TAKE_PTR(n);
         return 0;
@@ -1292,8 +1346,13 @@ int config_parse_ipv6_route_preference(
         int r;
 
         r = route_new_static(network, filename, section_line, &n);
-        if (r < 0)
-                return r;
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to allocate route, ignoring assignment: %m");
+                return 0;
+        }
 
         if (streq(rvalue, "low"))
                 n->pref = ICMPV6_ROUTER_PREF_LOW;
@@ -1302,7 +1361,7 @@ int config_parse_ipv6_route_preference(
         else if (streq(rvalue, "high"))
                 n->pref = ICMPV6_ROUTER_PREF_HIGH;
         else {
-                log_syntax(unit, LOG_ERR, filename, line, 0, "Unknown route preference: %s", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown route preference: %s", rvalue);
                 return 0;
         }
 
@@ -1327,8 +1386,13 @@ int config_parse_route_protocol(
         int r;
 
         r = route_new_static(network, filename, section_line, &n);
-        if (r < 0)
-                return r;
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to allocate route, ignoring assignment: %m");
+                return 0;
+        }
 
         r = route_protocol_from_string(rvalue);
         if (r >= 0)
@@ -1336,7 +1400,7 @@ int config_parse_route_protocol(
         else {
                 r = safe_atou8(rvalue , &n->protocol);
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
                                    "Could not parse route protocol \"%s\", ignoring assignment: %m", rvalue);
                         return 0;
                 }
@@ -1363,12 +1427,17 @@ int config_parse_route_type(
         int t, r;
 
         r = route_new_static(network, filename, section_line, &n);
-        if (r < 0)
-                return r;
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to allocate route, ignoring assignment: %m");
+                return 0;
+        }
 
         t = route_type_from_string(rvalue);
         if (t < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, 0,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "Could not parse route type \"%s\", ignoring assignment: %m", rvalue);
                 return 0;
         }
@@ -1403,17 +1472,22 @@ int config_parse_tcp_window(
         assert(data);
 
         r = route_new_static(network, filename, section_line, &n);
-        if (r < 0)
-                return r;
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to allocate route, ignoring assignment: %m");
+                return 0;
+        }
 
         r = parse_size(rvalue, 1024, &k);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Could not parse TCP %s \"%s\", ignoring assignment: %m", lvalue, rvalue);
                 return 0;
         }
         if (k > UINT32_MAX) {
-                log_syntax(unit, LOG_ERR, filename, line, 0,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "Specified TCP %s \"%s\" is too large, ignoring assignment: %m", lvalue, rvalue);
                 return 0;
         }
@@ -1429,82 +1503,6 @@ int config_parse_tcp_window(
         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_or_set_invalidp) 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;
-        TAKE_PTR(n);
-        return 0;
-}
-
-int config_parse_fast_open_no_cookie(
-                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_or_set_invalidp) 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 fastopen no cookie, ignoring: %s", rvalue);
-                return 0;
-        }
-
-        n->fast_open_no_cookie = k;
-        TAKE_PTR(n);
-        return 0;
-}
-
 int config_parse_route_mtu(
                 const char *unit,
                 const char *filename,
@@ -1528,8 +1526,13 @@ int config_parse_route_mtu(
         assert(data);
 
         r = route_new_static(network, filename, section_line, &n);
-        if (r < 0)
-                return r;
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to allocate route, ignoring assignment: %m");
+                return 0;
+        }
 
         r = config_parse_mtu(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &n->mtu, userdata);
         if (r < 0)
@@ -1539,45 +1542,6 @@ int config_parse_route_mtu(
         return 0;
 }
 
-int config_parse_route_ttl_propagate(
-                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_(route_free_or_set_invalidp) Route *n = NULL;
-        int r, k;
-
-        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 TTLPropagate= value, ignoring: %s", rvalue);
-                return 0;
-        }
-
-        n->ttl_propagate = k;
-
-        TAKE_PTR(n);
-        return 0;
-}
-
 int config_parse_multipath_route(
                 const char *unit,
                 const char *filename,
@@ -1605,8 +1569,13 @@ int config_parse_multipath_route(
         assert(data);
 
         r = route_new_static(network, filename, section_line, &n);
-        if (r < 0)
-                return r;
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to allocate route, ignoring assignment: %m");
+                return 0;
+        }
 
         if (isempty(rvalue)) {
                 n->multipath_routes = ordered_set_free_free(n->multipath_routes);
@@ -1622,7 +1591,7 @@ int config_parse_multipath_route(
         if (r == -ENOMEM)
                 return log_oom();
         if (r <= 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Invalid multipath route option, ignoring assignment: %s", rvalue);
                 return 0;
         }
@@ -1639,7 +1608,7 @@ int config_parse_multipath_route(
 
         r = in_addr_from_string_auto(ip, &family, &a);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Invalid multipath route gateway '%s', ignoring assignment: %m", rvalue);
                 return 0;
         }
@@ -1649,7 +1618,7 @@ int config_parse_multipath_route(
         if (dev) {
                 r = resolve_interface(NULL, dev);
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
                                    "Invalid interface name or index, ignoring assignment: %s", dev);
                         return 0;
                 }
@@ -1659,12 +1628,12 @@ int config_parse_multipath_route(
         if (!isempty(p)) {
                 r = safe_atou32(p, &m->weight);
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
                                    "Invalid multipath route weight, ignoring assignment: %s", p);
                         return 0;
                 }
                 if (m->weight == 0 || m->weight > 256) {
-                        log_syntax(unit, LOG_ERR, filename, line, 0,
+                        log_syntax(unit, LOG_WARNING, filename, line, 0,
                                    "Invalid multipath route weight, ignoring assignment: %s", p);
                         return 0;
                 }
@@ -1676,7 +1645,7 @@ int config_parse_multipath_route(
 
         r = ordered_set_put(n->multipath_routes, m);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to store multipath route, ignoring assignment: %m");
                 return 0;
         }
index 067c65f2f78217eb387d953526525ee2b8785e35..3beee9b03b159aa023f9bf14e8a68cb162b618a9 100644 (file)
@@ -66,7 +66,7 @@ extern const struct hash_ops route_hash_ops;
 
 int route_new(Route **ret);
 void route_free(Route *route);
-int route_configure(Route *route, Link *link, link_netlink_message_handler_t callback);
+int route_configure(Route *route, Link *link, link_netlink_message_handler_t callback, Route **ret);
 int route_remove(Route *route, Link *link, link_netlink_message_handler_t callback);
 
 int route_get(Link *link, Route *in, Route **ret);
@@ -100,13 +100,10 @@ CONFIG_PARSER_PROTOTYPE(config_parse_destination);
 CONFIG_PARSER_PROTOTYPE(config_parse_route_priority);
 CONFIG_PARSER_PROTOTYPE(config_parse_route_scope);
 CONFIG_PARSER_PROTOTYPE(config_parse_route_table);
-CONFIG_PARSER_PROTOTYPE(config_parse_gateway_onlink);
+CONFIG_PARSER_PROTOTYPE(config_parse_route_boolean);
 CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_route_preference);
 CONFIG_PARSER_PROTOTYPE(config_parse_route_protocol);
 CONFIG_PARSER_PROTOTYPE(config_parse_route_type);
 CONFIG_PARSER_PROTOTYPE(config_parse_tcp_window);
-CONFIG_PARSER_PROTOTYPE(config_parse_quickack);
-CONFIG_PARSER_PROTOTYPE(config_parse_fast_open_no_cookie);
-CONFIG_PARSER_PROTOTYPE(config_parse_route_ttl_propagate);
 CONFIG_PARSER_PROTOTYPE(config_parse_route_mtu);
 CONFIG_PARSER_PROTOTYPE(config_parse_multipath_route);
index 641f8840845c66d76c8468df0d810f9d25f1808e..36dad527d0969e501c03fc6a1b3debb9543d4606 100644 (file)
@@ -132,11 +132,8 @@ static void routing_policy_rule_hash_func(const RoutingPolicyRule *rule, struct
                 siphash24_compress(&rule->dport, sizeof(rule->dport), state);
                 siphash24_compress(&rule->uid_range, sizeof(rule->uid_range), state);
 
-                if (rule->iif)
-                        siphash24_compress(rule->iif, strlen(rule->iif), state);
-
-                if (rule->oif)
-                        siphash24_compress(rule->oif, strlen(rule->oif), state);
+                siphash24_compress_string(rule->iif, state);
+                siphash24_compress_string(rule->oif, state);
 
                 break;
         default:
@@ -230,7 +227,12 @@ static int routing_policy_rule_compare_func(const RoutingPolicyRule *a, const Ro
         }
 }
 
-DEFINE_PRIVATE_HASH_OPS(routing_policy_rule_hash_ops, RoutingPolicyRule, routing_policy_rule_hash_func, routing_policy_rule_compare_func);
+DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
+                routing_policy_rule_hash_ops,
+                RoutingPolicyRule,
+                routing_policy_rule_hash_func,
+                routing_policy_rule_compare_func,
+                routing_policy_rule_free);
 
 int routing_policy_rule_get(Manager *m, RoutingPolicyRule *rule, RoutingPolicyRule **ret) {
 
@@ -263,11 +265,7 @@ int routing_policy_rule_make_local(Manager *m, RoutingPolicyRule *rule) {
         if (set_contains(m->rules_foreign, rule)) {
                 set_remove(m->rules_foreign, rule);
 
-                r = set_ensure_allocated(&m->rules, &routing_policy_rule_hash_ops);
-                if (r < 0)
-                        return r;
-
-                r = set_put(m->rules, rule);
+                r = set_ensure_put(&m->rules, &routing_policy_rule_hash_ops, rule);
                 if (r < 0)
                         return r;
                 if (r == 0)
@@ -295,11 +293,7 @@ static int routing_policy_rule_add_internal(Manager *m, Set **rules, RoutingPoli
         if (r < 0)
                 return r;
 
-        r = set_ensure_allocated(rules, &routing_policy_rule_hash_ops);
-        if (r < 0)
-                return r;
-
-        r = set_put(*rules, rule);
+        r = set_ensure_put(rules, &routing_policy_rule_hash_ops, rule);
         if (r < 0)
                 return r;
         if (r == 0)
@@ -514,17 +508,17 @@ int routing_policy_rule_configure(RoutingPolicyRule *rule, Link *link, link_netl
         if (rule->tos > 0) {
                 r = sd_rtnl_message_routing_policy_rule_set_tos(m, rule->tos);
                 if (r < 0)
-                        return log_link_error_errno(link, r, "Could not set ip rule tos: %m");
+                        return log_link_error_errno(link, r, "Could not set IP rule TOS: %m");
         }
 
         if (rule->table < 256) {
                 r = sd_rtnl_message_routing_policy_rule_set_table(m, rule->table);
                 if (r < 0)
-                        return log_link_error_errno(link, r, "Could not set ip rule table: %m");
+                        return log_link_error_errno(link, r, "Could not set IP rule table: %m");
         } else {
                 r = sd_rtnl_message_routing_policy_rule_set_table(m, RT_TABLE_UNSPEC);
                 if (r < 0)
-                        return log_link_error_errno(link, r, "Could not set ip rule table: %m");
+                        return log_link_error_errno(link, r, "Could not set IP rule table: %m");
 
                 r = sd_netlink_message_append_u32(m, FRA_TABLE, rule->table);
                 if (r < 0)
@@ -702,11 +696,11 @@ int config_parse_routing_policy_rule_tos(
 
         r = routing_policy_rule_new_static(network, filename, section_line, &n);
         if (r < 0)
-                return r;
+                return log_oom();
 
         r = safe_atou8(rvalue, &n->tos);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse RPDB rule tos, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse RPDB rule TOS, ignoring: %s", rvalue);
                 return 0;
         }
 
@@ -739,11 +733,11 @@ int config_parse_routing_policy_rule_priority(
 
         r = routing_policy_rule_new_static(network, filename, section_line, &n);
         if (r < 0)
-                return r;
+                return log_oom();
 
         r = safe_atou32(rvalue, &n->priority);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse RPDB rule priority, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse RPDB rule priority, ignoring: %s", rvalue);
                 return 0;
         }
 
@@ -776,11 +770,11 @@ int config_parse_routing_policy_rule_table(
 
         r = routing_policy_rule_new_static(network, filename, section_line, &n);
         if (r < 0)
-                return r;
+                return log_oom();
 
         r = safe_atou32(rvalue, &n->table);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse RPDB rule table, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse RPDB rule table, ignoring: %s", rvalue);
                 return 0;
         }
 
@@ -813,11 +807,11 @@ int config_parse_routing_policy_rule_fwmark_mask(
 
         r = routing_policy_rule_new_static(network, filename, section_line, &n);
         if (r < 0)
-                return r;
+                return log_oom();
 
         r = parse_fwmark_fwmask(rvalue, &n->fwmark, &n->fwmask);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse RPDB rule firewall mark or mask, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse RPDB rule firewall mark or mask, ignoring: %s", rvalue);
                 return 0;
         }
 
@@ -852,7 +846,7 @@ int config_parse_routing_policy_rule_prefix(
 
         r = routing_policy_rule_new_static(network, filename, section_line, &n);
         if (r < 0)
-                return r;
+                return log_oom();
 
         if (streq(lvalue, "To")) {
                 buffer = &n->to;
@@ -867,7 +861,7 @@ int config_parse_routing_policy_rule_prefix(
         else
                 r = in_addr_prefix_from_string(rvalue, n->family, buffer, prefixlen);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "RPDB rule prefix is invalid, ignoring assignment: %s", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, r, "RPDB rule prefix is invalid, ignoring assignment: %s", rvalue);
                 return 0;
         }
 
@@ -900,10 +894,10 @@ int config_parse_routing_policy_rule_device(
 
         r = routing_policy_rule_new_static(network, filename, section_line, &n);
         if (r < 0)
-                return r;
+                return log_oom();
 
         if (!ifname_valid(rvalue)) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse '%s' interface name, ignoring: %s", lvalue, rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse '%s' interface name, ignoring: %s", lvalue, rvalue);
                 return 0;
         }
 
@@ -946,11 +940,11 @@ int config_parse_routing_policy_rule_port_range(
 
         r = routing_policy_rule_new_static(network, filename, section_line, &n);
         if (r < 0)
-                return r;
+                return log_oom();
 
         r = parse_ip_port_range(rvalue, &low, &high);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse routing policy rule port range '%s'", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse routing policy rule port range '%s'", rvalue);
                 return 0;
         }
 
@@ -991,11 +985,11 @@ int config_parse_routing_policy_rule_ip_protocol(
 
         r = routing_policy_rule_new_static(network, filename, section_line, &n);
         if (r < 0)
-                return r;
+                return log_oom();
 
         r = parse_ip_protocol(rvalue);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse IP protocol '%s' for routing policy rule, ignoring: %m", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse IP protocol '%s' for routing policy rule, ignoring: %m", rvalue);
                 return 0;
         }
 
@@ -1030,11 +1024,11 @@ int config_parse_routing_policy_rule_invert(
 
         r = routing_policy_rule_new_static(network, filename, section_line, &n);
         if (r < 0)
-                return r;
+                return log_oom();
 
         r = parse_boolean(rvalue);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse RPDB rule invert, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse RPDB rule invert, ignoring: %s", rvalue);
                 return 0;
         }
 
@@ -1070,11 +1064,11 @@ int config_parse_routing_policy_rule_family(
 
         r = routing_policy_rule_new_static(network, filename, section_line, &n);
         if (r < 0)
-                return r;
+                return log_oom();
 
         a = routing_policy_rule_address_family_from_string(rvalue);
         if (a < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, 0,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "Invalid address family '%s', ignoring.", rvalue);
                 return 0;
         }
@@ -1110,7 +1104,7 @@ int config_parse_routing_policy_rule_uid_range(
 
         r = routing_policy_rule_new_static(network, filename, section_line, &n);
         if (r < 0)
-                return r;
+                return log_oom();
 
         r = get_user_creds(&rvalue, &start, NULL, NULL, NULL, 0);
         if (r >= 0)
@@ -1118,7 +1112,7 @@ int config_parse_routing_policy_rule_uid_range(
         else {
                 r = parse_uid_range(rvalue, &start, &end);
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
                                    "Invalid uid or uid range '%s', ignoring: %m", rvalue);
                         return 0;
                 }
@@ -1155,15 +1149,15 @@ int config_parse_routing_policy_rule_suppress_prefixlen(
 
         r = routing_policy_rule_new_static(network, filename, section_line, &n);
         if (r < 0)
-                return r;
+                return log_oom();
 
         r = parse_ip_prefix_length(rvalue, &n->suppress_prefixlen);
         if (r == -ERANGE) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "Prefix length outside of valid range 0-128, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Prefix length outside of valid range 0-128, ignoring: %s", rvalue);
                 return 0;
         }
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse RPDB rule suppress_prefixlen, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse RPDB rule suppress_prefixlen, ignoring: %s", rvalue);
                 return 0;
         }
 
@@ -1328,10 +1322,6 @@ int routing_policy_load_rules(const char *state_file, Set **rules) {
         if (!l)
                 return -ENOMEM;
 
-        r = set_ensure_allocated(rules, &routing_policy_rule_hash_ops);
-        if (r < 0)
-                return r;
-
         STRV_FOREACH(i, l) {
                 _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *rule = NULL;
 
@@ -1384,7 +1374,7 @@ int routing_policy_load_rules(const char *state_file, Set **rules) {
                         } else if (streq(a, "tos")) {
                                 r = safe_atou8(b, &rule->tos);
                                 if (r < 0) {
-                                        log_error_errno(r, "Failed to parse RPDB rule tos, ignoring: %s", b);
+                                        log_error_errno(r, "Failed to parse RPDB rule TOS, ignoring: %s", b);
                                         continue;
                                 }
                         } else if (streq(a, "table")) {
@@ -1461,7 +1451,7 @@ int routing_policy_load_rules(const char *state_file, Set **rules) {
                         }
                 }
 
-                r = set_put(*rules, rule);
+                r = set_ensure_put(rules, &routing_policy_rule_hash_ops, rule);
                 if (r < 0) {
                         log_warning_errno(r, "Failed to add RPDB rule to saved DB, ignoring: %s", p);
                         continue;
diff --git a/src/network/networkd-sriov.c b/src/network/networkd-sriov.c
new file mode 100644 (file)
index 0000000..7d99707
--- /dev/null
@@ -0,0 +1,501 @@
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright © 2020 VMware, Inc. */
+
+#include "alloc-util.h"
+#include "netlink-util.h"
+#include "networkd-manager.h"
+#include "networkd-sriov.h"
+#include "parse-util.h"
+#include "set.h"
+#include "string-util.h"
+
+static int sr_iov_new(SRIOV **ret) {
+        SRIOV *sr_iov;
+
+        sr_iov = new(SRIOV, 1);
+        if (!sr_iov)
+                return -ENOMEM;
+
+        *sr_iov = (SRIOV) {
+                  .vf = (uint32_t) -1,
+                  .vlan_proto = ETH_P_8021Q,
+                  .vf_spoof_check_setting = -1,
+                  .trust = -1,
+                  .query_rss = -1,
+                  .link_state = _SR_IOV_LINK_STATE_INVALID,
+        };
+
+        *ret = TAKE_PTR(sr_iov);
+
+        return 0;
+}
+
+static int sr_iov_new_static(Network *network, const char *filename, unsigned section_line, SRIOV **ret) {
+        _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+        _cleanup_(sr_iov_freep) SRIOV *sr_iov = NULL;
+        SRIOV *existing = NULL;
+        int r;
+
+        assert(network);
+        assert(ret);
+        assert(filename);
+        assert(section_line > 0);
+
+        r = network_config_section_new(filename, section_line, &n);
+        if (r < 0)
+                return r;
+
+        existing = ordered_hashmap_get(network->sr_iov_by_section, n);
+        if (existing) {
+                *ret = existing;
+                return 0;
+        }
+
+        r = sr_iov_new(&sr_iov);
+        if (r < 0)
+                return r;
+
+        sr_iov->network = network;
+        sr_iov->section = TAKE_PTR(n);
+
+        r = ordered_hashmap_ensure_allocated(&network->sr_iov_by_section, &network_config_hash_ops);
+        if (r < 0)
+                return r;
+
+        r = ordered_hashmap_put(network->sr_iov_by_section, sr_iov->section, sr_iov);
+        if (r < 0)
+                return r;
+
+        *ret = TAKE_PTR(sr_iov);
+        return 0;
+}
+
+SRIOV *sr_iov_free(SRIOV *sr_iov) {
+        if (!sr_iov)
+                return NULL;
+
+        if (sr_iov->network && sr_iov->section)
+                ordered_hashmap_remove(sr_iov->network->sr_iov_by_section, sr_iov->section);
+
+        network_config_section_free(sr_iov->section);
+
+        return mfree(sr_iov);
+}
+
+static int sr_iov_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+        int r;
+
+        assert(link);
+        assert(link->sr_iov_messages > 0);
+        link->sr_iov_messages--;
+
+        if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+                return 1;
+
+        r = sd_netlink_message_get_errno(m);
+        if (r < 0 && r != -EEXIST) {
+                log_link_message_error_errno(link, m, r, "Could not set up SR-IOV");
+                link_enter_failed(link);
+                return 1;
+        }
+
+        if (link->sr_iov_messages == 0) {
+                log_link_debug(link, "SR-IOV configured");
+                link->sr_iov_configured = true;
+                link_check_ready(link);
+        }
+
+        return 1;
+}
+
+int sr_iov_configure(Link *link, SRIOV *sr_iov) {
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
+        int r;
+
+        assert(link);
+        assert(link->manager);
+        assert(link->manager->rtnl);
+        assert(link->ifindex > 0);
+
+        log_link_debug(link, "Setting SR-IOV virtual function %"PRIu32, sr_iov->vf);
+
+        r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m");
+
+        r = sd_netlink_message_open_container(req, IFLA_VFINFO_LIST);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not open IFLA_VFINFO_LIST container: %m");
+
+        r = sd_netlink_message_open_container(req, IFLA_VF_INFO);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not open IFLA_VF_INFO container: %m");
+
+        if (!ether_addr_is_null(&sr_iov->mac)) {
+                struct ifla_vf_mac ivm = {
+                        .vf = sr_iov->vf,
+                };
+
+                memcpy(ivm.mac, &sr_iov->mac, ETH_ALEN);
+                r = sd_netlink_message_append_data(req, IFLA_VF_MAC, &ivm, sizeof(struct ifla_vf_mac));
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append IFLA_VF_MAC: %m");
+        }
+
+        if (sr_iov->vf_spoof_check_setting >= 0) {
+                struct ifla_vf_spoofchk ivs = {
+                        .vf = sr_iov->vf,
+                        .setting = sr_iov->vf_spoof_check_setting,
+                };
+
+                r = sd_netlink_message_append_data(req, IFLA_VF_SPOOFCHK, &ivs, sizeof(struct ifla_vf_spoofchk));
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append IFLA_VF_SPOOFCHK: %m");
+        }
+
+        if (sr_iov->query_rss >= 0) {
+                struct ifla_vf_rss_query_en ivs = {
+                        .vf = sr_iov->vf,
+                        .setting = sr_iov->query_rss,
+                };
+
+                r = sd_netlink_message_append_data(req, IFLA_VF_RSS_QUERY_EN, &ivs, sizeof(struct ifla_vf_rss_query_en));
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append IFLA_VF_RSS_QUERY_EN: %m");
+        }
+
+        if (sr_iov->trust >= 0) {
+                struct ifla_vf_trust ivt = {
+                        .vf = sr_iov->vf,
+                        .setting = sr_iov->trust,
+                };
+
+                r = sd_netlink_message_append_data(req, IFLA_VF_TRUST, &ivt, sizeof(struct ifla_vf_trust));
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append IFLA_VF_TRUST: %m");
+        }
+
+        if (sr_iov->link_state >= 0) {
+                struct ifla_vf_link_state ivl = {
+                        .vf = sr_iov->vf,
+                        .link_state = sr_iov->link_state,
+                };
+
+                r = sd_netlink_message_append_data(req, IFLA_VF_LINK_STATE, &ivl, sizeof(struct ifla_vf_link_state));
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append IFLA_VF_LINK_STATE: %m");
+        }
+
+        if (sr_iov->vlan > 0) {
+                /* Because of padding, first the buffer must be initialized with 0. */
+                struct ifla_vf_vlan_info ivvi = {};
+                ivvi.vf = sr_iov->vf;
+                ivvi.vlan = sr_iov->vlan;
+                ivvi.qos = sr_iov->qos;
+                ivvi.vlan_proto = htobe16(sr_iov->vlan_proto);
+
+                r = sd_netlink_message_open_container(req, IFLA_VF_VLAN_LIST);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not open IFLA_VF_VLAN_LIST container: %m");
+
+                r = sd_netlink_message_append_data(req, IFLA_VF_VLAN_INFO, &ivvi, sizeof(struct ifla_vf_vlan_info));
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append IFLA_VF_VLAN_INFO: %m");
+
+                r = sd_netlink_message_close_container(req);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not close IFLA_VF_VLAN_LIST container: %m");
+        }
+
+        r = sd_netlink_message_close_container(req);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not close IFLA_VF_INFO container: %m");
+
+        r = sd_netlink_message_close_container(req);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not close IFLA_VFINFO_LIST container: %m");
+
+        r = netlink_call_async(link->manager->rtnl, NULL, req, sr_iov_handler,
+                               link_netlink_destroy_callback, link);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
+
+        link_ref(link);
+        link->sr_iov_messages++;
+
+        return 0;
+}
+
+int sr_iov_section_verify(SRIOV *sr_iov) {
+        assert(sr_iov);
+
+        if (section_is_invalid(sr_iov->section))
+                return -EINVAL;
+
+        if (sr_iov->vf == (uint32_t) -1)
+                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+                                         "%s: [SRIOV] section without VirtualFunction= field configured. "
+                                         "Ignoring [SRIOV] section from line %u.",
+                                         sr_iov->section->filename, sr_iov->section->line);
+
+        return 0;
+}
+
+int config_parse_sr_iov_uint32(
+                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_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
+        Network *network = data;
+        uint32_t k;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = sr_iov_new_static(network, filename, section_line, &sr_iov);
+        if (r < 0)
+                return r;
+
+        if (isempty(rvalue)) {
+                if (streq(lvalue, "VirtualFunction"))
+                        sr_iov->vf = (uint32_t) -1;
+                else if (streq(lvalue, "VLANId"))
+                        sr_iov->vlan = 0;
+                else if (streq(lvalue, "QualityOfService"))
+                        sr_iov->qos = 0;
+                else
+                        assert_not_reached("Invalid lvalue");
+
+                TAKE_PTR(sr_iov);
+                return 0;
+        }
+
+        r = safe_atou32(rvalue, &k);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue);
+                return 0;
+        }
+
+        if (streq(lvalue, "VLANId")) {
+                if (k == 0 || k > 4095) {
+                        log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid SR-IOV VLANId: %d", k);
+                        return 0;
+                }
+                sr_iov->vlan = k;
+        } else if (streq(lvalue, "VirtualFunction")) {
+                if (k >= INT_MAX) {
+                        log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid SR-IOV virtual function: %d", k);
+                        return 0;
+                }
+                sr_iov->vf = k;
+        } else if (streq(lvalue, "QualityOfService"))
+                sr_iov->qos = k;
+        else
+                assert_not_reached("Invalid lvalue");
+
+        TAKE_PTR(sr_iov);
+        return 0;
+}
+
+int config_parse_sr_iov_vlan_proto(
+                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_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
+        Network *network = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = sr_iov_new_static(network, filename, section_line, &sr_iov);
+        if (r < 0)
+                return r;
+
+        if (isempty(rvalue) || streq(rvalue, "802.1Q"))
+                sr_iov->vlan_proto = ETH_P_8021Q;
+        else if (streq(rvalue, "802.1ad"))
+                sr_iov->vlan_proto = ETH_P_8021AD;
+        else {
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "Invalid SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue);
+                return 0;
+        }
+
+        TAKE_PTR(sr_iov);
+        return 0;
+}
+
+int config_parse_sr_iov_link_state(
+                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_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
+        Network *network = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = sr_iov_new_static(network, filename, section_line, &sr_iov);
+        if (r < 0)
+                return r;
+
+        /* Unfortunately, SR_IOV_LINK_STATE_DISABLE is 2, not 0. So, we cannot use
+         * DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN() macro. */
+
+        if (isempty(rvalue)) {
+                sr_iov->link_state = _SR_IOV_LINK_STATE_INVALID;
+                TAKE_PTR(sr_iov);
+                return 0;
+        }
+
+        if (streq(rvalue, "auto")) {
+                sr_iov->link_state = SR_IOV_LINK_STATE_AUTO;
+                TAKE_PTR(sr_iov);
+                return 0;
+        }
+
+        r = parse_boolean(rvalue);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue);
+                return 0;
+        }
+
+        sr_iov->link_state = r ? SR_IOV_LINK_STATE_ENABLE : SR_IOV_LINK_STATE_DISABLE;
+        TAKE_PTR(sr_iov);
+        return 0;
+}
+
+int config_parse_sr_iov_boolean(
+                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_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
+        Network *network = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = sr_iov_new_static(network, filename, section_line, &sr_iov);
+        if (r < 0)
+                return r;
+
+        if (isempty(rvalue)) {
+                if (streq(lvalue, "MACSpoofCheck"))
+                        sr_iov->vf_spoof_check_setting = -1;
+                else if (streq(lvalue, "QueryReceiveSideScaling"))
+                        sr_iov->query_rss = -1;
+                else if (streq(lvalue, "Trust"))
+                        sr_iov->trust = -1;
+                else
+                        assert_not_reached("Invalid lvalue");
+
+                TAKE_PTR(sr_iov);
+                return 0;
+        }
+
+        r = parse_boolean(rvalue);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse '%s=', ignoring: %s", lvalue, rvalue);
+                return 0;
+        }
+
+        if (streq(lvalue, "MACSpoofCheck"))
+                sr_iov->vf_spoof_check_setting = r;
+        else if (streq(lvalue, "QueryReceiveSideScaling"))
+                sr_iov->query_rss = r;
+        else if (streq(lvalue, "Trust"))
+                sr_iov->trust = r;
+        else
+                assert_not_reached("Invalid lvalue");
+
+        TAKE_PTR(sr_iov);
+        return 0;
+}
+
+int config_parse_sr_iov_mac(
+                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_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL;
+        Network *network = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = sr_iov_new_static(network, filename, section_line, &sr_iov);
+        if (r < 0)
+                return r;
+
+        if (isempty(rvalue)) {
+                sr_iov->mac = ETHER_ADDR_NULL;
+                TAKE_PTR(sr_iov);
+                return 0;
+        }
+
+        r = ether_addr_from_string(rvalue, &sr_iov->mac);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue);
+                return 0;
+        }
+
+        TAKE_PTR(sr_iov);
+        return 0;
+}
diff --git a/src/network/networkd-sriov.h b/src/network/networkd-sriov.h
new file mode 100644 (file)
index 0000000..a545d12
--- /dev/null
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright © 2020 VMware, Inc. */
+#pragma once
+
+#include <linux/if_link.h>
+
+#include "conf-parser.h"
+#include "networkd-link.h"
+#include "networkd-network.h"
+#include "networkd-util.h"
+
+typedef enum SRIOVLinkState {
+        SR_IOV_LINK_STATE_AUTO = IFLA_VF_LINK_STATE_AUTO,
+        SR_IOV_LINK_STATE_ENABLE = IFLA_VF_LINK_STATE_ENABLE,
+        SR_IOV_LINK_STATE_DISABLE = IFLA_VF_LINK_STATE_DISABLE,
+        _SR_IOV_LINK_STATE_MAX,
+        _SR_IOV_LINK_STATE_INVALID = -1,
+} SRIOVLinkState;
+
+typedef struct SRIOV {
+        NetworkConfigSection *section;
+        Network *network;
+
+        uint32_t vf;   /* 0 - 2147483646 */
+        uint32_t vlan; /* 0 - 4095, 0 disables VLAN filter */
+        uint32_t qos;
+        uint16_t vlan_proto; /* ETH_P_8021Q or ETH_P_8021AD */
+        int vf_spoof_check_setting;
+        int query_rss;
+        int trust;
+        SRIOVLinkState link_state;
+        struct ether_addr mac;
+} SRIOV;
+
+SRIOV *sr_iov_free(SRIOV *sr_iov);
+
+int sr_iov_configure(Link *link, SRIOV *sr_iov);
+int sr_iov_section_verify(SRIOV *sr_iov);
+
+DEFINE_NETWORK_SECTION_FUNCTIONS(SRIOV, sr_iov_free);
+
+CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_uint32);
+CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_boolean);
+CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_link_state);
+CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_vlan_proto);
+CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_mac);
index deba9953726afbfafd33dc2afb60f95154ead026..ce9319d942a3aa450fe438484d7e5cbd1d2cbde8 100644 (file)
@@ -8,14 +8,14 @@
 #include "string-util.h"
 #include "util.h"
 
-static const char * const address_family_table[_ADDRESS_FAMILY_MAX] = {
+static const char* const address_family_table[_ADDRESS_FAMILY_MAX] = {
         [ADDRESS_FAMILY_NO]            = "no",
         [ADDRESS_FAMILY_YES]           = "yes",
         [ADDRESS_FAMILY_IPV4]          = "ipv4",
         [ADDRESS_FAMILY_IPV6]          = "ipv6",
 };
 
-static const char * const link_local_address_family_table[_ADDRESS_FAMILY_MAX] = {
+static const char* const link_local_address_family_table[_ADDRESS_FAMILY_MAX] = {
         [ADDRESS_FAMILY_NO]            = "no",
         [ADDRESS_FAMILY_YES]           = "yes",
         [ADDRESS_FAMILY_IPV4]          = "ipv4",
@@ -24,25 +24,35 @@ static const char * const link_local_address_family_table[_ADDRESS_FAMILY_MAX] =
         [ADDRESS_FAMILY_FALLBACK_IPV4] = "ipv4-fallback",
 };
 
-static const char * const routing_policy_rule_address_family_table[_ADDRESS_FAMILY_MAX] = {
+static const char* const routing_policy_rule_address_family_table[_ADDRESS_FAMILY_MAX] = {
         [ADDRESS_FAMILY_YES]           = "both",
         [ADDRESS_FAMILY_IPV4]          = "ipv4",
         [ADDRESS_FAMILY_IPV6]          = "ipv6",
 };
 
-static const char * const duplicate_address_detection_address_family_table[_ADDRESS_FAMILY_MAX] = {
+static const char* const duplicate_address_detection_address_family_table[_ADDRESS_FAMILY_MAX] = {
         [ADDRESS_FAMILY_NO]            = "none",
         [ADDRESS_FAMILY_YES]           = "both",
         [ADDRESS_FAMILY_IPV4]          = "ipv4",
         [ADDRESS_FAMILY_IPV6]          = "ipv6",
 };
 
+static const char* const dhcp_lease_server_type_table[_SD_DHCP_LEASE_SERVER_TYPE_MAX] = {
+        [SD_DHCP_LEASE_DNS]  = "DNS servers",
+        [SD_DHCP_LEASE_NTP]  = "NTP servers",
+        [SD_DHCP_LEASE_SIP]  = "SIP servers",
+        [SD_DHCP_LEASE_POP3] = "POP3 servers",
+        [SD_DHCP_LEASE_SMTP] = "SMTP servers",
+        [SD_DHCP_LEASE_LPR]  = "LPR servers",
+};
+
 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(address_family, AddressFamily, ADDRESS_FAMILY_YES);
 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(link_local_address_family, AddressFamily, ADDRESS_FAMILY_YES);
 DEFINE_STRING_TABLE_LOOKUP(routing_policy_rule_address_family, AddressFamily);
 DEFINE_STRING_TABLE_LOOKUP(duplicate_address_detection_address_family, AddressFamily);
 DEFINE_CONFIG_PARSE_ENUM(config_parse_link_local_address_family, link_local_address_family,
                          AddressFamily, "Failed to parse option");
+DEFINE_STRING_TABLE_LOOKUP(dhcp_lease_server_type, sd_dhcp_lease_server_type);
 
 int config_parse_address_family_with_kernel(
                 const char* unit,
@@ -76,7 +86,7 @@ int config_parse_address_family_with_kernel(
                 if (streq(rvalue, "kernel"))
                         s = ADDRESS_FAMILY_NO;
                 else {
-                        log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IPForward= option, ignoring: %s", rvalue);
+                        log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse IPForward= option, ignoring: %s", rvalue);
                         return 0;
                 }
         }
@@ -97,7 +107,7 @@ int kernel_route_expiration_supported(void) {
                         .type = CONDITION_KERNEL_VERSION,
                         .parameter = (char *) ">= 4.5"
                 };
-                r = condition_test(&c);
+                r = condition_test(&c, NULL);
                 if (r < 0)
                         return r;
 
@@ -107,7 +117,7 @@ int kernel_route_expiration_supported(void) {
 }
 
 static void network_config_hash_func(const NetworkConfigSection *c, struct siphash *state) {
-        siphash24_compress(c->filename, strlen(c->filename), state);
+        siphash24_compress_string(c->filename, state);
         siphash24_compress(&c->line, sizeof(c->line), state);
 }
 
index 28dd9d3fe564720a3761ebc17ac54becfcc85dd6..0433f883a32856417fea5b130e0fad9a59545714 100644 (file)
@@ -1,6 +1,8 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
+#include "sd-dhcp-lease.h"
+
 #include "conf-parser.h"
 #include "hash-funcs.h"
 #include "macro.h"
@@ -38,6 +40,9 @@ AddressFamily routing_policy_rule_address_family_from_string(const char *s) _pur
 const char *duplicate_address_detection_address_family_to_string(AddressFamily b) _const_;
 AddressFamily duplicate_address_detection_address_family_from_string(const char *s) _pure_;
 
+const char *dhcp_lease_server_type_to_string(sd_dhcp_lease_server_type t) _const_;
+sd_dhcp_lease_server_type dhcp_lease_server_type_from_string(const char *s) _pure_;
+
 int kernel_route_expiration_supported(void);
 
 int network_config_section_new(const char *filename, unsigned line, NetworkConfigSection **s);
index 35f22fea8014338d6a860a21277427330244b378..445aee16ad0435b4f7361cb6098dac5d859837dc 100644 (file)
@@ -17,8 +17,8 @@
 #include "user-util.h"
 
 static int run(int argc, char *argv[]) {
-        _cleanup_(notify_on_cleanup) const char *notify_message = NULL;
         _cleanup_(manager_freep) Manager *m = NULL;
+        _cleanup_(notify_on_cleanup) const char *notify_message = NULL;
         int r;
 
         log_setup_service();
index c5667da9fadb0dfdee7a59aac2ebb24744862fc5..5339e5e5eda659275e9765cbdc93a182c0f00f3c 100644 (file)
@@ -14,6 +14,7 @@
 [Network]
 #SpeedMeter=no
 #SpeedMeterIntervalSec=10sec
+#ManageForeignRoutes=yes
 
 [DHCP]
 #DUIDType=vendor
index 9b1895e657d2912614dcd13352a6c6cb1ccde934..50b0ef081422e31b7a683dbb720952b093197959 100644 (file)
                 <annotate key="org.freedesktop.policykit.owner">unix-user:systemd-network</annotate>
         </action>
 
+        <action id="org.freedesktop.network1.forcerenew">
+                <description gettext-domain="systemd">DHCP server sends force renew message</description>
+                <message gettext-domain="systemd">Authentication is required to send force renew message.</message>
+                <defaults>
+                        <allow_any>auth_admin</allow_any>
+                        <allow_inactive>auth_admin</allow_inactive>
+                        <allow_active>auth_admin_keep</allow_active>
+                </defaults>
+                <annotate key="org.freedesktop.policykit.owner">unix-user:systemd-network</annotate>
+        </action>
+
         <action id="org.freedesktop.network1.renew">
                 <description gettext-domain="systemd">Renew dynamic addresses</description>
                 <message gettext-domain="systemd">Authentication is required to renew dynamic addresses.</message>
diff --git a/src/network/tc/cake.c b/src/network/tc/cake.c
new file mode 100644 (file)
index 0000000..1da1ec4
--- /dev/null
@@ -0,0 +1,163 @@
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright © 2020 VMware, Inc. */
+
+#include <linux/pkt_sched.h>
+
+#include "alloc-util.h"
+#include "cake.h"
+#include "conf-parser.h"
+#include "netlink-util.h"
+#include "parse-util.h"
+#include "qdisc.h"
+#include "string-util.h"
+
+static int cake_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
+        CommonApplicationsKeptEnhanced *c;
+        int r;
+
+        assert(link);
+        assert(qdisc);
+        assert(req);
+
+        c = CAKE(qdisc);
+
+        r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "cake");
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m");
+
+        if (c->bandwidth > 0) {
+                r = sd_netlink_message_append_u64(req, TCA_CAKE_BASE_RATE64, c->bandwidth);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append TCA_CAKE_BASE_RATE64 attribute: %m");
+        }
+
+        r = sd_netlink_message_append_s32(req, TCA_CAKE_OVERHEAD, c->overhead);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not append TCA_CAKE_OVERHEAD attribute: %m");
+
+        r = sd_netlink_message_close_container(req);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m");
+
+        return 0;
+}
+
+int config_parse_cake_bandwidth(
+                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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
+        CommonApplicationsKeptEnhanced *c;
+        Network *network = data;
+        uint64_t k;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = qdisc_new_static(QDISC_KIND_CAKE, network, filename, section_line, &qdisc);
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "More than one kind of queueing discipline, ignoring assignment: %m");
+                return 0;
+        }
+
+        c = CAKE(qdisc);
+
+        if (isempty(rvalue)) {
+                c->bandwidth = 0;
+
+                qdisc = NULL;
+                return 0;
+        }
+
+        r = parse_size(rvalue, 1000, &k);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse '%s=', ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
+        }
+
+        c->bandwidth = k/8;
+        qdisc = NULL;
+
+        return 0;
+}
+
+int config_parse_cake_overhead(
+                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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
+        CommonApplicationsKeptEnhanced *c;
+        Network *network = data;
+        int32_t v;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = qdisc_new_static(QDISC_KIND_CAKE, network, filename, section_line, &qdisc);
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "More than one kind of queueing discipline, ignoring assignment: %m");
+                return 0;
+        }
+
+        c = CAKE(qdisc);
+
+        if (isempty(rvalue)) {
+                c->overhead = 0;
+                qdisc = NULL;
+                return 0;
+        }
+
+        r = safe_atoi32(rvalue, &v);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse '%s=', ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
+        }
+        if (v < -64 || v > 256) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "Invalid '%s=', ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
+        }
+
+        c->overhead = v;
+        qdisc = NULL;
+        return 0;
+}
+
+const QDiscVTable cake_vtable = {
+        .object_size = sizeof(CommonApplicationsKeptEnhanced),
+        .tca_kind = "cake",
+        .fill_message = cake_fill_message,
+};
diff --git a/src/network/tc/cake.h b/src/network/tc/cake.h
new file mode 100644 (file)
index 0000000..36de511
--- /dev/null
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright © 2020 VMware, Inc. */
+#pragma once
+
+#include "conf-parser.h"
+#include "qdisc.h"
+
+typedef struct CommonApplicationsKeptEnhanced {
+        QDisc meta;
+
+        int overhead;
+        uint64_t bandwidth;
+
+} CommonApplicationsKeptEnhanced;
+
+DEFINE_QDISC_CAST(CAKE, CommonApplicationsKeptEnhanced);
+extern const QDiscVTable cake_vtable;
+
+CONFIG_PARSER_PROTOTYPE(config_parse_cake_bandwidth);
+CONFIG_PARSER_PROTOTYPE(config_parse_cake_overhead);
index 65c724da751ee05ce81ff2982b8099464271eb80..cba6faf37696d3a66332f8b65a9baf9a1a39f1d5 100644 (file)
@@ -99,9 +99,11 @@ int config_parse_controlled_delay_u32(
         r = qdisc_new_static(QDISC_KIND_CODEL, network, filename, section_line, &qdisc);
         if (r == -ENOMEM)
                 return log_oom();
-        if (r < 0)
-                return log_syntax(unit, LOG_ERR, filename, line, r,
-                                  "More than one kind of queueing discipline, ignoring assignment: %m");
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "More than one kind of queueing discipline, ignoring assignment: %m");
+                return 0;
+        }
 
         cd = CODEL(qdisc);
 
@@ -114,7 +116,7 @@ int config_parse_controlled_delay_u32(
 
         r = safe_atou32(rvalue, &cd->packet_limit);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to parse '%s=', ignoring assignment: %s",
                            lvalue, rvalue);
                 return 0;
@@ -151,9 +153,11 @@ int config_parse_controlled_delay_usec(
         r = qdisc_new_static(QDISC_KIND_CODEL, network, filename, section_line, &qdisc);
         if (r == -ENOMEM)
                 return log_oom();
-        if (r < 0)
-                return log_syntax(unit, LOG_ERR, filename, line, r,
-                                  "More than one kind of queueing discipline, ignoring assignment: %m");
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "More than one kind of queueing discipline, ignoring assignment: %m");
+                return 0;
+        }
 
         cd = CODEL(qdisc);
 
@@ -178,7 +182,7 @@ int config_parse_controlled_delay_usec(
 
         r = parse_sec(rvalue, p);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to parse '%s=', ignoring assignment: %s",
                            lvalue, rvalue);
                 return 0;
@@ -214,9 +218,11 @@ int config_parse_controlled_delay_bool(
         r = qdisc_new_static(QDISC_KIND_CODEL, network, filename, section_line, &qdisc);
         if (r == -ENOMEM)
                 return log_oom();
-        if (r < 0)
-                return log_syntax(unit, LOG_ERR, filename, line, r,
-                                  "More than one kind of queueing discipline, ignoring assignment: %m");
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "More than one kind of queueing discipline, ignoring assignment: %m");
+                return 0;
+        }
 
         cd = CODEL(qdisc);
 
@@ -229,7 +235,7 @@ int config_parse_controlled_delay_bool(
 
         r = parse_boolean(rvalue);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to parse '%s=', ignoring assignment: %s",
                            lvalue, rvalue);
                 return 0;
diff --git a/src/network/tc/drr.c b/src/network/tc/drr.c
new file mode 100644 (file)
index 0000000..9810a12
--- /dev/null
@@ -0,0 +1,109 @@
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright © 2020 VMware, Inc. */
+
+#include <linux/pkt_sched.h>
+
+#include "alloc-util.h"
+#include "conf-parser.h"
+#include "drr.h"
+#include "netlink-util.h"
+#include "parse-util.h"
+#include "string-util.h"
+
+const QDiscVTable drr_vtable = {
+        .object_size = sizeof(DeficitRoundRobinScheduler),
+        .tca_kind = "drr",
+};
+
+static int drr_class_fill_message(Link *link, TClass *tclass, sd_netlink_message *req) {
+        DeficitRoundRobinSchedulerClass *drr;
+        int r;
+
+        assert(link);
+        assert(tclass);
+        assert(req);
+
+        drr = TCLASS_TO_DRR(tclass);
+
+        r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "drr");
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m");
+
+        if (drr->quantum > 0) {
+                r = sd_netlink_message_append_u32(req, TCA_DRR_QUANTUM, drr->quantum);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append TCA_DRR_QUANTUM, attribute: %m");
+        }
+
+        r = sd_netlink_message_close_container(req);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m");
+
+        return 0;
+}
+
+int config_parse_drr_size(
+                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_(tclass_free_or_set_invalidp) TClass *tclass = NULL;
+        DeficitRoundRobinSchedulerClass *drr;
+        Network *network = data;
+        uint64_t u;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = tclass_new_static(TCLASS_KIND_DRR, network, filename, section_line, &tclass);
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to create traffic control class, ignoring assignment: %m");
+                return 0;
+        }
+
+        drr = TCLASS_TO_DRR(tclass);
+
+        if (isempty(rvalue)) {
+                drr->quantum = 0;
+
+                tclass = NULL;
+                return 0;
+        }
+
+        r = parse_size(rvalue, 1024, &u);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse '%s=', ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
+        }
+        if (u > UINT32_MAX) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid '%s=', ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
+        }
+
+        drr->quantum = (uint32_t) u;
+
+        tclass = NULL;
+        return 0;
+}
+
+const TClassVTable drr_tclass_vtable = {
+        .object_size = sizeof(DeficitRoundRobinSchedulerClass),
+        .tca_kind = "drr",
+        .fill_message = drr_class_fill_message,
+};
diff --git a/src/network/tc/drr.h b/src/network/tc/drr.h
new file mode 100644 (file)
index 0000000..ff2b658
--- /dev/null
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright © 2020 VMware, Inc. */
+#pragma once
+
+#include "qdisc.h"
+
+typedef struct DeficitRoundRobinScheduler {
+        QDisc meta;
+} DeficitRoundRobinScheduler;
+
+DEFINE_QDISC_CAST(DRR, DeficitRoundRobinScheduler);
+extern const QDiscVTable drr_vtable;
+
+typedef struct DeficitRoundRobinSchedulerClass {
+        TClass meta;
+
+        uint32_t quantum;
+} DeficitRoundRobinSchedulerClass;
+
+DEFINE_TCLASS_CAST(DRR, DeficitRoundRobinSchedulerClass);
+extern const TClassVTable drr_tclass_vtable;
+
+CONFIG_PARSER_PROTOTYPE(config_parse_drr_size);
diff --git a/src/network/tc/ets.c b/src/network/tc/ets.c
new file mode 100644 (file)
index 0000000..ece1f36
--- /dev/null
@@ -0,0 +1,344 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <linux/pkt_sched.h>
+
+#include "alloc-util.h"
+#include "conf-parser.h"
+#include "ets.h"
+#include "memory-util.h"
+#include "netlink-util.h"
+#include "parse-util.h"
+#include "qdisc.h"
+#include "string-util.h"
+#include "tc-util.h"
+
+static int enhanced_transmission_selection_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
+        EnhancedTransmissionSelection *ets;
+        int r;
+
+        assert(link);
+        assert(qdisc);
+        assert(req);
+
+        ets = ETS(qdisc);
+
+        r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "ets");
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m");
+
+        r = sd_netlink_message_append_u8(req, TCA_ETS_NBANDS, ets->n_bands);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not append TCA_ETS_NBANDS attribute: %m");
+
+        if (ets->n_strict > 0) {
+                r = sd_netlink_message_append_u8(req, TCA_ETS_NSTRICT, ets->n_strict);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append TCA_ETS_NSTRICT attribute: %m");
+        }
+
+        if (ets->n_quanta > 0) {
+                r = sd_netlink_message_open_container(req, TCA_ETS_QUANTA);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not open container TCA_ETS_QUANTA: %m");
+
+                for (unsigned i = 0; i < ets->n_quanta; i++) {
+                        r = sd_netlink_message_append_u32(req, TCA_ETS_QUANTA_BAND, ets->quanta[i]);
+                        if (r < 0)
+                                return log_link_error_errno(link, r, "Could not append TCA_ETS_QUANTA_BAND attribute: %m");
+                }
+
+                r = sd_netlink_message_close_container(req);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not close container TCA_ETS_QUANTA: %m");
+        }
+
+        if (ets->n_prio > 0) {
+                r = sd_netlink_message_open_container(req, TCA_ETS_PRIOMAP);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not open container TCA_ETS_PRIOMAP: %m");
+
+                for (unsigned i = 0; i < ets->n_prio; i++) {
+                        r = sd_netlink_message_append_u8(req, TCA_ETS_PRIOMAP_BAND, ets->prio[i]);
+                        if (r < 0)
+                                return log_link_error_errno(link, r, "Could not append TCA_ETS_PRIOMAP_BAND attribute: %m");
+                }
+
+                r = sd_netlink_message_close_container(req);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not close container TCA_ETS_PRIOMAP: %m");
+        }
+
+        r = sd_netlink_message_close_container(req);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m");
+
+        return 0;
+}
+
+int config_parse_ets_u8(
+                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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
+        EnhancedTransmissionSelection *ets;
+        Network *network = data;
+        uint8_t v, *p;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = qdisc_new_static(QDISC_KIND_ETS, network, filename, section_line, &qdisc);
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "More than one kind of queueing discipline, ignoring assignment: %m");
+                return 0;
+        }
+
+        ets = ETS(qdisc);
+        if (streq(lvalue, "Bands"))
+                p = &ets->n_bands;
+        else if (streq(lvalue, "StrictBands"))
+                p = &ets->n_strict;
+        else
+                assert_not_reached("Invalid lvalue.");
+
+        if (isempty(rvalue)) {
+                *p = 0;
+
+                qdisc = NULL;
+                return 0;
+        }
+
+        r = safe_atou8(rvalue, &v);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse '%s=', ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
+        }
+        if (v > TCQ_ETS_MAX_BANDS) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "Invalid '%s='. The value must be <= %d, ignoring assignment: %s",
+                           lvalue, TCQ_ETS_MAX_BANDS, rvalue);
+                return 0;
+        }
+
+        *p = v;
+        qdisc = NULL;
+
+        return 0;
+}
+
+int config_parse_ets_quanta(
+                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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
+        EnhancedTransmissionSelection *ets;
+        Network *network = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = qdisc_new_static(QDISC_KIND_ETS, network, filename, section_line, &qdisc);
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "More than one kind of queueing discipline, ignoring assignment: %m");
+                return 0;
+        }
+
+        ets = ETS(qdisc);
+
+        if (isempty(rvalue)) {
+                memzero(ets->quanta, sizeof(uint32_t) * TCQ_ETS_MAX_BANDS);
+                ets->n_quanta = 0;
+
+                qdisc = NULL;
+                return 0;
+        }
+
+        for (const char *p = rvalue;;) {
+                _cleanup_free_ char *word = NULL;
+                uint64_t v;
+
+                r = extract_first_word(&p, &word, NULL, 0);
+                if (r == -ENOMEM)
+                        return log_oom();
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to extract next value, ignoring: %m");
+                        break;
+                }
+                if (r == 0)
+                        break;
+
+                r = parse_size(word, 1024, &v);
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to parse '%s=', ignoring assignment: %s",
+                                   lvalue, word);
+                        continue;
+                }
+                if (v == 0 || v > UINT32_MAX) {
+                        log_syntax(unit, LOG_WARNING, filename, line, 0,
+                                   "Invalid '%s=', ignoring assignment: %s",
+                                   lvalue, word);
+                        continue;
+                }
+                if (ets->n_quanta >= TCQ_ETS_MAX_BANDS) {
+                        log_syntax(unit, LOG_WARNING, filename, line, 0,
+                                   "Too many quanta in '%s=', ignoring assignment: %s",
+                                   lvalue, word);
+                        continue;
+                }
+
+                ets->quanta[ets->n_quanta++] = v;
+        }
+
+        qdisc = NULL;
+
+        return 0;
+}
+
+int config_parse_ets_prio(
+                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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
+        EnhancedTransmissionSelection *ets;
+        Network *network = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = qdisc_new_static(QDISC_KIND_ETS, network, filename, section_line, &qdisc);
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "More than one kind of queueing discipline, ignoring assignment: %m");
+                return 0;
+        }
+
+        ets = ETS(qdisc);
+
+        if (isempty(rvalue)) {
+                memzero(ets->prio, sizeof(uint8_t) * (TC_PRIO_MAX + 1));
+                ets->n_prio = 0;
+
+                qdisc = NULL;
+                return 0;
+        }
+
+        for (const char *p = rvalue;;) {
+                _cleanup_free_ char *word = NULL;
+                uint8_t v;
+
+                r = extract_first_word(&p, &word, NULL, 0);
+                if (r == -ENOMEM)
+                        return log_oom();
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to extract next value, ignoring: %m");
+                        break;
+                }
+                if (r == 0)
+                        break;
+
+                r = safe_atou8(word, &v);
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to parse '%s=', ignoring assignment: %s",
+                                   lvalue, word);
+                        continue;
+                }
+                if (ets->n_prio > TC_PRIO_MAX) {
+                        log_syntax(unit, LOG_WARNING, filename, line, 0,
+                                   "Too many priomap in '%s=', ignoring assignment: %s",
+                                   lvalue, word);
+                        continue;
+                }
+
+                ets->prio[ets->n_prio++] = v;
+        }
+
+        qdisc = NULL;
+
+        return 0;
+}
+
+static int enhanced_transmission_selection_verify(QDisc *qdisc) {
+        EnhancedTransmissionSelection *ets;
+
+        assert(qdisc);
+
+        ets = ETS(qdisc);
+
+        if (ets->n_bands == 0)
+                ets->n_bands = ets->n_strict + ets->n_quanta;
+
+        if (ets->n_bands == 0)
+                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+                                         "%s: At least one of Band=, Strict=, or Quanta= must be specified. "
+                                         "Ignoring [EnhancedTransmissionSelection] section from line %u.",
+                                         qdisc->section->filename, qdisc->section->line);
+
+        if (ets->n_bands < ets->n_strict + ets->n_quanta)
+                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+                                         "%s: Not enough total bands to cover all the strict bands and quanta. "
+                                         "Ignoring [EnhancedTransmissionSelection] section from line %u.",
+                                         qdisc->section->filename, qdisc->section->line);
+
+        for (unsigned i = 0; i < ets->n_prio; i++)
+                if (ets->prio[i] >= ets->n_bands)
+                        return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+                                                 "%s: PriorityMap= element is out of bands. "
+                                                 "Ignoring [EnhancedTransmissionSelection] section from line %u.",
+                                                 qdisc->section->filename, qdisc->section->line);
+
+        return 0;
+}
+
+const QDiscVTable ets_vtable = {
+        .object_size = sizeof(EnhancedTransmissionSelection),
+        .tca_kind = "ets",
+        .fill_message = enhanced_transmission_selection_fill_message,
+        .verify = enhanced_transmission_selection_verify,
+};
diff --git a/src/network/tc/ets.h b/src/network/tc/ets.h
new file mode 100644 (file)
index 0000000..c35d597
--- /dev/null
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <linux/pkt_sched.h>
+
+#include "conf-parser.h"
+#include "qdisc.h"
+
+typedef struct EnhancedTransmissionSelection {
+        QDisc meta;
+
+        uint8_t n_bands;
+        uint8_t n_strict;
+        unsigned n_quanta;
+        uint32_t quanta[TCQ_ETS_MAX_BANDS];
+        unsigned n_prio;
+        uint8_t prio[TC_PRIO_MAX + 1];
+} EnhancedTransmissionSelection;
+
+DEFINE_QDISC_CAST(ETS, EnhancedTransmissionSelection);
+extern const QDiscVTable ets_vtable;
+
+CONFIG_PARSER_PROTOTYPE(config_parse_ets_u8);
+CONFIG_PARSER_PROTOTYPE(config_parse_ets_quanta);
+CONFIG_PARSER_PROTOTYPE(config_parse_ets_prio);
diff --git a/src/network/tc/fifo.c b/src/network/tc/fifo.c
new file mode 100644 (file)
index 0000000..e955223
--- /dev/null
@@ -0,0 +1,187 @@
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright © 2020 VMware, Inc. */
+
+#include <linux/pkt_sched.h>
+
+#include "alloc-util.h"
+#include "conf-parser.h"
+#include "fifo.h"
+#include "netlink-util.h"
+#include "parse-util.h"
+#include "string-util.h"
+
+static int fifo_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
+        struct tc_fifo_qopt opt = {};
+        FirstInFirstOut *fifo;
+        int r;
+
+        assert(link);
+        assert(qdisc);
+        assert(req);
+
+        switch(qdisc->kind) {
+        case QDISC_KIND_PFIFO:
+                fifo = PFIFO(qdisc);
+                break;
+        case QDISC_KIND_BFIFO:
+                fifo = BFIFO(qdisc);
+                break;
+        case QDISC_KIND_PFIFO_HEAD_DROP:
+                fifo = PFIFO_HEAD_DROP(qdisc);
+                break;
+        default:
+                assert_not_reached("Invalid QDisc kind.");
+        }
+
+        opt.limit = fifo->limit;
+
+        r = sd_netlink_message_append_data(req, TCA_OPTIONS, &opt, sizeof(struct tc_fifo_qopt));
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not append TCA_OPTIONS attribute: %m");
+
+        return 0;
+}
+
+int config_parse_pfifo_size(
+                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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
+        Network *network = data;
+        FirstInFirstOut *fifo;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = qdisc_new_static(ltype, network, filename, section_line, &qdisc);
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "More than one kind of queueing discipline, ignoring assignment: %m");
+                return 0;
+        }
+
+        switch(qdisc->kind) {
+        case QDISC_KIND_PFIFO:
+                fifo = PFIFO(qdisc);
+                break;
+        case QDISC_KIND_PFIFO_HEAD_DROP:
+                fifo = PFIFO_HEAD_DROP(qdisc);
+                break;
+        default:
+                assert_not_reached("Invalid QDisc kind.");
+        }
+
+        if (isempty(rvalue)) {
+                fifo->limit = 0;
+
+                qdisc = NULL;
+                return 0;
+        }
+
+        r = safe_atou32(rvalue, &fifo->limit);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse '%s=', ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
+        }
+
+        qdisc = NULL;
+        return 0;
+}
+
+int config_parse_bfifo_size(
+                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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
+        Network *network = data;
+        FirstInFirstOut *fifo;
+        uint64_t u;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = qdisc_new_static(QDISC_KIND_BFIFO, network, filename, section_line, &qdisc);
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "More than one kind of queueing discipline, ignoring assignment: %m");
+                return 0;
+        }
+
+        fifo = BFIFO(qdisc);
+
+        if (isempty(rvalue)) {
+                fifo->limit = 0;
+
+                qdisc = NULL;
+                return 0;
+        }
+
+        r = parse_size(rvalue, 1024, &u);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse '%s=', ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
+        }
+        if (u > UINT32_MAX) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid '%s=', ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
+        }
+
+        fifo->limit = (uint32_t) u;
+
+        qdisc = NULL;
+        return 0;
+}
+
+const QDiscVTable pfifo_vtable = {
+        .object_size = sizeof(FirstInFirstOut),
+        .tca_kind = "pfifo",
+        .fill_message = fifo_fill_message,
+};
+
+const QDiscVTable bfifo_vtable = {
+       .object_size = sizeof(FirstInFirstOut),
+       .tca_kind = "bfifo",
+       .fill_message = fifo_fill_message,
+};
+
+const QDiscVTable pfifo_head_drop_vtable = {
+       .object_size = sizeof(FirstInFirstOut),
+       .tca_kind = "pfifo_head_drop",
+       .fill_message = fifo_fill_message,
+};
+
+const QDiscVTable pfifo_fast_vtable = {
+       .object_size = sizeof(FirstInFirstOut),
+       .tca_kind = "pfifo_fast",
+};
diff --git a/src/network/tc/fifo.h b/src/network/tc/fifo.h
new file mode 100644 (file)
index 0000000..e4c976b
--- /dev/null
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright © 2020 VMware, Inc. */
+#pragma once
+
+#include "conf-parser.h"
+#include "qdisc.h"
+
+typedef struct FirstInFirstOut {
+        QDisc meta;
+
+        uint32_t limit;
+} FirstInFirstOut;
+
+DEFINE_QDISC_CAST(PFIFO, FirstInFirstOut);
+DEFINE_QDISC_CAST(BFIFO, FirstInFirstOut);
+DEFINE_QDISC_CAST(PFIFO_HEAD_DROP, FirstInFirstOut);
+DEFINE_QDISC_CAST(PFIFO_FAST, FirstInFirstOut);
+
+extern const QDiscVTable pfifo_vtable;
+extern const QDiscVTable bfifo_vtable;
+extern const QDiscVTable pfifo_head_drop_vtable;
+extern const QDiscVTable pfifo_fast_vtable;
+
+CONFIG_PARSER_PROTOTYPE(config_parse_pfifo_size);
+CONFIG_PARSER_PROTOTYPE(config_parse_bfifo_size);
index 6c7932c70f738e5a441456545157a47e210a38ab..17a2915143208301ca9204b85f89dd93a99059fc 100644 (file)
@@ -9,6 +9,7 @@
 #include "parse-util.h"
 #include "qdisc.h"
 #include "string-util.h"
+#include "strv.h"
 
 static int fair_queueing_controlled_delay_init(QDisc *qdisc) {
         FairQueueingControlledDelay *fqcd;
@@ -119,9 +120,11 @@ int config_parse_fair_queueing_controlled_delay_u32(
         r = qdisc_new_static(QDISC_KIND_FQ_CODEL, network, filename, section_line, &qdisc);
         if (r == -ENOMEM)
                 return log_oom();
-        if (r < 0)
-                return log_syntax(unit, LOG_ERR, filename, line, r,
-                                  "More than one kind of queueing discipline, ignoring assignment: %m");
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "More than one kind of queueing discipline, ignoring assignment: %m");
+                return 0;
+        }
 
         fqcd = FQ_CODEL(qdisc);
 
@@ -141,7 +144,7 @@ int config_parse_fair_queueing_controlled_delay_u32(
 
         r = safe_atou32(rvalue, p);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to parse '%s=', ignoring assignment: %s",
                            lvalue, rvalue);
                 return 0;
@@ -178,9 +181,11 @@ int config_parse_fair_queueing_controlled_delay_usec(
         r = qdisc_new_static(QDISC_KIND_FQ_CODEL, network, filename, section_line, &qdisc);
         if (r == -ENOMEM)
                 return log_oom();
-        if (r < 0)
-                return log_syntax(unit, LOG_ERR, filename, line, r,
-                                  "More than one kind of queueing discipline, ignoring assignment: %m");
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "More than one kind of queueing discipline, ignoring assignment: %m");
+                return 0;
+        }
 
         fqcd = FQ_CODEL(qdisc);
 
@@ -205,7 +210,7 @@ int config_parse_fair_queueing_controlled_delay_usec(
 
         r = parse_sec(rvalue, p);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to parse '%s=', ignoring assignment: %s",
                            lvalue, rvalue);
                 return 0;
@@ -241,9 +246,11 @@ int config_parse_fair_queueing_controlled_delay_bool(
         r = qdisc_new_static(QDISC_KIND_FQ_CODEL, network, filename, section_line, &qdisc);
         if (r == -ENOMEM)
                 return log_oom();
-        if (r < 0)
-                return log_syntax(unit, LOG_ERR, filename, line, r,
-                                  "More than one kind of queueing discipline, ignoring assignment: %m");
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "More than one kind of queueing discipline, ignoring assignment: %m");
+                return 0;
+        }
 
         fqcd = FQ_CODEL(qdisc);
 
@@ -256,7 +263,7 @@ int config_parse_fair_queueing_controlled_delay_bool(
 
         r = parse_boolean(rvalue);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to parse '%s=', ignoring assignment: %s",
                            lvalue, rvalue);
                 return 0;
@@ -295,21 +302,23 @@ int config_parse_fair_queueing_controlled_delay_size(
         r = qdisc_new_static(QDISC_KIND_FQ_CODEL, network, filename, section_line, &qdisc);
         if (r == -ENOMEM)
                 return log_oom();
-        if (r < 0)
-                return log_syntax(unit, LOG_ERR, filename, line, r,
-                                  "More than one kind of queueing discipline, ignoring assignment: %m");
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "More than one kind of queueing discipline, ignoring assignment: %m");
+                return 0;
+        }
 
         fqcd = FQ_CODEL(qdisc);
 
-        if (streq(lvalue, "MemoryLimit"))
+        if (STR_IN_SET(lvalue, "MemoryLimitBytes", "MemoryLimit"))
                 p = &fqcd->memory_limit;
-        else if (streq(lvalue, "Quantum"))
+        else if (STR_IN_SET(lvalue, "QuantumBytes", "Quantum"))
                 p = &fqcd->quantum;
         else
                 assert_not_reached("Invalid lvalue.");
 
         if (isempty(rvalue)) {
-                if (streq(lvalue, "MemoryLimit"))
+                if (STR_IN_SET(lvalue, "MemoryLimitBytes", "MemoryLimit"))
                         *p = UINT32_MAX;
                 else
                         *p = 0;
@@ -320,13 +329,13 @@ int config_parse_fair_queueing_controlled_delay_size(
 
         r = parse_size(rvalue, 1024, &sz);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to parse '%s=', ignoring assignment: %s",
                            lvalue, rvalue);
                 return 0;
         }
         if (sz >= UINT32_MAX) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "Specified '%s=' is too large, ignoring assignment: %s",
                            lvalue, rvalue);
                 return 0;
index c7eeec230737c9721e8e386b3014b86ea6a0ab87..d3218203b952afc08f540aa4dd864c234a200326 100644 (file)
@@ -9,7 +9,7 @@
 #include "netlink-util.h"
 #include "parse-util.h"
 #include "string-util.h"
-#include "util.h"
+#include "strv.h"
 
 static int fair_queueing_init(QDisc *qdisc) {
         FairQueueing *fq;
@@ -128,9 +128,11 @@ int config_parse_fair_queueing_u32(
         r = qdisc_new_static(QDISC_KIND_FQ, network, filename, section_line, &qdisc);
         if (r == -ENOMEM)
                 return log_oom();
-        if (r < 0)
-                return log_syntax(unit, LOG_ERR, filename, line, r,
-                                  "More than one kind of queueing discipline, ignoring assignment: %m");
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "More than one kind of queueing discipline, ignoring assignment: %m");
+                return 0;
+        }
 
         fq = FQ(qdisc);
 
@@ -154,7 +156,7 @@ int config_parse_fair_queueing_u32(
 
         r = safe_atou32(rvalue, p);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to parse '%s=', ignoring assignment: %s",
                            lvalue, rvalue);
                 return 0;
@@ -192,15 +194,17 @@ int config_parse_fair_queueing_size(
         r = qdisc_new_static(QDISC_KIND_FQ, network, filename, section_line, &qdisc);
         if (r == -ENOMEM)
                 return log_oom();
-        if (r < 0)
-                return log_syntax(unit, LOG_ERR, filename, line, r,
-                                  "More than one kind of queueing discipline, ignoring assignment: %m");
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "More than one kind of queueing discipline, ignoring assignment: %m");
+                return 0;
+        }
 
         fq = FQ(qdisc);
 
-        if (streq(lvalue, "Quantum"))
+        if (STR_IN_SET(lvalue, "QuantumBytes", "Quantum"))
                 p = &fq->quantum;
-        else if (streq(lvalue, "InitialQuantum"))
+        else if (STR_IN_SET(lvalue, "InitialQuantumBytes", "InitialQuantum"))
                 p = &fq->initial_quantum;
         else
                 assert_not_reached("Invalid lvalue");
@@ -214,13 +218,13 @@ int config_parse_fair_queueing_size(
 
         r = parse_size(rvalue, 1024, &sz);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to parse '%s=', ignoring assignment: %s",
                            lvalue, rvalue);
                 return 0;
         }
         if (sz > UINT32_MAX) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "Specified '%s=' is too large, ignoring assignment: %s",
                            lvalue, rvalue);
                 return 0;
@@ -257,9 +261,11 @@ int config_parse_fair_queueing_bool(
         r = qdisc_new_static(QDISC_KIND_FQ, network, filename, section_line, &qdisc);
         if (r == -ENOMEM)
                 return log_oom();
-        if (r < 0)
-                return log_syntax(unit, LOG_ERR, filename, line, r,
-                                  "More than one kind of queueing discipline, ignoring assignment: %m");
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "More than one kind of queueing discipline, ignoring assignment: %m");
+                return 0;
+        }
 
         fq = FQ(qdisc);
 
@@ -272,7 +278,7 @@ int config_parse_fair_queueing_bool(
 
         r = parse_boolean(rvalue);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to parse '%s=', ignoring assignment: %s",
                            lvalue, rvalue);
                 return 0;
@@ -310,9 +316,11 @@ int config_parse_fair_queueing_usec(
         r = qdisc_new_static(QDISC_KIND_FQ, network, filename, section_line, &qdisc);
         if (r == -ENOMEM)
                 return log_oom();
-        if (r < 0)
-                return log_syntax(unit, LOG_ERR, filename, line, r,
-                                  "More than one kind of queueing discipline, ignoring assignment: %m");
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "More than one kind of queueing discipline, ignoring assignment: %m");
+                return 0;
+        }
 
         fq = FQ(qdisc);
 
@@ -325,13 +333,13 @@ int config_parse_fair_queueing_usec(
 
         r = parse_sec(rvalue, &sec);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to parse '%s=', ignoring assignment: %s",
                            lvalue, rvalue);
                 return 0;
         }
         if (sec > UINT32_MAX) {
-                log_syntax(unit, LOG_ERR, filename, line, 0,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "Specified '%s=' is too large, ignoring assignment: %s",
                            lvalue, rvalue);
                 return 0;
@@ -369,9 +377,11 @@ int config_parse_fair_queueing_max_rate(
         r = qdisc_new_static(QDISC_KIND_FQ, network, filename, section_line, &qdisc);
         if (r == -ENOMEM)
                 return log_oom();
-        if (r < 0)
-                return log_syntax(unit, LOG_ERR, filename, line, r,
-                                  "More than one kind of queueing discipline, ignoring assignment: %m");
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "More than one kind of queueing discipline, ignoring assignment: %m");
+                return 0;
+        }
 
         fq = FQ(qdisc);
 
@@ -384,13 +394,13 @@ int config_parse_fair_queueing_max_rate(
 
         r = parse_size(rvalue, 1000, &sz);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to parse '%s=', ignoring assignment: %s",
                            lvalue, rvalue);
                 return 0;
         }
         if (sz / 8 > UINT32_MAX) {
-                log_syntax(unit, LOG_ERR, filename, line, 0,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "Specified '%s=' is too large, ignoring assignment: %s",
                            lvalue, rvalue);
                 return 0;
diff --git a/src/network/tc/gred.c b/src/network/tc/gred.c
new file mode 100644 (file)
index 0000000..5629ecd
--- /dev/null
@@ -0,0 +1,197 @@
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright © 2020 VMware, Inc. */
+
+#include <linux/pkt_sched.h>
+
+#include "alloc-util.h"
+#include "conf-parser.h"
+#include "netlink-util.h"
+#include "parse-util.h"
+#include "qdisc.h"
+#include "string-util.h"
+
+static int generic_random_early_detection_init(QDisc *qdisc) {
+        GenericRandomEarlyDetection *gred;
+
+        assert(qdisc);
+
+        gred = GRED(qdisc);
+
+        gred->grio = -1;
+
+        return 0;
+}
+
+static int generic_random_early_detection_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
+        GenericRandomEarlyDetection *gred;
+        struct tc_gred_sopt opt = {};
+        int r;
+
+        assert(link);
+        assert(qdisc);
+        assert(req);
+
+        gred = GRED(qdisc);
+
+        opt.DPs = gred->virtual_queues;
+        opt.def_DP = gred->default_virtual_queue;
+
+        if (gred->grio >= 0)
+                opt.grio = gred->grio;
+
+        r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "gred");
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m");
+
+        r = sd_netlink_message_append_data(req, TCA_GRED_DPS, &opt, sizeof(struct tc_gred_sopt));
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not append TCA_GRED_DPS attribute: %m");
+
+        r = sd_netlink_message_close_container(req);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m");
+
+        return 0;
+}
+
+static int generic_random_early_detection_verify(QDisc *qdisc) {
+        GenericRandomEarlyDetection *gred = GRED(qdisc);
+
+        if (gred->default_virtual_queue >= gred->virtual_queues)
+                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+                                         "%s: DefaultVirtualQueue= must be less than VirtualQueues=. "
+                                         "Ignoring [GenericRandomEarlyDetection] section from line %u.",
+                                         qdisc->section->filename, qdisc->section->line);
+
+        return 0;
+}
+
+int config_parse_generic_random_early_detection_u32(
+                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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
+        GenericRandomEarlyDetection *gred;
+        Network *network = data;
+        uint32_t *p;
+        uint32_t v;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = qdisc_new_static(QDISC_KIND_GRED, network, filename, section_line, &qdisc);
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "More than one kind of queueing discipline, ignoring assignment: %m");
+                return 0;
+        }
+
+        gred = GRED(qdisc);
+
+        if (streq(lvalue, "VirtualQueues"))
+                p = &gred->virtual_queues;
+        else if (streq(lvalue, "DefaultVirtualQueue"))
+                p = &gred->default_virtual_queue;
+        else
+                assert_not_reached("Invalid lvalue.");
+
+        if (isempty(rvalue)) {
+                *p = 0;
+
+                qdisc = NULL;
+                return 0;
+        }
+
+        r = safe_atou32(rvalue, &v);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse '%s=', ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
+        }
+
+        if (v > MAX_DPs) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "Invalid '%s=', ignoring assignment: %s",
+                           lvalue, rvalue);
+        }
+
+        *p = v;
+        qdisc = NULL;
+
+        return 0;
+}
+int config_parse_generic_random_early_detection_bool(
+                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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
+        GenericRandomEarlyDetection *gred;
+        Network *network = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = qdisc_new_static(QDISC_KIND_GRED, network, filename, section_line, &qdisc);
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "More than one kind of queueing discipline, ignoring assignment: %m");
+                return 0;
+        }
+
+        gred = GRED(qdisc);
+
+        if (isempty(rvalue)) {
+                gred->grio = -1;
+
+                qdisc = NULL;
+                return 0;
+        }
+
+        r = parse_boolean(rvalue);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse '%s=', ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
+        }
+
+        gred->grio = r;
+        qdisc = NULL;
+
+        return 0;
+}
+
+const QDiscVTable gred_vtable = {
+        .object_size = sizeof(GenericRandomEarlyDetection),
+        .tca_kind = "gred",
+        .init = generic_random_early_detection_init,
+        .fill_message = generic_random_early_detection_fill_message,
+        .verify = generic_random_early_detection_verify,
+};
diff --git a/src/network/tc/gred.h b/src/network/tc/gred.h
new file mode 100644 (file)
index 0000000..4fb2b37
--- /dev/null
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright © 2020 VMware, Inc. */
+#pragma once
+
+#include "conf-parser.h"
+#include "qdisc.h"
+
+typedef struct GenericRandomEarlyDetection {
+        QDisc meta;
+
+        uint32_t virtual_queues;
+        uint32_t default_virtual_queue;
+        int grio;
+} GenericRandomEarlyDetection;
+
+DEFINE_QDISC_CAST(GRED, GenericRandomEarlyDetection);
+extern const QDiscVTable gred_vtable;
+
+CONFIG_PARSER_PROTOTYPE(config_parse_generic_random_early_detection_u32);
+CONFIG_PARSER_PROTOTYPE(config_parse_generic_random_early_detection_bool);
diff --git a/src/network/tc/hhf.c b/src/network/tc/hhf.c
new file mode 100644 (file)
index 0000000..324a975
--- /dev/null
@@ -0,0 +1,98 @@
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright © 2020 VMware, Inc. */
+
+#include <linux/pkt_sched.h>
+
+#include "alloc-util.h"
+#include "conf-parser.h"
+#include "hhf.h"
+#include "netlink-util.h"
+#include "parse-util.h"
+#include "string-util.h"
+#include "util.h"
+
+static int heavy_hitter_filter_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
+        HeavyHitterFilter *hhf;
+        int r;
+
+        assert(link);
+        assert(qdisc);
+        assert(req);
+
+        hhf = HHF(qdisc);
+
+        r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "hhf");
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m");
+
+        if (hhf->packet_limit > 0) {
+                r = sd_netlink_message_append_u32(req, TCA_HHF_BACKLOG_LIMIT, hhf->packet_limit);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append TCA_HHF_BACKLOG_LIMIT attribute: %m");
+        }
+
+       r = sd_netlink_message_close_container(req);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m");
+
+        return 0;
+}
+
+int config_parse_heavy_hitter_filter_packet_limit(
+                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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
+        HeavyHitterFilter *hhf;
+        Network *network = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = qdisc_new_static(QDISC_KIND_HHF, network, filename, section_line, &qdisc);
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "More than one kind of queueing discipline, ignoring assignment: %m");
+                return 0;
+        }
+
+        hhf = HHF(qdisc);
+
+        if (isempty(rvalue)) {
+                hhf->packet_limit = 0;
+
+                qdisc = NULL;
+                return 0;
+        }
+
+        r = safe_atou32(rvalue, &hhf->packet_limit);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse '%s=', ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
+        }
+
+        qdisc = NULL;
+
+        return 0;
+}
+
+const QDiscVTable hhf_vtable = {
+        .object_size = sizeof(HeavyHitterFilter),
+        .tca_kind = "hhf",
+        .fill_message = heavy_hitter_filter_fill_message,
+};
diff --git a/src/network/tc/hhf.h b/src/network/tc/hhf.h
new file mode 100644 (file)
index 0000000..a555998
--- /dev/null
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright © 2020 VMware, Inc. */
+#pragma once
+
+#include "conf-parser.h"
+#include "qdisc.h"
+
+typedef struct HeavyHitterFilter {
+        QDisc meta;
+
+        uint32_t packet_limit;
+} HeavyHitterFilter;
+
+DEFINE_QDISC_CAST(HHF, HeavyHitterFilter);
+extern const QDiscVTable hhf_vtable;
+
+CONFIG_PARSER_PROTOTYPE(config_parse_heavy_hitter_filter_packet_limit);
diff --git a/src/network/tc/htb.c b/src/network/tc/htb.c
new file mode 100644 (file)
index 0000000..65481a7
--- /dev/null
@@ -0,0 +1,489 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <linux/pkt_sched.h>
+
+#include "alloc-util.h"
+#include "conf-parser.h"
+#include "netlink-util.h"
+#include "parse-util.h"
+#include "qdisc.h"
+#include "htb.h"
+#include "string-util.h"
+#include "tc-util.h"
+
+#define HTB_DEFAULT_RATE_TO_QUANTUM  10
+#define HTB_DEFAULT_MTU              1600  /* Ethernet packet length */
+
+static int hierarchy_token_bucket_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
+        HierarchyTokenBucket *htb;
+        struct tc_htb_glob opt = {
+                .version = 3,
+        };
+        int r;
+
+        assert(link);
+        assert(qdisc);
+        assert(req);
+
+        htb = HTB(qdisc);
+
+        opt.rate2quantum = htb->rate_to_quantum;
+        opt.defcls = htb->default_class;
+
+        r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "htb");
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m");
+
+        r = sd_netlink_message_append_data(req, TCA_HTB_INIT, &opt, sizeof(opt));
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not append TCA_HTB_INIT attribute: %m");
+
+        r = sd_netlink_message_close_container(req);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m");
+        return 0;
+}
+
+int config_parse_hierarchy_token_bucket_default_class(
+                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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
+        HierarchyTokenBucket *htb;
+        Network *network = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = qdisc_new_static(QDISC_KIND_HTB, network, filename, section_line, &qdisc);
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "More than one kind of queueing discipline, ignoring assignment: %m");
+                return 0;
+        }
+
+        htb = HTB(qdisc);
+
+        if (isempty(rvalue)) {
+                htb->default_class = 0;
+
+                qdisc = NULL;
+                return 0;
+        }
+
+        r = safe_atou32_full(rvalue, 16, &htb->default_class);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse '%s=', ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
+        }
+
+        qdisc = NULL;
+
+        return 0;
+}
+
+int config_parse_hierarchy_token_bucket_u32(
+                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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
+        HierarchyTokenBucket *htb;
+        Network *network = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = qdisc_new_static(QDISC_KIND_HTB, network, filename, section_line, &qdisc);
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "More than one kind of queueing discipline, ignoring assignment: %m");
+                return 0;
+        }
+
+        htb = HTB(qdisc);
+
+        if (isempty(rvalue)) {
+                htb->rate_to_quantum = HTB_DEFAULT_RATE_TO_QUANTUM;
+
+                qdisc = NULL;
+                return 0;
+        }
+
+        r = safe_atou32(rvalue, &htb->rate_to_quantum);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse '%s=', ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
+        }
+
+        qdisc = NULL;
+
+        return 0;
+}
+
+static int hierarchy_token_bucket_init(QDisc *qdisc) {
+        HierarchyTokenBucket *htb;
+
+        assert(qdisc);
+
+        htb = HTB(qdisc);
+
+        htb->rate_to_quantum = HTB_DEFAULT_RATE_TO_QUANTUM;
+
+        return 0;
+}
+
+const QDiscVTable htb_vtable = {
+        .object_size = sizeof(HierarchyTokenBucket),
+        .tca_kind = "htb",
+        .fill_message = hierarchy_token_bucket_fill_message,
+        .init = hierarchy_token_bucket_init,
+};
+
+static int hierarchy_token_bucket_class_fill_message(Link *link, TClass *tclass, sd_netlink_message *req) {
+        HierarchyTokenBucketClass *htb;
+        struct tc_htb_opt opt = {};
+        uint32_t rtab[256], ctab[256];
+        int r;
+
+        assert(link);
+        assert(tclass);
+        assert(req);
+
+        htb = TCLASS_TO_HTB(tclass);
+
+        opt.prio = htb->priority;
+        opt.quantum = htb->quantum;
+        opt.rate.rate = (htb->rate >= (1ULL << 32)) ? ~0U : htb->rate;
+        opt.ceil.rate = (htb->ceil_rate >= (1ULL << 32)) ? ~0U : htb->ceil_rate;
+        opt.rate.overhead = htb->overhead;
+        opt.ceil.overhead = htb->overhead;
+
+        r = tc_transmit_time(htb->rate, htb->buffer, &opt.buffer);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Failed to calculate buffer size: %m");
+
+        r = tc_transmit_time(htb->ceil_rate, htb->ceil_buffer, &opt.cbuffer);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Failed to calculate ceil buffer size: %m");
+
+        r = tc_fill_ratespec_and_table(&opt.rate, rtab, htb->mtu);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Failed to calculate rate table: %m");
+
+        r = tc_fill_ratespec_and_table(&opt.ceil, ctab, htb->mtu);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Failed to calculate ceil rate table: %m");
+
+        r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "htb");
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m");
+
+        r = sd_netlink_message_append_data(req, TCA_HTB_PARMS, &opt, sizeof(opt));
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not append TCA_HTB_PARMS attribute: %m");
+
+        if (htb->rate >= (1ULL << 32)) {
+                r = sd_netlink_message_append_u64(req, TCA_HTB_RATE64, htb->rate);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append TCA_HTB_RATE64 attribute: %m");
+        }
+
+        if (htb->ceil_rate >= (1ULL << 32)) {
+                r = sd_netlink_message_append_u64(req, TCA_HTB_CEIL64, htb->ceil_rate);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append TCA_HTB_CEIL64 attribute: %m");
+        }
+
+        r = sd_netlink_message_append_data(req, TCA_HTB_RTAB, rtab, sizeof(rtab));
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not append TCA_HTB_RTAB attribute: %m");
+
+        r = sd_netlink_message_append_data(req, TCA_HTB_CTAB, ctab, sizeof(ctab));
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not append TCA_HTB_CTAB attribute: %m");
+
+        r = sd_netlink_message_close_container(req);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m");
+        return 0;
+}
+
+int config_parse_hierarchy_token_bucket_class_u32(
+                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_(tclass_free_or_set_invalidp) TClass *tclass = NULL;
+        HierarchyTokenBucketClass *htb;
+        Network *network = data;
+        uint32_t v;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = tclass_new_static(TCLASS_KIND_HTB, network, filename, section_line, &tclass);
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to create traffic control class, ignoring assignment: %m");
+                return 0;
+        }
+
+        htb = TCLASS_TO_HTB(tclass);
+
+        if (isempty(rvalue)) {
+                htb->priority = 0;
+                tclass = NULL;
+                return 0;
+        }
+
+        r = safe_atou32(rvalue, &v);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse '%s=', ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
+        }
+
+        htb->priority = v;
+        tclass = NULL;
+
+        return 0;
+}
+
+int config_parse_hierarchy_token_bucket_class_size(
+                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_(tclass_free_or_set_invalidp) TClass *tclass = NULL;
+        HierarchyTokenBucketClass *htb;
+        Network *network = data;
+        uint64_t v;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = tclass_new_static(TCLASS_KIND_HTB, network, filename, section_line, &tclass);
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to create traffic control class, ignoring assignment: %m");
+                return 0;
+        }
+
+        htb = TCLASS_TO_HTB(tclass);
+
+        if (isempty(rvalue)) {
+                if (streq(lvalue, "QuantumBytes"))
+                        htb->quantum = 0;
+                else if (streq(lvalue, "MTUBytes"))
+                        htb->mtu = HTB_DEFAULT_MTU;
+                else if (streq(lvalue, "OverheadBytes"))
+                        htb->overhead = 0;
+                else if (streq(lvalue, "BufferBytes"))
+                        htb->buffer = 0;
+                else if (streq(lvalue, "CeilBufferBytes"))
+                        htb->ceil_buffer = 0;
+                else
+                        assert_not_reached("Invalid lvalue");
+
+                tclass = NULL;
+                return 0;
+        }
+
+        r = parse_size(rvalue, 1024, &v);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse '%s=', ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
+        }
+        if ((streq(lvalue, "OverheadBytes") && v > UINT16_MAX) || v > UINT32_MAX) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "Invalid '%s=', ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
+        }
+
+        if (streq(lvalue, "QuantumBytes"))
+                htb->quantum = v;
+        else if (streq(lvalue, "OverheadBytes"))
+                htb->overhead = v;
+        else if (streq(lvalue, "MTUBytes"))
+                htb->mtu = v;
+        else if (streq(lvalue, "BufferBytes"))
+                htb->buffer = v;
+        else if (streq(lvalue, "CeilBufferBytes"))
+                htb->ceil_buffer = v;
+        else
+                assert_not_reached("Invalid lvalue");
+
+        tclass = NULL;
+
+        return 0;
+}
+
+int config_parse_hierarchy_token_bucket_class_rate(
+                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_(tclass_free_or_set_invalidp) TClass *tclass = NULL;
+        HierarchyTokenBucketClass *htb;
+        Network *network = data;
+        uint64_t *v;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = tclass_new_static(TCLASS_KIND_HTB, network, filename, section_line, &tclass);
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to create traffic control class, ignoring assignment: %m");
+                return 0;
+        }
+
+        htb = TCLASS_TO_HTB(tclass);
+        if (streq(lvalue, "Rate"))
+                v = &htb->rate;
+        else if (streq(lvalue, "CeilRate"))
+                v = &htb->ceil_rate;
+        else
+                assert_not_reached("Invalid lvalue");
+
+        if (isempty(rvalue)) {
+                *v = 0;
+
+                tclass = NULL;
+                return 0;
+        }
+
+        r = parse_size(rvalue, 1000, v);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse '%s=', ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
+        }
+
+        *v /= 8;
+        tclass = NULL;
+
+        return 0;
+}
+
+static int hierarchy_token_bucket_class_init(TClass *tclass) {
+        HierarchyTokenBucketClass *htb;
+
+        assert(tclass);
+
+        htb = TCLASS_TO_HTB(tclass);
+
+        htb->mtu = HTB_DEFAULT_MTU;
+
+        return 0;
+}
+
+static int hierarchy_token_bucket_class_verify(TClass *tclass) {
+        HierarchyTokenBucketClass *htb;
+        uint32_t hz;
+        int r;
+
+        assert(tclass);
+
+        htb = TCLASS_TO_HTB(tclass);
+
+        if (htb->rate == 0)
+                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
+                                         "%s: Rate= is mandatory. "
+                                         "Ignoring [HierarchyTokenBucketClass] section from line %u.",
+                                         tclass->section->filename, tclass->section->line);
+
+        /* if CeilRate= setting is missing, use the same as Rate= */
+        if (htb->ceil_rate == 0)
+                htb->ceil_rate = htb->rate;
+
+        r = tc_init(NULL, &hz);
+        if (r < 0)
+                return log_error_errno(r, "Failed to read /proc/net/psched: %m");
+
+        if (htb->buffer == 0)
+                htb->buffer = htb->rate / hz + htb->mtu;
+        if (htb->ceil_buffer == 0)
+                htb->ceil_buffer = htb->ceil_rate / hz + htb->mtu;
+
+        return 0;
+}
+
+const TClassVTable htb_tclass_vtable = {
+        .object_size = sizeof(HierarchyTokenBucketClass),
+        .tca_kind = "htb",
+        .fill_message = hierarchy_token_bucket_class_fill_message,
+        .init = hierarchy_token_bucket_class_init,
+        .verify = hierarchy_token_bucket_class_verify,
+};
diff --git a/src/network/tc/htb.h b/src/network/tc/htb.h
new file mode 100644 (file)
index 0000000..b385872
--- /dev/null
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "conf-parser.h"
+#include "qdisc.h"
+#include "tclass.h"
+
+typedef struct HierarchyTokenBucket {
+        QDisc meta;
+
+        uint32_t default_class;
+        uint32_t rate_to_quantum;
+} HierarchyTokenBucket;
+
+DEFINE_QDISC_CAST(HTB, HierarchyTokenBucket);
+extern const QDiscVTable htb_vtable;
+
+CONFIG_PARSER_PROTOTYPE(config_parse_hierarchy_token_bucket_default_class);
+CONFIG_PARSER_PROTOTYPE(config_parse_hierarchy_token_bucket_u32);
+
+typedef struct HierarchyTokenBucketClass {
+        TClass meta;
+
+        uint32_t priority;
+        uint32_t quantum;
+        uint32_t mtu;
+        uint16_t overhead;
+        uint64_t rate;
+        uint32_t buffer;
+        uint64_t ceil_rate;
+        uint32_t ceil_buffer;
+} HierarchyTokenBucketClass;
+
+DEFINE_TCLASS_CAST(HTB, HierarchyTokenBucketClass);
+extern const TClassVTable htb_tclass_vtable;
+
+CONFIG_PARSER_PROTOTYPE(config_parse_hierarchy_token_bucket_class_u32);
+CONFIG_PARSER_PROTOTYPE(config_parse_hierarchy_token_bucket_class_size);
+CONFIG_PARSER_PROTOTYPE(config_parse_hierarchy_token_bucket_class_rate);
index 7d0add9e303f813f46ee3ba70b84c9f7f495a0b1..a94a9a369e966f1f2b8a49d54fc8a6279e6f3406 100644 (file)
@@ -80,9 +80,11 @@ int config_parse_network_emulator_delay(
         r = qdisc_new_static(QDISC_KIND_NETEM, network, filename, section_line, &qdisc);
         if (r == -ENOMEM)
                 return log_oom();
-        if (r < 0)
-                return log_syntax(unit, LOG_ERR, filename, line, r,
-                                  "More than one kind of queueing discipline, ignoring assignment: %m");
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "More than one kind of queueing discipline, ignoring assignment: %m");
+                return 0;
+        }
 
         ne = NETEM(qdisc);
 
@@ -98,7 +100,7 @@ int config_parse_network_emulator_delay(
 
         r = parse_sec(rvalue, &u);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to parse '%s=', ignoring assignment: %s",
                            lvalue, rvalue);
                 return 0;
@@ -140,9 +142,11 @@ int config_parse_network_emulator_rate(
         r = qdisc_new_static(QDISC_KIND_NETEM, network, filename, section_line, &qdisc);
         if (r == -ENOMEM)
                 return log_oom();
-        if (r < 0)
-                return log_syntax(unit, LOG_ERR, filename, line, r,
-                                  "More than one kind of queueing discipline, ignoring assignment: %m");
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "More than one kind of queueing discipline, ignoring assignment: %m");
+                return 0;
+        }
 
         ne = NETEM(qdisc);
 
@@ -158,7 +162,7 @@ int config_parse_network_emulator_rate(
 
         r = parse_tc_percent(rvalue, &rate);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to parse '%s=', ignoring assignment: %s",
                            lvalue, rvalue);
                 return 0;
@@ -198,9 +202,11 @@ int config_parse_network_emulator_packet_limit(
         r = qdisc_new_static(QDISC_KIND_NETEM, network, filename, section_line, &qdisc);
         if (r == -ENOMEM)
                 return log_oom();
-        if (r < 0)
-                return log_syntax(unit, LOG_ERR, filename, line, r,
-                                  "More than one kind of queueing discipline, ignoring assignment: %m");
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "More than one kind of queueing discipline, ignoring assignment: %m");
+                return 0;
+        }
 
         ne = NETEM(qdisc);
 
@@ -213,7 +219,7 @@ int config_parse_network_emulator_packet_limit(
 
         r = safe_atou(rvalue, &ne->limit);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to parse '%s=', ignoring assignment: %s",
                            lvalue, rvalue);
                 return 0;
diff --git a/src/network/tc/pie.c b/src/network/tc/pie.c
new file mode 100644 (file)
index 0000000..eccbaa2
--- /dev/null
@@ -0,0 +1,97 @@
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright © 2020 VMware, Inc. */
+
+#include <linux/pkt_sched.h>
+
+#include "alloc-util.h"
+#include "conf-parser.h"
+#include "pie.h"
+#include "netlink-util.h"
+#include "parse-util.h"
+#include "string-util.h"
+
+static int pie_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
+        ProportionalIntegralControllerEnhanced *pie;
+        int r;
+
+        assert(link);
+        assert(qdisc);
+        assert(req);
+
+        pie = PIE(qdisc);
+
+        r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "pie");
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m");
+
+        if (pie->packet_limit > 0) {
+                r = sd_netlink_message_append_u32(req, TCA_PIE_LIMIT, pie->packet_limit);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append TCA_PIE_PLIMIT attribute: %m");
+        }
+
+        r = sd_netlink_message_close_container(req);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m");
+
+        return 0;
+}
+
+int config_parse_pie_packet_limit(
+                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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
+        ProportionalIntegralControllerEnhanced *pie;
+        Network *network = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = qdisc_new_static(QDISC_KIND_PIE, network, filename, section_line, &qdisc);
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "More than one kind of queueing discipline, ignoring assignment: %m");
+                return 0;
+        }
+
+        pie = PIE(qdisc);
+
+        if (isempty(rvalue)) {
+                pie->packet_limit = 0;
+
+                qdisc = NULL;
+                return 0;
+        }
+
+        r = safe_atou32(rvalue, &pie->packet_limit);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse '%s=', ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
+        }
+
+        qdisc = NULL;
+
+        return 0;
+}
+
+const QDiscVTable pie_vtable = {
+        .object_size = sizeof(ProportionalIntegralControllerEnhanced),
+        .tca_kind = "pie",
+        .fill_message = pie_fill_message,
+};
diff --git a/src/network/tc/pie.h b/src/network/tc/pie.h
new file mode 100644 (file)
index 0000000..7c76474
--- /dev/null
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright © 2020 VMware, Inc. */
+#pragma once
+
+#include "conf-parser.h"
+#include "qdisc.h"
+
+typedef struct ProportionalIntegralControllerEnhanced {
+        QDisc meta;
+
+        uint32_t packet_limit;
+} ProportionalIntegralControllerEnhanced;
+
+DEFINE_QDISC_CAST(PIE, ProportionalIntegralControllerEnhanced);
+extern const QDiscVTable pie_vtable;
+
+CONFIG_PARSER_PROTOTYPE(config_parse_pie_packet_limit);
index 0619e894cca94da4d3f8abdbd60eceb4b79fc2de..e1262c12840754090a468a9dd573b536adccbcdf 100644 (file)
 #include "qdisc.h"
 #include "set.h"
 #include "string-util.h"
+#include "strv.h"
+#include "tc-util.h"
 
 const QDiscVTable * const qdisc_vtable[_QDISC_KIND_MAX] = {
+        [QDISC_KIND_BFIFO] = &bfifo_vtable,
+        [QDISC_KIND_CAKE] = &cake_vtable,
         [QDISC_KIND_CODEL] = &codel_vtable,
+        [QDISC_KIND_DRR] = &drr_vtable,
+        [QDISC_KIND_ETS] = &ets_vtable,
         [QDISC_KIND_FQ] = &fq_vtable,
         [QDISC_KIND_FQ_CODEL] = &fq_codel_vtable,
+        [QDISC_KIND_GRED] = &gred_vtable,
+        [QDISC_KIND_HHF] = &hhf_vtable,
+        [QDISC_KIND_HTB] = &htb_vtable,
         [QDISC_KIND_NETEM] = &netem_vtable,
+        [QDISC_KIND_PIE] = &pie_vtable,
+        [QDISC_KIND_QFQ] = &qfq_vtable,
+        [QDISC_KIND_PFIFO] = &pfifo_vtable,
+        [QDISC_KIND_PFIFO_FAST] = &pfifo_fast_vtable,
+        [QDISC_KIND_PFIFO_HEAD_DROP] = &pfifo_head_drop_vtable,
+        [QDISC_KIND_SFB] = &sfb_vtable,
         [QDISC_KIND_SFQ] = &sfq_vtable,
         [QDISC_KIND_TBF] = &tbf_vtable,
         [QDISC_KIND_TEQL] = &teql_vtable,
 };
 
 static int qdisc_new(QDiscKind kind, QDisc **ret) {
-        QDisc *qdisc;
+        _cleanup_(qdisc_freep) QDisc *qdisc = NULL;
         int r;
 
         if (kind == _QDISC_KIND_INVALID) {
@@ -33,6 +48,7 @@ static int qdisc_new(QDiscKind kind, QDisc **ret) {
                         return -ENOMEM;
 
                 *qdisc = (QDisc) {
+                        .meta.kind = TC_KIND_QDISC,
                         .family = AF_UNSPEC,
                         .parent = TC_H_ROOT,
                         .kind = kind,
@@ -42,6 +58,7 @@ static int qdisc_new(QDiscKind kind, QDisc **ret) {
                 if (!qdisc)
                         return -ENOMEM;
 
+                qdisc->meta.kind = TC_KIND_QDISC,
                 qdisc->family = AF_UNSPEC;
                 qdisc->parent = TC_H_ROOT;
                 qdisc->kind = kind;
@@ -61,7 +78,8 @@ static int qdisc_new(QDiscKind kind, QDisc **ret) {
 int qdisc_new_static(QDiscKind kind, Network *network, const char *filename, unsigned section_line, QDisc **ret) {
         _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
         _cleanup_(qdisc_freep) QDisc *qdisc = NULL;
-        QDisc *existing;
+        TrafficControl *existing;
+        QDisc *q = NULL;
         int r;
 
         assert(network);
@@ -73,15 +91,20 @@ int qdisc_new_static(QDiscKind kind, Network *network, const char *filename, uns
         if (r < 0)
                 return r;
 
-        existing = ordered_hashmap_get(network->qdiscs_by_section, n);
+        existing = ordered_hashmap_get(network->tc_by_section, n);
         if (existing) {
-                if (existing->kind != _QDISC_KIND_INVALID &&
+                if (existing->kind != TC_KIND_QDISC)
+                        return -EINVAL;
+
+                q = TC_TO_QDISC(existing);
+
+                if (q->kind != _QDISC_KIND_INVALID &&
                     kind != _QDISC_KIND_INVALID &&
-                    existing->kind != kind)
+                    q->kind != kind)
                         return -EINVAL;
 
-                if (existing->kind == kind || kind == _QDISC_KIND_INVALID) {
-                        *ret = existing;
+                if (q->kind == kind || kind == _QDISC_KIND_INVALID) {
+                        *ret = q;
                         return 0;
                 }
         }
@@ -90,23 +113,23 @@ int qdisc_new_static(QDiscKind kind, Network *network, const char *filename, uns
         if (r < 0)
                 return r;
 
-        if (existing) {
-                qdisc->family = existing->family;
-                qdisc->handle = existing->handle;
-                qdisc->parent = existing->parent;
-                qdisc->tca_kind = TAKE_PTR(existing->tca_kind);
+        if (q) {
+                qdisc->family = q->family;
+                qdisc->handle = q->handle;
+                qdisc->parent = q->parent;
+                qdisc->tca_kind = TAKE_PTR(q->tca_kind);
 
-                qdisc_free(ordered_hashmap_remove(network->qdiscs_by_section, n));
+                qdisc_free(q);
         }
 
         qdisc->network = network;
         qdisc->section = TAKE_PTR(n);
 
-        r = ordered_hashmap_ensure_allocated(&network->qdiscs_by_section, &network_config_hash_ops);
+        r = ordered_hashmap_ensure_allocated(&network->tc_by_section, &network_config_hash_ops);
         if (r < 0)
                 return r;
 
-        r = ordered_hashmap_put(network->qdiscs_by_section, qdisc->section, qdisc);
+        r = ordered_hashmap_put(network->tc_by_section, qdisc->section, TC(qdisc));
         if (r < 0)
                 return r;
 
@@ -119,7 +142,7 @@ void qdisc_free(QDisc *qdisc) {
                 return;
 
         if (qdisc->network && qdisc->section)
-                ordered_hashmap_remove(qdisc->network->qdiscs_by_section, qdisc->section);
+                ordered_hashmap_remove(qdisc->network->tc_by_section, qdisc->section);
 
         network_config_section_free(qdisc->section);
 
@@ -131,8 +154,8 @@ static int qdisc_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
         int r;
 
         assert(link);
-        assert(link->qdisc_messages > 0);
-        link->qdisc_messages--;
+        assert(link->tc_messages > 0);
+        link->tc_messages--;
 
         if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
                 return 1;
@@ -144,9 +167,9 @@ static int qdisc_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
                 return 1;
         }
 
-        if (link->qdisc_messages == 0) {
-                log_link_debug(link, "QDisc configured");
-                link->qdiscs_configured = true;
+        if (link->tc_messages == 0) {
+                log_link_debug(link, "Traffic control configured");
+                link->tc_configured = true;
                 link_check_ready(link);
         }
 
@@ -203,7 +226,7 @@ int qdisc_configure(Link *link, QDisc *qdisc) {
                 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
 
         link_ref(link);
-        link->qdisc_messages++;
+        link->tc_messages++;
 
         return 0;
 }
@@ -265,8 +288,13 @@ int config_parse_qdisc_parent(
         assert(data);
 
         r = qdisc_new_static(ltype, network, filename, section_line, &qdisc);
-        if (r < 0)
-                return r;
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "More than one kind of queueing discipline, ignoring assignment: %m");
+                return 0;
+        }
 
         if (streq(rvalue, "root")) {
                 qdisc->parent = TC_H_ROOT;
@@ -279,19 +307,21 @@ int config_parse_qdisc_parent(
                 qdisc->parent = TC_H_INGRESS;
                 qdisc->handle = TC_H_MAKE(TC_H_INGRESS, 0);
         } else {
-                log_syntax(unit, LOG_ERR, filename, line, r,
-                           "Failed to parse 'Parent=', ignoring assignment: %s",
-                           rvalue);
-                return 0;
+                r = parse_handle(rvalue, &qdisc->parent);
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to parse 'Parent=', ignoring assignment: %s",
+                                   rvalue);
+                        return 0;
+                }
         }
 
-        if (streq(rvalue, "root"))
-                qdisc->tca_kind = mfree(qdisc->tca_kind);
-        else {
+        if (STR_IN_SET(rvalue, "clsact", "ingress")) {
                 r = free_and_strdup(&qdisc->tca_kind, rvalue);
                 if (r < 0)
                         return log_oom();
-        }
+        } else
+                qdisc->tca_kind = mfree(qdisc->tca_kind);
 
         qdisc = NULL;
 
@@ -321,8 +351,13 @@ int config_parse_qdisc_handle(
         assert(data);
 
         r = qdisc_new_static(ltype, network, filename, section_line, &qdisc);
-        if (r < 0)
-                return r;
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "More than one kind of queueing discipline, ignoring assignment: %m");
+                return 0;
+        }
 
         if (isempty(rvalue)) {
                 qdisc->handle = TC_H_UNSPEC;
@@ -332,7 +367,7 @@ int config_parse_qdisc_handle(
 
         r = safe_atou16_full(rvalue, 16, &n);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to parse 'Handle=', ignoring assignment: %s",
                            rvalue);
                 return 0;
index 8e4a70de539f158105d32a69a859b83a38e995dc..0c9c0544b6c9b95e0d9e8fdbb978f76924eecd26 100644 (file)
@@ -6,12 +6,26 @@
 #include "networkd-link.h"
 #include "networkd-network.h"
 #include "networkd-util.h"
+#include "tc.h"
 
 typedef enum QDiscKind {
+        QDISC_KIND_BFIFO,
+        QDISC_KIND_CAKE,
         QDISC_KIND_CODEL,
+        QDISC_KIND_DRR,
+        QDISC_KIND_ETS,
         QDISC_KIND_FQ,
         QDISC_KIND_FQ_CODEL,
+        QDISC_KIND_GRED,
+        QDISC_KIND_HHF,
+        QDISC_KIND_HTB,
         QDISC_KIND_NETEM,
+        QDISC_KIND_PFIFO,
+        QDISC_KIND_PFIFO_FAST,
+        QDISC_KIND_PFIFO_HEAD_DROP,
+        QDISC_KIND_PIE,
+        QDISC_KIND_QFQ,
+        QDISC_KIND_SFB,
         QDISC_KIND_SFQ,
         QDISC_KIND_TBF,
         QDISC_KIND_TEQL,
@@ -20,6 +34,8 @@ typedef enum QDiscKind {
 } QDiscKind;
 
 typedef struct QDisc {
+        TrafficControl meta;
+
         NetworkConfigSection *section;
         Network *network;
 
@@ -65,13 +81,25 @@ int qdisc_section_verify(QDisc *qdisc, bool *has_root, bool *has_clsact);
 
 DEFINE_NETWORK_SECTION_FUNCTIONS(QDisc, qdisc_free);
 
+DEFINE_TC_CAST(QDISC, QDisc);
+
 CONFIG_PARSER_PROTOTYPE(config_parse_qdisc_parent);
 CONFIG_PARSER_PROTOTYPE(config_parse_qdisc_handle);
 
+#include "cake.h"
 #include "codel.h"
+#include "ets.h"
+#include "fifo.h"
 #include "fq-codel.h"
 #include "fq.h"
+#include "gred.h"
+#include "hhf.h"
+#include "htb.h"
+#include "pie.h"
+#include "qfq.h"
 #include "netem.h"
+#include "drr.h"
+#include "sfb.h"
 #include "sfq.h"
 #include "tbf.h"
 #include "teql.h"
diff --git a/src/network/tc/qfq.c b/src/network/tc/qfq.c
new file mode 100644 (file)
index 0000000..2104067
--- /dev/null
@@ -0,0 +1,178 @@
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright © 2020 VMware, Inc. */
+
+#include <linux/pkt_sched.h>
+
+#include "parse-util.h"
+#include "qdisc.h"
+#include "qfq.h"
+#include "string-util.h"
+
+#define QFQ_MAX_WEIGHT       (1 << 10)
+#define QFQ_MIN_MAX_PACKET   512
+#define QFQ_MAX_MAX_PACKET   (1 << 16)
+
+const QDiscVTable qfq_vtable = {
+        .object_size = sizeof(QuickFairQueueing),
+        .tca_kind = "qfq",
+};
+
+static int quick_fair_queueing_class_fill_message(Link *link, TClass *tclass, sd_netlink_message *req) {
+        QuickFairQueueingClass *qfq;
+        int r;
+
+        assert(link);
+        assert(tclass);
+        assert(req);
+
+        qfq = TCLASS_TO_QFQ(tclass);
+
+        r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "qfq");
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m");
+
+        if (qfq->weight > 0) {
+                r = sd_netlink_message_append_u32(req, TCA_QFQ_WEIGHT, qfq->weight);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append TCA_QFQ_WEIGHT attribute: %m");
+        }
+
+        if (qfq->max_packet > 0) {
+                r = sd_netlink_message_append_u32(req, TCA_QFQ_LMAX, qfq->max_packet);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not append TCA_QFQ_LMAX attribute: %m");
+        }
+
+        r = sd_netlink_message_close_container(req);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m");
+        return 0;
+}
+
+int config_parse_quick_fair_queueing_weight(
+                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_(tclass_free_or_set_invalidp) TClass *tclass = NULL;
+        QuickFairQueueingClass *qfq;
+        Network *network = data;
+        uint32_t v;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = tclass_new_static(TCLASS_KIND_QFQ, network, filename, section_line, &tclass);
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to create traffic control class, ignoring assignment: %m");
+                return 0;
+        }
+
+        qfq = TCLASS_TO_QFQ(tclass);
+
+        if (isempty(rvalue)) {
+                qfq->weight = 0;
+                tclass = NULL;
+                return 0;
+        }
+
+        r = safe_atou32(rvalue, &v);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse '%s=', ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
+        }
+
+        if (v == 0 || v > QFQ_MAX_WEIGHT) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "Invalid '%s=', ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
+        }
+
+        qfq->weight = v;
+        tclass = NULL;
+
+        return 0;
+}
+
+int config_parse_quick_fair_queueing_max_packet(
+                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_(tclass_free_or_set_invalidp) TClass *tclass = NULL;
+        QuickFairQueueingClass *qfq;
+        Network *network = data;
+        uint64_t v;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = tclass_new_static(TCLASS_KIND_QFQ, network, filename, section_line, &tclass);
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to create traffic control class, ignoring assignment: %m");
+                return 0;
+        }
+
+        qfq = TCLASS_TO_QFQ(tclass);
+
+        if (isempty(rvalue)) {
+                qfq->max_packet = 0;
+                tclass = NULL;
+                return 0;
+        }
+
+        r = parse_size(rvalue, 1024, &v);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse '%s=', ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
+        }
+
+        if (v < QFQ_MIN_MAX_PACKET || v > QFQ_MAX_MAX_PACKET) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "Invalid '%s=', ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
+        }
+
+        qfq->max_packet = (uint32_t) v;
+        tclass = NULL;
+
+        return 0;
+}
+
+const TClassVTable qfq_tclass_vtable = {
+        .object_size = sizeof(QuickFairQueueingClass),
+        .tca_kind = "qfq",
+        .fill_message = quick_fair_queueing_class_fill_message,
+};
diff --git a/src/network/tc/qfq.h b/src/network/tc/qfq.h
new file mode 100644 (file)
index 0000000..10bab3e
--- /dev/null
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright © 2020 VMware, Inc. */
+#pragma once
+
+#include "conf-parser.h"
+#include "qdisc.h"
+
+typedef struct QuickFairQueueing {
+        QDisc meta;
+} QuickFairQueueing;
+
+DEFINE_QDISC_CAST(QFQ, QuickFairQueueing);
+extern const QDiscVTable qfq_vtable;
+
+typedef struct QuickFairQueueingClass {
+        TClass meta;
+
+        uint32_t weight;
+        uint32_t max_packet;
+} QuickFairQueueingClass;
+
+DEFINE_TCLASS_CAST(QFQ, QuickFairQueueingClass);
+extern const TClassVTable qfq_tclass_vtable;
+
+CONFIG_PARSER_PROTOTYPE(config_parse_quick_fair_queueing_weight);
+CONFIG_PARSER_PROTOTYPE(config_parse_quick_fair_queueing_max_packet);
diff --git a/src/network/tc/sfb.c b/src/network/tc/sfb.c
new file mode 100644 (file)
index 0000000..3692a50
--- /dev/null
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright © 2020 VMware, Inc. */
+
+#include <linux/pkt_sched.h>
+
+#include "alloc-util.h"
+#include "conf-parser.h"
+#include "netlink-util.h"
+#include "parse-util.h"
+#include "qdisc.h"
+#include "sfb.h"
+#include "string-util.h"
+
+static int stochastic_fair_blue_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
+        StochasticFairBlue *sfb;
+        struct tc_sfb_qopt opt = {
+            .rehash_interval = 600*1000,
+            .warmup_time = 60*1000,
+            .penalty_rate = 10,
+            .penalty_burst = 20,
+            .increment = (SFB_MAX_PROB + 1000) / 2000,
+            .decrement = (SFB_MAX_PROB + 10000) / 20000,
+            .max = 25,
+            .bin_size = 20,
+        };
+        int r;
+
+        assert(link);
+        assert(qdisc);
+        assert(req);
+
+        sfb = SFB(qdisc);
+
+        opt.limit = sfb->packet_limit;
+
+        r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "sfb");
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m");
+
+        r = sd_netlink_message_append_data(req, TCA_SFB_PARMS, &opt, sizeof(struct tc_sfb_qopt));
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not append TCA_SFB_PARMS attribute: %m");
+
+        r = sd_netlink_message_close_container(req);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m");
+
+        return 0;
+}
+
+int config_parse_stochastic_fair_blue_u32(
+                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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
+        StochasticFairBlue *sfb;
+        Network *network = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = qdisc_new_static(QDISC_KIND_SFB, network, filename, section_line, &qdisc);
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "More than one kind of queueing discipline, ignoring assignment: %m");
+                return 0;
+        }
+
+        sfb = SFB(qdisc);
+
+        if (isempty(rvalue)) {
+                sfb->packet_limit = 0;
+
+                qdisc = NULL;
+                return 0;
+        }
+
+        r = safe_atou32(rvalue, &sfb->packet_limit);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse '%s=', ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
+        }
+
+        qdisc = NULL;
+
+        return 0;
+}
+
+const QDiscVTable sfb_vtable = {
+        .object_size = sizeof(StochasticFairBlue),
+        .tca_kind = "sfb",
+        .fill_message = stochastic_fair_blue_fill_message,
+};
diff --git a/src/network/tc/sfb.h b/src/network/tc/sfb.h
new file mode 100644 (file)
index 0000000..3cc87d7
--- /dev/null
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright © 2020 VMware, Inc. */
+#pragma once
+
+#include "conf-parser.h"
+#include "qdisc.h"
+
+typedef struct StochasticFairBlue {
+        QDisc meta;
+
+        uint32_t packet_limit;
+} StochasticFairBlue;
+
+DEFINE_QDISC_CAST(SFB, StochasticFairBlue);
+extern const QDiscVTable sfb_vtable;
+
+CONFIG_PARSER_PROTOTYPE(config_parse_stochastic_fair_blue_u32);
index ee66409b94abc2edab5c8ef74a1aeaf471e8338d..d671281c46a0c95b7eddc65892c7b3ec8b4d3377 100644 (file)
@@ -56,9 +56,11 @@ int config_parse_stochastic_fairness_queueing_perturb_period(
         r = qdisc_new_static(QDISC_KIND_SFQ, network, filename, section_line, &qdisc);
         if (r == -ENOMEM)
                 return log_oom();
-        if (r < 0)
-                return log_syntax(unit, LOG_ERR, filename, line, r,
-                                  "More than one kind of queueing discipline, ignoring assignment: %m");
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "More than one kind of queueing discipline, ignoring assignment: %m");
+                return 0;
+        }
 
         sfq = SFQ(qdisc);
 
@@ -71,7 +73,7 @@ int config_parse_stochastic_fairness_queueing_perturb_period(
 
         r = parse_sec(rvalue, &sfq->perturb_period);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to parse '%s=', ignoring assignment: %s",
                            lvalue, rvalue);
                 return 0;
index 0682ab4cc6575463aa0ad0dc1654149b0d1bfd87..cb3c3bcc15c34a9c402753a3d74f95911bd0f67d 100644 (file)
@@ -12,8 +12,8 @@
 #include "parse-util.h"
 #include "qdisc.h"
 #include "string-util.h"
+#include "strv.h"
 #include "tc-util.h"
-#include "util.h"
 
 static int token_bucket_filter_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
         uint32_t rtab[256], ptab[256];
@@ -136,50 +136,110 @@ int config_parse_token_bucket_filter_size(
         r = qdisc_new_static(QDISC_KIND_TBF, network, filename, section_line, &qdisc);
         if (r == -ENOMEM)
                 return log_oom();
-        if (r < 0)
-                return log_syntax(unit, LOG_ERR, filename, line, r,
-                                  "More than one kind of queueing discipline, ignoring assignment: %m");
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "More than one kind of queueing discipline, ignoring assignment: %m");
+                return 0;
+        }
 
         tbf = TBF(qdisc);
 
         if (isempty(rvalue)) {
-                if (streq(lvalue, "Rate"))
-                        tbf->rate = 0;
-                else if (streq(lvalue, "Burst"))
+                if (STR_IN_SET(lvalue, "BurstBytes", "Burst"))
                         tbf->burst = 0;
-                else if (streq(lvalue, "LimitSize"))
+                else if (STR_IN_SET(lvalue, "LimitBytes", "LimitSize"))
                         tbf->limit = 0;
                 else if (streq(lvalue, "MTUBytes"))
                         tbf->mtu = 0;
                 else if (streq(lvalue, "MPUBytes"))
                         tbf->mpu = 0;
-                else if (streq(lvalue, "PeakRate"))
-                        tbf->peak_rate = 0;
+                else
+                        assert_not_reached("unknown lvalue");
 
                 qdisc = NULL;
                 return 0;
         }
 
-        r = parse_size(rvalue, 1000, &k);
+        r = parse_size(rvalue, 1024, &k);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to parse '%s=', ignoring assignment: %s",
                            lvalue, rvalue);
                 return 0;
         }
 
-        if (streq(lvalue, "Rate"))
-                tbf->rate = k / 8;
-        else if (streq(lvalue, "Burst"))
+        if (STR_IN_SET(lvalue, "BurstBytes", "Burst"))
                 tbf->burst = k;
-        else if (streq(lvalue, "LimitSize"))
+        else if (STR_IN_SET(lvalue, "LimitBytes", "LimitSize"))
                 tbf->limit = k;
         else if (streq(lvalue, "MPUBytes"))
                 tbf->mpu = k;
         else if (streq(lvalue, "MTUBytes"))
                 tbf->mtu = k;
+        else
+                assert_not_reached("unknown lvalue");
+
+        qdisc = NULL;
+
+        return 0;
+}
+
+int config_parse_token_bucket_filter_rate(
+                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_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
+        Network *network = data;
+        TokenBucketFilter *tbf;
+        uint64_t k, *p;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = qdisc_new_static(QDISC_KIND_TBF, network, filename, section_line, &qdisc);
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "More than one kind of queueing discipline, ignoring assignment: %m");
+                return 0;
+        }
+
+        tbf = TBF(qdisc);
+        if (streq(lvalue, "Rate"))
+                p = &tbf->rate;
         else if (streq(lvalue, "PeakRate"))
-                tbf->peak_rate = k / 8;
+                p = &tbf->peak_rate;
+        else
+                assert_not_reached("unknown lvalue");
+
+        if (isempty(rvalue)) {
+                *p = 0;
+
+                qdisc = NULL;
+                return 0;
+        }
+
+        r = parse_size(rvalue, 1000, &k);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse '%s=', ignoring assignment: %s",
+                           lvalue, rvalue);
+                return 0;
+        }
+
+        *p = k / 8;
 
         qdisc = NULL;
 
@@ -212,9 +272,11 @@ int config_parse_token_bucket_filter_latency(
         r = qdisc_new_static(QDISC_KIND_TBF, network, filename, section_line, &qdisc);
         if (r == -ENOMEM)
                 return log_oom();
-        if (r < 0)
-                return log_syntax(unit, LOG_ERR, filename, line, r,
-                                  "More than one kind of queueing discipline, ignoring assignment: %m");
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "More than one kind of queueing discipline, ignoring assignment: %m");
+                return 0;
+        }
 
         tbf = TBF(qdisc);
 
@@ -227,7 +289,7 @@ int config_parse_token_bucket_filter_latency(
 
         r = parse_sec(rvalue, &u);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to parse '%s=', ignoring assignment: %s",
                            lvalue, rvalue);
                 return 0;
index b66aef206c49b7455703262ead378d81b12c0964..a785be25e0e60b287ef11458c8da9407cc6f5eca 100644 (file)
@@ -23,3 +23,4 @@ extern const QDiscVTable tbf_vtable;
 
 CONFIG_PARSER_PROTOTYPE(config_parse_token_bucket_filter_latency);
 CONFIG_PARSER_PROTOTYPE(config_parse_token_bucket_filter_size);
+CONFIG_PARSER_PROTOTYPE(config_parse_token_bucket_filter_rate);
index c46550f955942907b7ba8f99c1d7be9c61cc1650..8a5afeab2d03c66a8dda71f8fadd2164daf9ea2c 100644 (file)
@@ -2,43 +2,52 @@
  * Copyright © 2019 VMware, Inc. */
 
 #include "alloc-util.h"
+#include "extract-word.h"
 #include "fileio.h"
 #include "parse-util.h"
 #include "tc-util.h"
 #include "time-util.h"
 
-static int tc_init(double *ticks_in_usec) {
-        uint32_t clock_resolution, ticks_to_usec, usec_to_ticks;
-        _cleanup_free_ char *line = NULL;
-        double clock_factor;
-        int r;
+int tc_init(double *ret_ticks_in_usec, uint32_t *ret_hz) {
+        static double ticks_in_usec = -1;
+        static uint32_t hz;
 
-        r = read_one_line_file("/proc/net/psched", &line);
-        if (r < 0)
-                return r;
+        if (ticks_in_usec < 0) {
+                uint32_t clock_resolution, ticks_to_usec, usec_to_ticks;
+                _cleanup_free_ char *line = NULL;
+                double clock_factor;
+                int r;
 
-        r = sscanf(line, "%08x%08x%08x", &ticks_to_usec, &usec_to_ticks, &clock_resolution);
-        if (r < 3)
-                return -EIO;
+                r = read_one_line_file("/proc/net/psched", &line);
+                if (r < 0)
+                        return r;
 
-        clock_factor =  (double) clock_resolution / USEC_PER_SEC;
-        *ticks_in_usec = (double) ticks_to_usec / usec_to_ticks * clock_factor;
+                r = sscanf(line, "%08x%08x%08x%08x", &ticks_to_usec, &usec_to_ticks, &clock_resolution, &hz);
+                if (r < 4)
+                        return -EIO;
+
+                clock_factor = (double) clock_resolution / USEC_PER_SEC;
+                ticks_in_usec = (double) ticks_to_usec / usec_to_ticks * clock_factor;
+        }
+
+        if (ret_ticks_in_usec)
+                *ret_ticks_in_usec = ticks_in_usec;
+        if (ret_hz)
+                *ret_hz = hz;
 
         return 0;
 }
 
 int tc_time_to_tick(usec_t t, uint32_t *ret) {
-        static double ticks_in_usec = -1;
+        double ticks_in_usec;
         usec_t a;
         int r;
 
         assert(ret);
 
-        if (ticks_in_usec < 0) {
-                r = tc_init(&ticks_in_usec);
-                if (r < 0)
-                        return r;
-        }
+        r = tc_init(&ticks_in_usec, NULL);
+        if (r < 0)
+                return r;
 
         a = t * ticks_in_usec;
         if (a > UINT32_MAX)
@@ -48,7 +57,7 @@ int tc_time_to_tick(usec_t t, uint32_t *ret) {
         return 0;
 }
 
-int parse_tc_percent(const char *s, uint32_t *percent)  {
+int parse_tc_percent(const char *s, uint32_t *percent) {
         int r;
 
         assert(s);
@@ -92,3 +101,32 @@ int tc_fill_ratespec_and_table(struct tc_ratespec *rate, uint32_t *rtab, uint32_
         rate->linklayer = TC_LINKLAYER_ETHERNET;
         return 0;
 }
+
+int parse_handle(const char *t, uint32_t *ret) {
+        _cleanup_free_ char *word = NULL;
+        uint16_t major, minor;
+        int r;
+
+        assert(t);
+        assert(ret);
+
+        /* Extract the major number. */
+        r = extract_first_word(&t, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return -EINVAL;
+        if (!t)
+                return -EINVAL;
+
+        r = safe_atou16_full(word, 16, &major);
+        if (r < 0)
+                return r;
+
+        r = safe_atou16_full(t, 16, &minor);
+        if (r < 0)
+                return r;
+
+        *ret = ((uint32_t) major << 16) | minor;
+        return 0;
+}
index c901f50691c342a1f31bebf403cd2e2719cb670d..6287b35a76571e1311172bfb2601afbcca225fd2 100644 (file)
@@ -6,7 +6,9 @@
 
 #include "time-util.h"
 
+int tc_init(double *ret_ticks_in_usec, uint32_t *ret_hz);
 int tc_time_to_tick(usec_t t, uint32_t *ret);
 int parse_tc_percent(const char *s, uint32_t *percent);
 int tc_transmit_time(uint64_t rate, uint32_t size, uint32_t *ret);
 int tc_fill_ratespec_and_table(struct tc_ratespec *rate, uint32_t *rtab, uint32_t mtu);
+int parse_handle(const char *t, uint32_t *ret);
diff --git a/src/network/tc/tc.c b/src/network/tc/tc.c
new file mode 100644 (file)
index 0000000..30a0013
--- /dev/null
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "macro.h"
+#include "qdisc.h"
+#include "tc.h"
+#include "tclass.h"
+
+void traffic_control_free(TrafficControl *tc) {
+        if (!tc)
+                return;
+
+        switch (tc->kind) {
+        case TC_KIND_QDISC:
+                qdisc_free(TC_TO_QDISC(tc));
+                break;
+        case TC_KIND_TCLASS:
+                tclass_free(TC_TO_TCLASS(tc));
+                break;
+        default:
+                assert_not_reached("Invalid traffic control type");
+        }
+}
+
+int traffic_control_configure(Link *link, TrafficControl *tc) {
+        assert(link);
+        assert(tc);
+
+        switch(tc->kind) {
+        case TC_KIND_QDISC:
+                return qdisc_configure(link, TC_TO_QDISC(tc));
+        case TC_KIND_TCLASS:
+                return tclass_configure(link, TC_TO_TCLASS(tc));
+        default:
+                assert_not_reached("Invalid traffic control type");
+        }
+}
+
+int traffic_control_section_verify(TrafficControl *tc, bool *qdisc_has_root, bool *qdisc_has_clsact) {
+        assert(tc);
+
+        switch(tc->kind) {
+        case TC_KIND_QDISC:
+                return qdisc_section_verify(TC_TO_QDISC(tc), qdisc_has_root, qdisc_has_clsact);
+        case TC_KIND_TCLASS:
+                return tclass_section_verify(TC_TO_TCLASS(tc));
+        default:
+                assert_not_reached("Invalid traffic control type");
+        }
+}
diff --git a/src/network/tc/tc.h b/src/network/tc/tc.h
new file mode 100644 (file)
index 0000000..defa0b7
--- /dev/null
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "networkd-link.h"
+
+typedef enum TrafficControlKind {
+        TC_KIND_QDISC,
+        TC_KIND_TCLASS,
+        TC_KIND_FILTER,
+        _TC_KIND_MAX,
+        _TC_KIND_INVALID = -1,
+} TrafficControlKind;
+
+typedef struct TrafficControl {
+        TrafficControlKind kind;
+} TrafficControl;
+
+/* For casting a tc into the various tc kinds */
+#define DEFINE_TC_CAST(UPPERCASE, MixedCase)                           \
+        static inline MixedCase* TC_TO_##UPPERCASE(TrafficControl *tc) {                    \
+                if (_unlikely_(!tc || tc->kind != TC_KIND_##UPPERCASE))  \
+                        return NULL;                                      \
+                                                                          \
+                return (MixedCase*) tc;                                    \
+        }
+
+/* For casting the various tc kinds into a tc */
+#define TC(tc) (&(tc)->meta)
+
+void traffic_control_free(TrafficControl *tc);
+int traffic_control_configure(Link *link, TrafficControl *tc);
+int traffic_control_section_verify(TrafficControl *tc, bool *qdisc_has_root, bool *qdisc_has_clsact);
diff --git a/src/network/tc/tclass.c b/src/network/tc/tclass.c
new file mode 100644 (file)
index 0000000..9a39713
--- /dev/null
@@ -0,0 +1,289 @@
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright © 2019 VMware, Inc. */
+
+#include <linux/pkt_sched.h>
+
+#include "alloc-util.h"
+#include "conf-parser.h"
+#include "in-addr-util.h"
+#include "netlink-util.h"
+#include "networkd-manager.h"
+#include "parse-util.h"
+#include "set.h"
+#include "string-util.h"
+#include "strv.h"
+#include "tc-util.h"
+#include "tclass.h"
+
+const TClassVTable * const tclass_vtable[_TCLASS_KIND_MAX] = {
+        [TCLASS_KIND_DRR] = &drr_tclass_vtable,
+        [TCLASS_KIND_HTB] = &htb_tclass_vtable,
+        [TCLASS_KIND_QFQ] = &qfq_tclass_vtable,
+};
+
+static int tclass_new(TClassKind kind, TClass **ret) {
+        _cleanup_(tclass_freep) TClass *tclass = NULL;
+        int r;
+
+        tclass = malloc0(tclass_vtable[kind]->object_size);
+        if (!tclass)
+                return -ENOMEM;
+
+        tclass->meta.kind = TC_KIND_TCLASS,
+        tclass->parent = TC_H_ROOT;
+        tclass->kind = kind;
+
+        if (TCLASS_VTABLE(tclass)->init) {
+                r = TCLASS_VTABLE(tclass)->init(tclass);
+                if (r < 0)
+                        return r;
+        }
+
+        *ret = TAKE_PTR(tclass);
+
+        return 0;
+}
+
+int tclass_new_static(TClassKind kind, Network *network, const char *filename, unsigned section_line, TClass **ret) {
+        _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
+        _cleanup_(tclass_freep) TClass *tclass = NULL;
+        TrafficControl *existing;
+        int r;
+
+        assert(network);
+        assert(ret);
+        assert(filename);
+        assert(section_line > 0);
+
+        r = network_config_section_new(filename, section_line, &n);
+        if (r < 0)
+                return r;
+
+        existing = ordered_hashmap_get(network->tc_by_section, n);
+        if (existing) {
+                TClass *t;
+
+                if (existing->kind != TC_KIND_TCLASS)
+                        return -EINVAL;
+
+                t = TC_TO_TCLASS(existing);
+
+                if (t->kind != kind)
+                        return -EINVAL;
+
+                *ret = t;
+                return 0;
+        }
+
+        r = tclass_new(kind, &tclass);
+        if (r < 0)
+                return r;
+
+        tclass->network = network;
+        tclass->section = TAKE_PTR(n);
+
+        r = ordered_hashmap_ensure_allocated(&network->tc_by_section, &network_config_hash_ops);
+        if (r < 0)
+                return r;
+
+        r = ordered_hashmap_put(network->tc_by_section, tclass->section, tclass);
+        if (r < 0)
+                return r;
+
+        *ret = TAKE_PTR(tclass);
+        return 0;
+}
+
+void tclass_free(TClass *tclass) {
+        if (!tclass)
+                return;
+
+        if (tclass->network && tclass->section)
+                ordered_hashmap_remove(tclass->network->tc_by_section, tclass->section);
+
+        network_config_section_free(tclass->section);
+
+        free(tclass);
+}
+
+static int tclass_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+        int r;
+
+        assert(link);
+        assert(link->tc_messages > 0);
+        link->tc_messages--;
+
+        if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+                return 1;
+
+        r = sd_netlink_message_get_errno(m);
+        if (r < 0 && r != -EEXIST) {
+                log_link_message_error_errno(link, m, r, "Could not set TClass");
+                link_enter_failed(link);
+                return 1;
+        }
+
+        if (link->tc_messages == 0) {
+                log_link_debug(link, "Traffic control configured");
+                link->tc_configured = true;
+                link_check_ready(link);
+        }
+
+        return 1;
+}
+
+int tclass_configure(Link *link, TClass *tclass) {
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
+        int r;
+
+        assert(link);
+        assert(link->manager);
+        assert(link->manager->rtnl);
+        assert(link->ifindex > 0);
+
+        r = sd_rtnl_message_new_tclass(link->manager->rtnl, &req, RTM_NEWTCLASS, AF_UNSPEC, link->ifindex);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not create RTM_NEWTCLASS message: %m");
+
+        r = sd_rtnl_message_set_tclass_parent(req, tclass->parent);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not create tcm_parent message: %m");
+
+        if (tclass->classid != TC_H_UNSPEC) {
+                r = sd_rtnl_message_set_tclass_handle(req, tclass->classid);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not set tcm_handle message: %m");
+        }
+
+        r = sd_netlink_message_append_string(req, TCA_KIND, TCLASS_VTABLE(tclass)->tca_kind);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not append TCA_KIND attribute: %m");
+
+        if (TCLASS_VTABLE(tclass)->fill_message) {
+                r = TCLASS_VTABLE(tclass)->fill_message(link, tclass, req);
+                if (r < 0)
+                        return r;
+        }
+
+        r = netlink_call_async(link->manager->rtnl, NULL, req, tclass_handler, link_netlink_destroy_callback, link);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
+
+        link_ref(link);
+        link->tc_messages++;
+
+        return 0;
+}
+
+int tclass_section_verify(TClass *tclass) {
+        int r;
+
+        assert(tclass);
+
+        if (section_is_invalid(tclass->section))
+                return -EINVAL;
+
+        if (TCLASS_VTABLE(tclass)->verify) {
+                r = TCLASS_VTABLE(tclass)->verify(tclass);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+int config_parse_tclass_parent(
+                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_(tclass_free_or_set_invalidp) TClass *tclass = NULL;
+        Network *network = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = tclass_new_static(ltype, network, filename, section_line, &tclass);
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to create traffic control class, ignoring assignment: %m");
+                return 0;
+        }
+
+        if (streq(rvalue, "root"))
+                tclass->parent = TC_H_ROOT;
+        else {
+                r = parse_handle(rvalue, &tclass->parent);
+                if (r < 0) {
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
+                                   "Failed to parse 'Parent=', ignoring assignment: %s",
+                                   rvalue);
+                        return 0;
+                }
+        }
+
+        tclass = NULL;
+
+        return 0;
+}
+
+int config_parse_tclass_classid(
+                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_(tclass_free_or_set_invalidp) TClass *tclass = NULL;
+        Network *network = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = tclass_new_static(ltype, network, filename, section_line, &tclass);
+        if (r == -ENOMEM)
+                return log_oom();
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to create traffic control class, ignoring assignment: %m");
+                return 0;
+        }
+
+        if (isempty(rvalue)) {
+                tclass->classid = TC_H_UNSPEC;
+                tclass = NULL;
+                return 0;
+        }
+
+        r = parse_handle(rvalue, &tclass->classid);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "Failed to parse 'ClassId=', ignoring assignment: %s",
+                           rvalue);
+                return 0;
+        }
+
+        tclass = NULL;
+
+        return 0;
+}
diff --git a/src/network/tc/tclass.h b/src/network/tc/tclass.h
new file mode 100644 (file)
index 0000000..dc6886a
--- /dev/null
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: LGPL-2.1+
+ * Copyright © 2019 VMware, Inc. */
+#pragma once
+
+#include "conf-parser.h"
+#include "networkd-link.h"
+#include "networkd-network.h"
+#include "networkd-util.h"
+#include "tc.h"
+
+typedef enum TClassKind {
+        TCLASS_KIND_DRR,
+        TCLASS_KIND_HTB,
+        TCLASS_KIND_QFQ,
+        _TCLASS_KIND_MAX,
+        _TCLASS_KIND_INVALID = -1,
+} TClassKind;
+
+typedef struct TClass {
+        TrafficControl meta;
+
+        NetworkConfigSection *section;
+        Network *network;
+
+        uint32_t classid;
+        uint32_t parent;
+
+        TClassKind kind;
+} TClass;
+
+typedef struct TClassVTable {
+        size_t object_size;
+        const char *tca_kind;
+        /* called in tclass_new() */
+        int (*init)(TClass *tclass);
+        int (*fill_message)(Link *link, TClass *tclass, sd_netlink_message *m);
+        int (*verify)(TClass *tclass);
+} TClassVTable;
+
+extern const TClassVTable * const tclass_vtable[_TCLASS_KIND_MAX];
+
+#define TCLASS_VTABLE(t) ((t)->kind != _TCLASS_KIND_INVALID ? tclass_vtable[(t)->kind] : NULL)
+
+/* For casting a tclass into the various tclass kinds */
+#define DEFINE_TCLASS_CAST(UPPERCASE, MixedCase)                          \
+        static inline MixedCase* TCLASS_TO_##UPPERCASE(TClass *t) {       \
+                if (_unlikely_(!t || t->kind != TCLASS_KIND_##UPPERCASE)) \
+                        return NULL;                                      \
+                                                                          \
+                return (MixedCase*) t;                                    \
+        }
+
+/* For casting the various tclass kinds into a tclass */
+#define TCLASS(t) (&(t)->meta)
+
+void tclass_free(TClass *tclass);
+int tclass_new_static(TClassKind kind, Network *network, const char *filename, unsigned section_line, TClass **ret);
+
+int tclass_configure(Link *link, TClass *tclass);
+int tclass_section_verify(TClass *tclass);
+
+DEFINE_NETWORK_SECTION_FUNCTIONS(TClass, tclass_free);
+
+DEFINE_TC_CAST(TCLASS, TClass);
+
+CONFIG_PARSER_PROTOTYPE(config_parse_tclass_parent);
+CONFIG_PARSER_PROTOTYPE(config_parse_tclass_classid);
+
+#include "drr.h"
+#include "htb.h"
+#include "qfq.h"
index aac949961620e7f5fd6505163a73448493cbb04d..bc64860a353af8982138d173fd9e17505c838b81 100644 (file)
@@ -57,9 +57,11 @@ int config_parse_trivial_link_equalizer_id(
         r = qdisc_new_static(QDISC_KIND_TEQL, network, filename, section_line, &qdisc);
         if (r == -ENOMEM)
                 return log_oom();
-        if (r < 0)
-                return log_syntax(unit, LOG_ERR, filename, line, r,
-                                  "More than one kind of queueing discipline, ignoring assignment: %m");
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r,
+                           "More than one kind of queueing discipline, ignoring assignment: %m");
+                return 0;
+        }
 
         teql = TEQL(qdisc);
 
@@ -72,13 +74,13 @@ int config_parse_trivial_link_equalizer_id(
 
         r = safe_atou(rvalue, &id);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to parse '%s=', ignoring assignment: %s",
                            lvalue, rvalue);
                 return 0;
         }
         if (id > INT_MAX) {
-                log_syntax(unit, LOG_ERR, filename, line, 0,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "'%s=' is too large, ignoring assignment: %s",
                            lvalue, rvalue);
         }
index d441099b5a5c07262820d5ea5846d996027d00f2..d84d746c3fa8e2dd423b159bb716b4546437bd86 100644 (file)
@@ -53,7 +53,7 @@ static void test_rule_serialization(const char *title, const char *ruleset, cons
         log_info("$ %s", cmd);
         assert_se(system(cmd) == 0);
 
-        set_free_with_destructor(rules, routing_policy_rule_free);
+        set_free(rules);
 }
 
 int main(int argc, char **argv) {
index 40a29f19aa0d098ffc529cf45539189114c5e2e1..6ab26d3ca98b1a35b4098cc0bffb3179910be239 100644 (file)
@@ -323,7 +323,7 @@ int manager_new(Manager **ret, Hashmap *interfaces, char **ignore,
         if (r < 0)
                 return r;
 
-        (void) sd_event_add_signal(m->event, NULL, SIGTERM, NULL,  NULL);
+        (void) sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
         (void) sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
 
         if (timeout > 0) {
index 17ed5d38cfe3de6bd53344cc7f391f90e9f61c13..cfd9093f1a3e954b609736ab85adf9994855214f 100644 (file)
@@ -183,8 +183,8 @@ static int parse_argv(int argc, char *argv[]) {
 }
 
 static int run(int argc, char *argv[]) {
-        _cleanup_(notify_on_cleanup) const char *notify_message = NULL;
         _cleanup_(manager_freep) Manager *m = NULL;
+        _cleanup_(notify_on_cleanup) const char *notify_message = NULL;
         int r;
 
         log_setup_service();
index a76f3376951f8eabfbf204f94e3fa5dcd4893358..69d473401da661b1f48185abdc63976f90c8edc7 100644 (file)
@@ -18,6 +18,7 @@
 #include "string-util.h"
 #include "strv.h"
 #include "terminal-util.h"
+#include "time-util.h"
 #include "user-util.h"
 #include "util.h"
 
@@ -27,6 +28,7 @@ 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 bool arg_no_block = false;
 
 static int help(void) {
         _cleanup_free_ char *link = NULL;
@@ -45,6 +47,7 @@ static int help(void) {
                "     --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"
+               "     --no-block        Do not wait until operation finished\n"
                "\nSee the %s for details.\n"
                , program_invocation_short_name
                , ansi_highlight(), ansi_normal()
@@ -54,6 +57,26 @@ static int help(void) {
         return 0;
 }
 
+static pid_t manager_pid(void) {
+        const char *e;
+        pid_t pid;
+        int r;
+
+        /* If we run as a service managed by systemd --user the $MANAGERPID environment variable points to
+         * the service manager's PID. */
+        e = getenv("MANAGERPID");
+        if (!e)
+                return 0;
+
+        r = parse_pid(e, &pid);
+        if (r < 0) {
+                log_warning_errno(r, "$MANAGERPID is set to an invalid PID, ignoring: %s", e);
+                return 0;
+        }
+
+        return pid;
+}
+
 static int parse_argv(int argc, char *argv[]) {
 
         enum {
@@ -63,6 +86,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_STATUS,
                 ARG_BOOTED,
                 ARG_UID,
+                ARG_NO_BLOCK
         };
 
         static const struct option options[] = {
@@ -73,6 +97,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "status",    required_argument, NULL, ARG_STATUS    },
                 { "booted",    no_argument,       NULL, ARG_BOOTED    },
                 { "uid",       required_argument, NULL, ARG_UID       },
+                { "no-block",  no_argument,       NULL, ARG_NO_BLOCK  },
                 {}
         };
 
@@ -96,13 +121,24 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_PID:
+                        if (isempty(optarg) || streq(optarg, "auto")) {
+                                arg_pid = getppid();
 
-                        if (optarg) {
-                                if (parse_pid(optarg, &arg_pid) < 0)
-                                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                                               "Failed to parse PID %s.", optarg);
-                        } else
+                                if (arg_pid <= 1 ||
+                                    arg_pid == manager_pid()) /* Don't send from PID 1 or the service
+                                                               * manager's PID (which might be distinct from
+                                                               * 1, if we are a --user instance), that'd just
+                                                               * be confusing for the service manager */
+                                        arg_pid = getpid();
+                        } else if (streq(optarg, "parent"))
                                 arg_pid = getppid();
+                        else if (streq(optarg, "self"))
+                                arg_pid = getpid();
+                        else {
+                                r = parse_pid(optarg, &arg_pid);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to parse PID %s.", optarg);
+                        }
 
                         break;
 
@@ -126,6 +162,10 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
                 }
 
+                case ARG_NO_BLOCK:
+                        arg_no_block = true;
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -151,6 +191,7 @@ static int run(int argc, char* argv[]) {
         _cleanup_strv_free_ char **final_env = NULL;
         char* our_env[4];
         unsigned i = 0;
+        pid_t source_pid;
         int r;
 
         log_show_color(true);
@@ -207,12 +248,33 @@ static int run(int argc, char* argv[]) {
             setreuid(arg_uid, (uid_t) -1) < 0)
                 return log_error_errno(errno, "Failed to change UID: %m");
 
-        r = sd_pid_notify(arg_pid ? arg_pid : getppid(), false, n);
+        if (arg_pid > 0)
+                source_pid = arg_pid;
+        else {
+                /* Pretend the message originates from our parent, given that we are typically called from a
+                 * shell script, i.e. we are not the main process of a service but only a child of it. */
+                source_pid = getppid();
+                if (source_pid <= 1 ||
+                    source_pid == manager_pid()) /* safety check: don't claim we'd send anything from PID 1
+                                                  * or the service manager itself */
+                        source_pid = 0;
+        }
+        r = sd_pid_notify(source_pid, false, n);
         if (r < 0)
                 return log_error_errno(r, "Failed to notify init system: %m");
         if (r == 0)
                 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
                                        "No status data could be sent: $NOTIFY_SOCKET was not set");
+
+        if (!arg_no_block) {
+                r = sd_notify_barrier(0, 5 * USEC_PER_SEC);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to invoke barrier: %m");
+                if (r == 0)
+                        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+                                               "No status data could be sent: $NOTIFY_SOCKET was not set");
+        }
+
         return 0;
 }
 
index f5048d9473cb2c11c53b4077af2718911090ae7e..a16ee5c60a23491ace496cd79b53310c7fbd118f 100644 (file)
@@ -199,16 +199,12 @@ int create_subcgroup(pid_t pid, bool keep_unit, CGroupUnified unified_requested)
  * namespace.
  */
 static int get_process_controllers(Set **ret) {
-        _cleanup_set_free_free_ Set *controllers = NULL;
+        _cleanup_set_free_ Set *controllers = NULL;
         _cleanup_fclose_ FILE *f = NULL;
         int r;
 
         assert(ret);
 
-        controllers = set_new(&string_hash_ops);
-        if (!controllers)
-                return -ENOMEM;
-
         f = fopen("/proc/self/cgroup", "re");
         if (!f)
                 return errno == ENOENT ? -ESRCH : -errno;
@@ -237,7 +233,7 @@ static int get_process_controllers(Set **ret) {
                 if (STR_IN_SET(l, "", "name=systemd", "name=unified"))
                         continue;
 
-                r = set_put_strdup(controllers, l);
+                r = set_put_strdup(&controllers, l);
                 if (r < 0)
                         return r;
         }
@@ -303,7 +299,7 @@ static int mount_legacy_cgns_supported(
                 uid_t uid_range,
                 const char *selinux_apifs_context) {
 
-        _cleanup_set_free_free_ Set *controllers = NULL;
+        _cleanup_set_free_ Set *controllers = NULL;
         const char *cgroup_root = "/sys/fs/cgroup", *c;
         int r;
 
@@ -323,7 +319,7 @@ static int mount_legacy_cgns_supported(
                  * uid/gid as seen from e.g. /proc/1/mountinfo. So we simply
                  * pass uid 0 and not uid_shift to tmpfs_patch_options().
                  */
-                r = tmpfs_patch_options("mode=755", 0, selinux_apifs_context, &options);
+                r = tmpfs_patch_options("mode=755" TMPFS_LIMITS_SYS_FS_CGROUP, 0, selinux_apifs_context, &options);
                 if (r < 0)
                         return log_oom();
 
@@ -425,7 +421,7 @@ static int mount_legacy_cgns_unsupported(
         if (r == 0) {
                 _cleanup_free_ char *options = NULL;
 
-                r = tmpfs_patch_options("mode=755", uid_shift == 0 ? UID_INVALID : uid_shift, selinux_apifs_context, &options);
+                r = tmpfs_patch_options("mode=755" TMPFS_LIMITS_SYS_FS_CGROUP, uid_shift == 0 ? UID_INVALID : uid_shift, selinux_apifs_context, &options);
                 if (r < 0)
                         return log_oom();
 
index 9b54cda2f723e1c5616a941dc40a32cbe1dd6d71..ac3a1a02c4857e488544a1189aa79331f03970a3 100644 (file)
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
 #include <sys/types.h>
index 10e61927342f045c68fd995f8d2ed431001bb636..69f36691fb72b4df119291261cd80a57e9269a8a 100644 (file)
@@ -52,7 +52,7 @@ int expose_port_parse(ExposePort **l, const char *s) {
         }
 
         if (r < 0)
-                return -EINVAL;
+                return r;
 
         LIST_FOREACH(ports, p, *l)
                 if (p->protocol == protocol && p->host_port == host_port)
@@ -62,9 +62,11 @@ int expose_port_parse(ExposePort **l, const char *s) {
         if (!p)
                 return -ENOMEM;
 
-        p->protocol = protocol;
-        p->host_port = host_port;
-        p->container_port = container_port;
+        *p = (ExposePort) {
+                .protocol = protocol,
+                .host_port = host_port,
+                .container_port = container_port,
+        };
 
         LIST_PREPEND(ports, *l, p);
 
@@ -115,7 +117,6 @@ int expose_port_flush(ExposePort* l, union in_addr_union *exposed) {
 
 int expose_port_execute(sd_netlink *rtnl, ExposePort *l, union in_addr_union *exposed) {
         _cleanup_free_ struct local_address *addresses = NULL;
-        _cleanup_free_ char *pretty = NULL;
         union in_addr_union new_exposed;
         ExposePort *p;
         bool add;
@@ -144,8 +145,11 @@ int expose_port_execute(sd_netlink *rtnl, ExposePort *l, union in_addr_union *ex
         if (in_addr_equal(af, exposed, &new_exposed))
                 return 0;
 
-        in_addr_to_string(af, &new_exposed, &pretty);
-        log_debug("New container IP is %s.", strna(pretty));
+        if (DEBUG_LOGGING) {
+                _cleanup_free_ char *pretty = NULL;
+                in_addr_to_string(af, &new_exposed, &pretty);
+                log_debug("New container IP is %s.", strna(pretty));
+        }
 
         LIST_FOREACH(ports, p, l) {
 
index 10c147a67f394bcd8163f0249389df4c6da1eaa5..5599c6a1b3b307c676de3d705f362bef944cda74 100644 (file)
@@ -487,59 +487,6 @@ int mount_sysfs(const char *dest, MountSettingsMask mount_settings) {
                              MS_BIND|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT|extra_flags, NULL);
 }
 
-static int mkdir_userns(const char *path, mode_t mode, uid_t uid_shift) {
-        int r;
-
-        assert(path);
-
-        r = mkdir_errno_wrapper(path, mode);
-        if (r < 0 && r != -EEXIST)
-                return r;
-
-        if (uid_shift == UID_INVALID)
-                return 0;
-
-        if (lchown(path, uid_shift, uid_shift) < 0)
-                return -errno;
-
-        return 0;
-}
-
-static int mkdir_userns_p(const char *prefix, const char *path, mode_t mode, uid_t uid_shift) {
-        const char *p, *e;
-        int r;
-
-        assert(path);
-
-        if (prefix && !path_startswith(path, prefix))
-                return -ENOTDIR;
-
-        /* create every parent directory in the path, except the last component */
-        p = path + strspn(path, "/");
-        for (;;) {
-                char t[strlen(path) + 1];
-
-                e = p + strcspn(p, "/");
-                p = e + strspn(e, "/");
-
-                /* Is this the last component? If so, then we're done */
-                if (*p == 0)
-                        break;
-
-                memcpy(t, path, e - path);
-                t[e-path] = 0;
-
-                if (prefix && path_startswith(prefix, t))
-                        continue;
-
-                r = mkdir_userns(t, mode, uid_shift);
-                if (r < 0)
-                        return r;
-        }
-
-        return mkdir_userns(path, mode, uid_shift);
-}
-
 int mount_all(const char *dest,
               MountSettingsMask mount_settings,
               uid_t uid_shift,
@@ -598,29 +545,38 @@ int mount_all(const char *dest,
                 PROC_READ_ONLY("/proc/irq"),
                 PROC_READ_ONLY("/proc/scsi"),
 
-                { "mqueue",          "/dev/mqueue",     "mqueue", NULL,       MS_NOSUID|MS_NOEXEC|MS_NODEV,
+                { "mqueue",                 "/dev/mqueue",                  "mqueue", NULL,                            MS_NOSUID|MS_NOEXEC|MS_NODEV,
                   MOUNT_IN_USERNS|MOUNT_MKDIR },
 
                 /* Then we list outer child mounts (i.e. mounts applied *before* entering user namespacing) */
-                { "tmpfs",           "/tmp",            "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME,
+                { "tmpfs",                  "/tmp",                         "tmpfs", "mode=1777" NESTED_TMPFS_LIMITS,  MS_NOSUID|MS_NODEV|MS_STRICTATIME,
                   MOUNT_FATAL|MOUNT_APPLY_TMPFS_TMP|MOUNT_MKDIR },
-                { "tmpfs",           "/sys",            "tmpfs", "mode=555",  MS_NOSUID|MS_NOEXEC|MS_NODEV,
+                { "tmpfs",                  "/sys",                         "tmpfs", "mode=555" TMPFS_LIMITS_SYS,      MS_NOSUID|MS_NOEXEC|MS_NODEV,
                   MOUNT_FATAL|MOUNT_APPLY_APIVFS_NETNS|MOUNT_MKDIR },
-                { "sysfs",           "/sys",            "sysfs", NULL,        MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV,
+                { "sysfs",                  "/sys",                         "sysfs", NULL,                             MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV,
                   MOUNT_FATAL|MOUNT_APPLY_APIVFS_RO|MOUNT_MKDIR },    /* skipped if above was mounted */
-                { "sysfs",           "/sys",            "sysfs", NULL,        MS_NOSUID|MS_NOEXEC|MS_NODEV,
+                { "sysfs",                  "/sys",                         "sysfs", NULL,                             MS_NOSUID|MS_NOEXEC|MS_NODEV,
                   MOUNT_FATAL|MOUNT_MKDIR },                          /* skipped if above was mounted */
-                { "tmpfs",           "/dev",            "tmpfs", "mode=755",  MS_NOSUID|MS_STRICTATIME,
+                { "tmpfs",                  "/dev",                         "tmpfs", "mode=755" TMPFS_LIMITS_DEV,      MS_NOSUID|MS_STRICTATIME,
                   MOUNT_FATAL|MOUNT_MKDIR },
-                { "tmpfs",           "/dev/shm",        "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME,
+                { "tmpfs",                  "/dev/shm",                     "tmpfs", "mode=1777" NESTED_TMPFS_LIMITS,  MS_NOSUID|MS_NODEV|MS_STRICTATIME,
                   MOUNT_FATAL|MOUNT_MKDIR },
-                { "tmpfs",           "/run",            "tmpfs", "mode=755",  MS_NOSUID|MS_NODEV|MS_STRICTATIME,
+                { "tmpfs",                  "/run",                         "tmpfs", "mode=755" TMPFS_LIMITS_RUN,      MS_NOSUID|MS_NODEV|MS_STRICTATIME,
                   MOUNT_FATAL|MOUNT_MKDIR },
-
+                { "/run/host",              "/run/host",                    NULL,    NULL,                             MS_BIND,
+                  MOUNT_FATAL|MOUNT_MKDIR|MOUNT_PREFIX_ROOT }, /* Prepare this so that we can make it read-only when we are done */
+                { "/etc/os-release",        "/run/host/os-release",         NULL,    NULL,                             MS_BIND,
+                  MOUNT_TOUCH }, /* As per kernel interface requirements, bind mount first (creating mount points) and make read-only later */
+                { "/usr/lib/os-release",    "/run/host/os-release",         NULL,    NULL,                             MS_BIND,
+                  MOUNT_FATAL }, /* If /etc/os-release doesn't exist use the version in /usr/lib as fallback */
+                { NULL,                     "/run/host/os-release",         NULL,    NULL,                             MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT,
+                  MOUNT_FATAL },
+                { NULL,                     "/run/host",                    NULL,    NULL,                             MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT,
+                  MOUNT_FATAL|MOUNT_IN_USERNS },
 #if HAVE_SELINUX
-                { "/sys/fs/selinux", "/sys/fs/selinux", NULL,    NULL,        MS_BIND,
+                { "/sys/fs/selinux",        "/sys/fs/selinux",              NULL,    NULL,                             MS_BIND,
                   MOUNT_MKDIR },  /* Bind mount first (mkdir/chown the mount point in case /sys/ is mounted as minimal skeleton tmpfs) */
-                { NULL,              "/sys/fs/selinux", NULL,    NULL,        MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT,
+                { NULL,                     "/sys/fs/selinux",              NULL,    NULL,                             MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT,
                   0 },            /* Then, make it r/o (don't mkdir/chown the mount point here, the previous entry already did that) */
 #endif
         };
@@ -634,9 +590,9 @@ int mount_all(const char *dest,
         int r;
 
         for (k = 0; k < ELEMENTSOF(mount_table); k++) {
-                _cleanup_free_ char *where = NULL, *options = NULL;
-                const char *o;
+                _cleanup_free_ char *where = NULL, *options = NULL, *prefixed = NULL;
                 bool fatal = FLAGS_SET(mount_table[k].mount_settings, MOUNT_FATAL);
+                const char *o;
 
                 if (in_userns != FLAGS_SET(mount_table[k].mount_settings, MOUNT_IN_USERNS))
                         continue;
@@ -663,8 +619,13 @@ int mount_all(const char *dest,
                                 continue;
                 }
 
-                if (FLAGS_SET(mount_table[k].mount_settings, MOUNT_MKDIR)) {
-                        r = mkdir_userns_p(dest, where, 0755, (use_userns && !in_userns) ? uid_shift : UID_INVALID);
+                if ((mount_table[k].mount_settings & (MOUNT_MKDIR|MOUNT_TOUCH)) != 0) {
+                        uid_t u = (use_userns && !in_userns) ? uid_shift : UID_INVALID;
+
+                        if (FLAGS_SET(mount_table[k].mount_settings, MOUNT_TOUCH))
+                                r = mkdir_parents_safe(dest, where, 0755, u, u, 0);
+                        else
+                                r = mkdir_p_safe(dest, where, 0755, u, u, 0);
                         if (r < 0 && r != -EEXIST) {
                                 if (fatal && r != -EROFS)
                                         return log_error_errno(r, "Failed to create directory %s: %m", where);
@@ -678,6 +639,18 @@ int mount_all(const char *dest,
                         }
                 }
 
+                if (FLAGS_SET(mount_table[k].mount_settings, MOUNT_TOUCH)) {
+                        r = touch(where);
+                        if (r < 0 && r != -EEXIST) {
+                                if (fatal && r != -EROFS)
+                                        return log_error_errno(r, "Failed to create file %s: %m", where);
+
+                                log_debug_errno(r, "Failed to create file %s: %m", where);
+                                if (r != -EROFS)
+                                        continue;
+                        }
+                }
+
                 o = mount_table[k].options;
                 if (streq_ptr(mount_table[k].type, "tmpfs")) {
                         r = tmpfs_patch_options(o, in_userns ? 0 : uid_shift, selinux_apifs_context, &options);
@@ -687,8 +660,18 @@ int mount_all(const char *dest,
                                 o = options;
                 }
 
+                if (FLAGS_SET(mount_table[k].mount_settings, MOUNT_PREFIX_ROOT)) {
+                        /* Optionally prefix the mount source with the root dir. This is useful in bind
+                         * mounts to be created within the container image before we transition into it. Note
+                         * that MOUNT_IN_USERNS is run after we transitioned hence prefixing is not ncessary
+                         * for those. */
+                        r = chase_symlinks(mount_table[k].what, dest, CHASE_PREFIX_ROOT, &prefixed, NULL);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to resolve %s/%s: %m", dest, mount_table[k].what);
+                }
+
                 r = mount_verbose(fatal ? LOG_ERR : LOG_DEBUG,
-                                  mount_table[k].what,
+                                  prefixed ?: mount_table[k].what,
                                   where,
                                   mount_table[k].type,
                                   mount_table[k].flags,
@@ -898,7 +881,7 @@ static int mount_inaccessible(const char *dest, CustomMount *m) {
                 return m->graceful ? 0 : r;
         }
 
-        r = mode_to_inaccessible_node("/run/systemd", st.st_mode, &source);
+        r = mode_to_inaccessible_node(NULL, st.st_mode, &source);
         if (r < 0)
                 return m->graceful ? 0 : r;
 
@@ -1023,7 +1006,7 @@ static int setup_volatile_state(const char *directory, uid_t uid_shift, const ch
         if (r < 0 && errno != EEXIST)
                 return log_error_errno(errno, "Failed to create %s: %m", directory);
 
-        options = "mode=755";
+        options = "mode=755" TMPFS_LIMITS_VOLATILE_STATE;
         r = tmpfs_patch_options(options, uid_shift == 0 ? UID_INVALID : uid_shift, selinux_apifs_context, &buf);
         if (r < 0)
                 return log_oom();
@@ -1068,7 +1051,7 @@ static int setup_volatile_yes(const char *directory, uid_t uid_shift, const char
         if (!mkdtemp(template))
                 return log_error_errno(errno, "Failed to create temporary directory: %m");
 
-        options = "mode=755";
+        options = "mode=755" TMPFS_LIMITS_ROOTFS;
         r = tmpfs_patch_options(options, uid_shift == 0 ? UID_INVALID : uid_shift, selinux_apifs_context, &buf);
         if (r < 0)
                 goto fail;
@@ -1135,7 +1118,7 @@ static int setup_volatile_overlay(const char *directory, uid_t uid_shift, const
         if (!mkdtemp(template))
                 return log_error_errno(errno, "Failed to create temporary directory: %m");
 
-        options = "mode=755";
+        options = "mode=755" TMPFS_LIMITS_ROOTFS;
         r = tmpfs_patch_options(options, uid_shift == 0 ? UID_INVALID : uid_shift, selinux_apifs_context, &buf);
         if (r < 0)
                 goto finish;
index e8bb90383987297d50a2121001f0e6972dc8d080..3898c74f18eaaf0897d769288df3b064ad882c33 100644 (file)
@@ -17,6 +17,8 @@ typedef enum MountSettingsMask {
         MOUNT_ROOT_ONLY          = 1 << 6, /* if set, only root mounts are mounted */
         MOUNT_NON_ROOT_ONLY      = 1 << 7, /* if set, only non-root mounts are mounted */
         MOUNT_MKDIR              = 1 << 8, /* if set, make directory to mount over first */
+        MOUNT_TOUCH              = 1 << 9, /* if set, touch file to mount over first */
+        MOUNT_PREFIX_ROOT        = 1 << 10,/* if set, prefix the source path with the container's root directory */
 } MountSettingsMask;
 
 typedef enum CustomMountType {
index 782c03c539bc6bf07cad1967b3458839ac98a53e..e3ade92371f95e945fce82b27dc6b310055e6661 100644 (file)
  * cgrouspv1 crap: kernel, kernelTCP, swapiness, disableOOMKiller, swap, devices, leafWeight
  * general: it shouldn't leak lower level abstractions this obviously
  * unmanagable cgroups stuff: realtimeRuntime/realtimePeriod
- * needs to say what happense when some option is not specified, i.e. which defautls apply
+ * needs to say what happense when some option is not specified, i.e. which defaults apply
  * no architecture? no personality?
  * seccomp example and logic is simply broken: there's no constant "SCMP_ACT_ERRNO".
  * spec should say what to do with unknown props
  * /bin/mount regarding NFS and FUSE required?
  * what does terminal=false mean?
- * sysctl inside or outside? whitelisting?
+ * sysctl inside or outside? allow-listing?
  * swapiness typo -> swappiness
  *
  * Unsupported:
@@ -1029,39 +1029,40 @@ static int oci_cgroup_devices(const char *name, JsonVariant *v, JsonDispatchFlag
                         return r;
 
                 if (!data.allow) {
-                        /* The fact that OCI allows 'deny' entries makes really no sense, as 'allow' vs. 'deny' for the
-                         * devices cgroup controller is really not about whitelisting and blacklisting but about adding
-                         * and removing entries from the whitelist. Since we always start out with an empty whitelist
-                         * we hence ignore the whole thing, as removing entries which don't exist make no sense. We'll
-                         * log about this, since this is really borked in the spec, with one exception: the entry
-                         * that's supposed to drop the kernel's default we ignore silently */
+                        /* The fact that OCI allows 'deny' entries makes really no sense, as 'allow'
+                         * vs. 'deny' for the devices cgroup controller is really not about allow-listing and
+                         * deny-listing but about adding and removing entries from the allow list. Since we
+                         * always start out with an empty allow list we hence ignore the whole thing, as
+                         * removing entries which don't exist make no sense. We'll log about this, since this
+                         * is really borked in the spec, with one exception: the entry that's supposed to
+                         * drop the kernel's default we ignore silently */
 
                         if (!data.r || !data.w || !data.m || data.type != 0 || data.major != (unsigned) -1 || data.minor != (unsigned) -1)
-                                json_log(v, flags|JSON_WARNING, 0, "Devices cgroup whitelist with arbitrary 'allow' entries not supported, ignoring.");
+                                json_log(v, flags|JSON_WARNING, 0, "Devices cgroup allow list with arbitrary 'allow' entries not supported, ignoring.");
 
                         /* We ignore the 'deny' entry as for us that's implied */
                         continue;
                 }
 
                 if (!data.r && !data.w && !data.m) {
-                        json_log(v, flags|LOG_WARNING, 0, "Device cgroup whitelist entry with no effect found, ignoring.");
+                        json_log(v, flags|LOG_WARNING, 0, "Device cgroup allow list entry with no effect found, ignoring.");
                         continue;
                 }
 
                 if (data.minor != (unsigned) -1 && data.major == (unsigned) -1)
                         return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP),
-                                        "Device cgroup whitelist entries with minors but no majors not supported.");
+                                        "Device cgroup allow list entries with minors but no majors not supported.");
 
                 if (data.major != (unsigned) -1 && data.type == 0)
                         return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP),
-                                        "Device cgroup whitelist entries with majors but no device node type not supported.");
+                                        "Device cgroup allow list entries with majors but no device node type not supported.");
 
                 if (data.type == 0) {
-                        if (data.r && data.w && data.m) /* a catchall whitelist entry means we are looking at a noop */
+                        if (data.r && data.w && data.m) /* a catchall allow list entry means we are looking at a noop */
                                 noop = true;
                         else
                                 return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP),
-                                                "Device cgroup whitelist entries with no type not supported.");
+                                                "Device cgroup allow list entries with no type not supported.");
                 }
 
                 a = reallocarray(list, n_list + 1, sizeof(struct device_data));
index fc591e2725387b8f5c3b9866328d043a07c0cf5b..112c3562acea99883c8d15cff7d8dd1bfaf5d0a2 100644 (file)
@@ -8,6 +8,7 @@
 #include "acl-util.h"
 #include "dirent-util.h"
 #include "fd-util.h"
+#include "fileio.h"
 #include "fs-util.h"
 #include "missing_magic.h"
 #include "nspawn-def.h"
@@ -335,12 +336,11 @@ static int recurse_fd(int fd, bool donate_fd, const struct stat *st, uid_t shift
                         donate_fd = true;
                 }
 
-                d = fdopendir(fd);
+                d = take_fdopendir(&fd);
                 if (!d) {
                         r = -errno;
                         goto finish;
                 }
-                fd = -1;
 
                 FOREACH_DIRENT_ALL(de, d, r = -errno; goto finish) {
                         struct stat fst;
index 9b7ca5e3dd5fc97e2932a1b2aabc9c3414acff59..50867f38435dbbfd3eb6556d6f95cad81ae926ba 100644 (file)
@@ -3,6 +3,7 @@
 #include "sd-bus.h"
 
 #include "bus-error.h"
+#include "bus-locator.h"
 #include "bus-unit-util.h"
 #include "bus-util.h"
 #include "bus-wait-for-jobs.h"
@@ -122,11 +123,9 @@ int register_machine(
         assert(bus);
 
         if (keep_unit) {
-                r = sd_bus_call_method(
+                r = bus_call_method(
                                 bus,
-                                "org.freedesktop.machine1",
-                                "/org/freedesktop/machine1",
-                                "org.freedesktop.machine1.Manager",
+                                bus_machine_mgr,
                                 "RegisterMachineWithNetwork",
                                 &error,
                                 NULL,
@@ -141,13 +140,7 @@ int register_machine(
         } else {
                 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
 
-                r = sd_bus_message_new_method_call(
-                                bus,
-                                &m,
-                                "org.freedesktop.machine1",
-                                "/org/freedesktop/machine1",
-                                "org.freedesktop.machine1.Manager",
-                                "CreateMachineWithNetwork");
+                r = bus_message_new_method_call(bus, &m,  bus_machine_mgr, "CreateMachineWithNetwork");
                 if (r < 0)
                         return bus_log_create_error(r);
 
@@ -218,16 +211,7 @@ int unregister_machine(
 
         assert(bus);
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.machine1",
-                        "/org/freedesktop/machine1",
-                        "org.freedesktop.machine1.Manager",
-                        "UnregisterMachine",
-                        &error,
-                        NULL,
-                        "s",
-                        machine_name);
+        r = bus_call_method(bus, bus_machine_mgr, "UnregisterMachine", &error, NULL, "s", machine_name);
         if (r < 0)
                 log_debug("Failed to unregister machine: %s", bus_error_message(&error, r));
 
@@ -262,13 +246,7 @@ int allocate_scope(
         if (r < 0)
                 return log_error_errno(r, "Failed to mangle scope name: %m");
 
-        r = sd_bus_message_new_method_call(
-                        bus,
-                        &m,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "StartTransientUnit");
+        r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "StartTransientUnit");
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -354,26 +332,15 @@ int terminate_scope(
         if (r < 0)
                 return log_error_errno(r, "Failed to mangle scope name: %m");
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "AbandonScope",
-                        &error,
-                        NULL,
-                        "s",
-                        scope);
+        r = bus_call_method(bus, bus_systemd_mgr, "AbandonScope", &error, NULL, "s", scope);
         if (r < 0) {
                 log_debug_errno(r, "Failed to abandon scope '%s', ignoring: %s", scope, bus_error_message(&error, r));
                 sd_bus_error_free(&error);
         }
 
-        r = sd_bus_call_method(
+        r = bus_call_method(
                         bus,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
+                        bus_systemd_mgr,
                         "KillUnit",
                         &error,
                         NULL,
@@ -386,16 +353,7 @@ int terminate_scope(
                 sd_bus_error_free(&error);
         }
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "UnrefUnit",
-                        &error,
-                        NULL,
-                        "s",
-                        scope);
+        r = bus_call_method(bus, bus_systemd_mgr, "UnrefUnit", &error, NULL, "s", scope);
         if (r < 0)
                 log_debug_errno(r, "Failed to drop reference to scope '%s', ignoring: %s", scope, bus_error_message(&error, r));
 
index f94f131f22e29663a903346bebc4cf636d651765..79110d90d5e0560508269f87c13489febda5fd32 100644 (file)
@@ -25,13 +25,13 @@ static int seccomp_add_default_syscall_filter(
                 scmp_filter_ctx ctx,
                 uint32_t arch,
                 uint64_t cap_list_retain,
-                char **syscall_whitelist,
-                char **syscall_blacklist) {
+                char **syscall_allow_list,
+                char **syscall_deny_list) {
 
         static const struct {
                 uint64_t capability;
                 const char* name;
-        } whitelist[] = {
+        } allow_list[] = {
                 /* Let's use set names where we can */
                 { 0,                  "@aio"                   },
                 { 0,                  "@basic-io"              },
@@ -142,17 +142,17 @@ static int seccomp_add_default_syscall_filter(
         char **p;
         int r;
 
-        for (size_t i = 0; i < ELEMENTSOF(whitelist); i++) {
-                if (whitelist[i].capability != 0 && (cap_list_retain & (1ULL << whitelist[i].capability)) == 0)
+        for (size_t i = 0; i < ELEMENTSOF(allow_list); i++) {
+                if (allow_list[i].capability != 0 && (cap_list_retain & (1ULL << allow_list[i].capability)) == 0)
                         continue;
 
-                r = seccomp_add_syscall_filter_item(ctx, whitelist[i].name, SCMP_ACT_ALLOW, syscall_blacklist, false);
+                r = seccomp_add_syscall_filter_item(ctx, allow_list[i].name, SCMP_ACT_ALLOW, syscall_deny_list, false);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to add syscall filter item %s: %m", whitelist[i].name);
+                        return log_error_errno(r, "Failed to add syscall filter item %s: %m", allow_list[i].name);
         }
 
-        STRV_FOREACH(p, syscall_whitelist) {
-                r = seccomp_add_syscall_filter_item(ctx, *p, SCMP_ACT_ALLOW, syscall_blacklist, true);
+        STRV_FOREACH(p, syscall_allow_list) {
+                r = seccomp_add_syscall_filter_item(ctx, *p, SCMP_ACT_ALLOW, syscall_deny_list, true);
                 if (r < 0)
                         log_warning_errno(r, "Failed to add rule for system call %s on %s, ignoring: %m",
                                           *p, seccomp_arch_to_string(arch));
@@ -161,7 +161,7 @@ static int seccomp_add_default_syscall_filter(
         return 0;
 }
 
-int setup_seccomp(uint64_t cap_list_retain, char **syscall_whitelist, char **syscall_blacklist) {
+int setup_seccomp(uint64_t cap_list_retain, char **syscall_allow_list, char **syscall_deny_list) {
         uint32_t arch;
         int r;
 
@@ -173,13 +173,13 @@ int setup_seccomp(uint64_t cap_list_retain, char **syscall_whitelist, char **sys
         SECCOMP_FOREACH_LOCAL_ARCH(arch) {
                 _cleanup_(seccomp_releasep) scmp_filter_ctx seccomp = NULL;
 
-                log_debug("Applying whitelist on architecture: %s", seccomp_arch_to_string(arch));
+                log_debug("Applying allow list on architecture: %s", seccomp_arch_to_string(arch));
 
                 r = seccomp_init_for_arch(&seccomp, arch, SCMP_ACT_ERRNO(EPERM));
                 if (r < 0)
                         return log_error_errno(r, "Failed to allocate seccomp object: %m");
 
-                r = seccomp_add_default_syscall_filter(seccomp, arch, cap_list_retain, syscall_whitelist, syscall_blacklist);
+                r = seccomp_add_default_syscall_filter(seccomp, arch, cap_list_retain, syscall_allow_list, syscall_deny_list);
                 if (r < 0)
                         return r;
 
@@ -231,7 +231,7 @@ int setup_seccomp(uint64_t cap_list_retain, char **syscall_whitelist, char **sys
 
 #else
 
-int setup_seccomp(uint64_t cap_list_retain, char **syscall_whitelist, char **syscall_blacklist) {
+int setup_seccomp(uint64_t cap_list_retain, char **syscall_allow_list, char **syscall_deny_list) {
         return 0;
 }
 
index d852eef638b8682cceaede0bc58a4e8f87b055e5..4174323520998faf5529712c6d06c1c0461d4235 100644 (file)
@@ -3,4 +3,4 @@
 
 #include <sys/types.h>
 
-int setup_seccomp(uint64_t cap_list_retain, char **syscall_whitelist, char **syscall_blacklist);
+int setup_seccomp(uint64_t cap_list_retain, char **syscall_allow_ist, char **syscall_deny_list);
index 5fb5b49bbcc31b3c5e2c9fa40002b79b24eda39b..d341fa25aac423d1a13e6a8346a790fe8861efbb 100644 (file)
@@ -78,7 +78,7 @@ int settings_load(FILE *f, const char *path, Settings **ret) {
                          "Files\0",
                          config_item_perf_lookup, nspawn_gperf_lookup,
                          CONFIG_PARSE_WARN,
-                         s);
+                         s, NULL);
         if (r < 0)
                 return r;
 
@@ -129,8 +129,8 @@ Settings* settings_free(Settings *s) {
         free(s->pivot_root_new);
         free(s->pivot_root_old);
         free(s->working_directory);
-        strv_free(s->syscall_whitelist);
-        strv_free(s->syscall_blacklist);
+        strv_free(s->syscall_allow_list);
+        strv_free(s->syscall_deny_list);
         rlimit_free_all(s->rlimit);
         free(s->hostname);
         cpu_set_reset(&s->cpu_set);
@@ -295,35 +295,6 @@ int config_parse_capability(
         return 0;
 }
 
-int config_parse_id128(
-                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) {
-
-        sd_id128_t t, *result = data;
-        int r;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-
-        r = sd_id128_from_string(rvalue, &t);
-        if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse 128bit ID/UUID, ignoring: %s", rvalue);
-                return 0;
-        }
-
-        *result = t;
-        return 0;
-}
-
 int config_parse_pivot_root(
                 const char *unit,
                 const char *filename,
@@ -718,9 +689,9 @@ int config_parse_syscall_filter(
                 }
 
                 if (negative)
-                        r = strv_extend(&settings->syscall_blacklist, word);
+                        r = strv_extend(&settings->syscall_deny_list, word);
                 else
-                        r = strv_extend(&settings->syscall_whitelist, word);
+                        r = strv_extend(&settings->syscall_allow_list, word);
                 if (r < 0)
                         return log_oom();
         }
@@ -821,8 +792,16 @@ static const char *const resolv_conf_mode_table[_RESOLV_CONF_MODE_MAX] = {
         [RESOLV_CONF_OFF] = "off",
         [RESOLV_CONF_COPY_HOST] = "copy-host",
         [RESOLV_CONF_COPY_STATIC] = "copy-static",
+        [RESOLV_CONF_COPY_UPLINK] = "copy-uplink",
+        [RESOLV_CONF_COPY_STUB] = "copy-stub",
+        [RESOLV_CONF_REPLACE_HOST] = "replace-host",
+        [RESOLV_CONF_REPLACE_STATIC] = "replace-static",
+        [RESOLV_CONF_REPLACE_UPLINK] = "replace-uplink",
+        [RESOLV_CONF_REPLACE_STUB] = "replace-stub",
         [RESOLV_CONF_BIND_HOST] = "bind-host",
         [RESOLV_CONF_BIND_STATIC] = "bind-static",
+        [RESOLV_CONF_BIND_UPLINK] = "bind-uplink",
+        [RESOLV_CONF_BIND_STUB] = "bind-stub",
         [RESOLV_CONF_DELETE] = "delete",
         [RESOLV_CONF_AUTO] = "auto",
 };
index f1a1a754660563a7deee862ebeede3d13a46e56e..ab31c05a9e0e6b92bf512786f206717b3780c248 100644 (file)
@@ -38,10 +38,18 @@ typedef enum UserNamespaceMode {
 
 typedef enum ResolvConfMode {
         RESOLV_CONF_OFF,
-        RESOLV_CONF_COPY_HOST,
-        RESOLV_CONF_COPY_STATIC,
+        RESOLV_CONF_COPY_HOST,     /* /etc/resolv.conf */
+        RESOLV_CONF_COPY_STATIC,   /* /usr/lib/systemd/resolv.conf */
+        RESOLV_CONF_COPY_UPLINK,   /* /run/systemd/resolve/resolv.conf */
+        RESOLV_CONF_COPY_STUB,     /* /run/systemd/resolve/stub-resolv.conf */
+        RESOLV_CONF_REPLACE_HOST,
+        RESOLV_CONF_REPLACE_STATIC,
+        RESOLV_CONF_REPLACE_UPLINK,
+        RESOLV_CONF_REPLACE_STUB,
         RESOLV_CONF_BIND_HOST,
         RESOLV_CONF_BIND_STATIC,
+        RESOLV_CONF_BIND_UPLINK,
+        RESOLV_CONF_BIND_STUB,
         RESOLV_CONF_DELETE,
         RESOLV_CONF_AUTO,
         _RESOLV_CONF_MODE_MAX,
@@ -157,8 +165,8 @@ typedef struct Settings {
         UserNamespaceMode userns_mode;
         uid_t uid_shift, uid_range;
         bool notify_ready;
-        char **syscall_whitelist;
-        char **syscall_blacklist;
+        char **syscall_allow_list;
+        char **syscall_deny_list;
         struct rlimit *rlimit[_RLIMIT_MAX];
         char *hostname;
         int no_new_privileges;
@@ -226,7 +234,6 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Settings*, settings_free);
 const struct ConfigPerfItem* nspawn_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
 
 CONFIG_PARSER_PROTOTYPE(config_parse_capability);
-CONFIG_PARSER_PROTOTYPE(config_parse_id128);
 CONFIG_PARSER_PROTOTYPE(config_parse_expose_port);
 CONFIG_PARSER_PROTOTYPE(config_parse_volatile_mode);
 CONFIG_PARSER_PROTOTYPE(config_parse_pivot_root);
index cb2b2272b6bbeb177d2889b0d7aa553274c3d331..d0e575fef23e3611f4ee8b0cb3186468b26099c7 100644 (file)
@@ -118,10 +118,9 @@ int change_uid_gid(const char *user, char **_home) {
         if (fd < 0)
                 return fd;
 
-        f = fdopen(fd, "r");
+        f = take_fdopen(&fd, "r");
         if (!f)
                 return log_oom();
-        fd = -1;
 
         r = read_line(f, LONG_LINE_MAX, &line);
         if (r == 0)
@@ -191,10 +190,9 @@ int change_uid_gid(const char *user, char **_home) {
         if (fd < 0)
                 return fd;
 
-        f = fdopen(fd, "r");
+        f = take_fdopen(&fd, "r");
         if (!f)
                 return log_oom();
-        fd = -1;
 
         r = read_line(f, LONG_LINE_MAX, &line);
         if (r == 0)
index eaf2d7977a67c60f9a7eefc92256ef12a836c3b8..3b9493f232e26f4c15a0b7e236f5288e14afe8e2 100644 (file)
@@ -79,6 +79,7 @@
 #include "ptyfwd.h"
 #include "random-util.h"
 #include "raw-clone.h"
+#include "resolve-util.h"
 #include "rlimit-util.h"
 #include "rm-rf.h"
 #if HAVE_SECCOMP
 #include "user-util.h"
 #include "util.h"
 
-#if HAVE_SPLIT_USR
-#define STATIC_RESOLV_CONF "/lib/systemd/resolv.conf"
-#else
-#define STATIC_RESOLV_CONF "/usr/lib/systemd/resolv.conf"
-#endif
-
 /* nspawn is listening on the socket at the path in the constant nspawn_notify_socket_path
  * nspawn_notify_socket_path is relative to the container
  * the init process in the container pid can send messages to nspawn following the sd_notify(3) protocol */
@@ -204,9 +199,13 @@ static bool arg_use_cgns = true;
 static unsigned long arg_clone_ns_flags = CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS;
 static MountSettingsMask arg_mount_settings = MOUNT_APPLY_APIVFS_RO|MOUNT_APPLY_TMPFS_TMP;
 static void *arg_root_hash = NULL;
+static char *arg_verity_data = NULL;
+static char *arg_root_hash_sig_path = NULL;
+static void *arg_root_hash_sig = NULL;
+static size_t arg_root_hash_sig_size = 0;
 static size_t arg_root_hash_size = 0;
-static char **arg_syscall_whitelist = NULL;
-static char **arg_syscall_blacklist = NULL;
+static char **arg_syscall_allow_list = NULL;
+static char **arg_syscall_deny_list = NULL;
 #if HAVE_SECCOMP
 static scmp_filter_ctx arg_seccomp = NULL;
 #endif
@@ -247,8 +246,11 @@ STATIC_DESTRUCTOR_REGISTER(arg_property, strv_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_property_message, sd_bus_message_unrefp);
 STATIC_DESTRUCTOR_REGISTER(arg_parameters, strv_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_root_hash, freep);
-STATIC_DESTRUCTOR_REGISTER(arg_syscall_whitelist, strv_freep);
-STATIC_DESTRUCTOR_REGISTER(arg_syscall_blacklist, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_verity_data, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_root_hash_sig_path, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_root_hash_sig, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_syscall_allow_list, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_syscall_deny_list, strv_freep);
 #if HAVE_SECCOMP
 STATIC_DESTRUCTOR_REGISTER(arg_seccomp, seccomp_releasep);
 #endif
@@ -308,6 +310,11 @@ static int help(void) {
                "     --read-only            Mount the root directory read-only\n"
                "     --volatile[=MODE]      Run the system in volatile mode\n"
                "     --root-hash=HASH       Specify verity root hash for root disk image\n"
+               "     --root-hash-sig=SIG    Specify pkcs7 signature of root hash for verity\n"
+               "                            as a DER encoded PKCS7, either as a path to a file\n"
+               "                            or as an ASCII base64 encoded string prefixed by\n"
+               "                            'base64:'\n"
+               "     --verity-data=PATH     Specify hash device for verity\n"
                "     --pivot-root=PATH[:PATH]\n"
                "                            Pivot root to given directory in the container\n\n"
                "%3$sExecution:%4$s\n"
@@ -668,6 +675,8 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_PIPE,
                 ARG_OCI_BUNDLE,
                 ARG_NO_PAGER,
+                ARG_VERITY_DATA,
+                ARG_ROOT_HASH_SIG,
         };
 
         static const struct option options[] = {
@@ -733,6 +742,8 @@ static int parse_argv(int argc, char *argv[]) {
                 { "pipe",                   no_argument,       NULL, ARG_PIPE                   },
                 { "oci-bundle",             required_argument, NULL, ARG_OCI_BUNDLE             },
                 { "no-pager",               no_argument,       NULL, ARG_NO_PAGER               },
+                { "verity-data",            required_argument, NULL, ARG_VERITY_DATA            },
+                { "root-hash-sig",          required_argument, NULL, ARG_ROOT_HASH_SIG          },
                 {}
         };
 
@@ -1321,6 +1332,37 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
                 }
 
+                case ARG_VERITY_DATA:
+                        r = parse_path_argument_and_warn(optarg, false, &arg_verity_data);
+                        if (r < 0)
+                                return r;
+                        break;
+
+                case ARG_ROOT_HASH_SIG: {
+                        char *value;
+
+                        if ((value = startswith(optarg, "base64:"))) {
+                                void *p;
+                                size_t l;
+
+                                r = unbase64mem(value, strlen(value), &p, &l);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to parse root hash signature '%s': %m", optarg);
+
+                                free_and_replace(arg_root_hash_sig, p);
+                                arg_root_hash_sig_size = l;
+                                arg_root_hash_sig_path = mfree(arg_root_hash_sig_path);
+                        } else {
+                                r = parse_path_argument_and_warn(optarg, false, &arg_root_hash_sig_path);
+                                if (r < 0)
+                                        return r;
+                                arg_root_hash_sig = mfree(arg_root_hash_sig);
+                                arg_root_hash_sig_size = 0;
+                        }
+
+                        break;
+                }
+
                 case ARG_SYSTEM_CALL_FILTER: {
                         bool negative;
                         const char *items;
@@ -1340,9 +1382,9 @@ static int parse_argv(int argc, char *argv[]) {
                                         return log_error_errno(r, "Failed to parse system call filter: %m");
 
                                 if (negative)
-                                        r = strv_extend(&arg_syscall_blacklist, word);
+                                        r = strv_extend(&arg_syscall_deny_list, word);
                                 else
-                                        r = strv_extend(&arg_syscall_whitelist, word);
+                                        r = strv_extend(&arg_syscall_allow_list, word);
                                 if (r < 0)
                                         return log_oom();
                         }
@@ -1850,12 +1892,13 @@ static int setup_resolv_conf(const char *dest) {
         if (arg_resolv_conf == RESOLV_CONF_AUTO) {
                 if (arg_private_network)
                         m = RESOLV_CONF_OFF;
-                else if (have_resolv_conf(STATIC_RESOLV_CONF) > 0 && resolved_listening() > 0)
-                        m = etc_writable() ? RESOLV_CONF_COPY_STATIC : RESOLV_CONF_BIND_STATIC;
+                else if (have_resolv_conf(PRIVATE_STUB_RESOLV_CONF) > 0 && resolved_listening() > 0)
+                        m = etc_writable() ? RESOLV_CONF_COPY_STUB : RESOLV_CONF_BIND_STUB;
                 else if (have_resolv_conf("/etc/resolv.conf") > 0)
                         m = etc_writable() ? RESOLV_CONF_COPY_HOST : RESOLV_CONF_BIND_HOST;
                 else
                         m = etc_writable() ? RESOLV_CONF_DELETE : RESOLV_CONF_OFF;
+
         } else
                 m = arg_resolv_conf;
 
@@ -1877,12 +1920,16 @@ static int setup_resolv_conf(const char *dest) {
                 return 0;
         }
 
-        if (IN_SET(m, RESOLV_CONF_BIND_STATIC, RESOLV_CONF_COPY_STATIC))
-                what = STATIC_RESOLV_CONF;
+        if (IN_SET(m, RESOLV_CONF_BIND_STATIC, RESOLV_CONF_REPLACE_STATIC, RESOLV_CONF_COPY_STATIC))
+                what = PRIVATE_STATIC_RESOLV_CONF;
+        else if (IN_SET(m, RESOLV_CONF_BIND_UPLINK, RESOLV_CONF_REPLACE_UPLINK, RESOLV_CONF_COPY_UPLINK))
+                what = PRIVATE_UPLINK_RESOLV_CONF;
+        else if (IN_SET(m, RESOLV_CONF_BIND_STUB, RESOLV_CONF_REPLACE_STUB, RESOLV_CONF_COPY_STUB))
+                what = PRIVATE_STUB_RESOLV_CONF;
         else
                 what = "/etc/resolv.conf";
 
-        if (IN_SET(m, RESOLV_CONF_BIND_HOST, RESOLV_CONF_BIND_STATIC)) {
+        if (IN_SET(m, RESOLV_CONF_BIND_HOST, RESOLV_CONF_BIND_STATIC, RESOLV_CONF_BIND_UPLINK, RESOLV_CONF_BIND_STUB)) {
                 _cleanup_free_ char *resolved = NULL;
                 int found;
 
@@ -1898,17 +1945,22 @@ static int setup_resolv_conf(const char *dest) {
                 r = mount_verbose(LOG_WARNING, what, resolved, NULL, MS_BIND, NULL);
                 if (r >= 0)
                         return mount_verbose(LOG_ERR, NULL, resolved, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_NOSUID|MS_NODEV, NULL);
+
+                /* If that didn't work, let's copy the file */
         }
 
-        /* If that didn't work, let's copy the file */
-        r = copy_file(what, where, O_TRUNC|O_NOFOLLOW, 0644, 0, 0, COPY_REFLINK);
+        if (IN_SET(m, RESOLV_CONF_REPLACE_HOST, RESOLV_CONF_REPLACE_STATIC, RESOLV_CONF_REPLACE_UPLINK, RESOLV_CONF_REPLACE_STUB))
+                r = copy_file_atomic(what, where, 0644, 0, 0, COPY_REFLINK|COPY_REPLACE);
+        else
+                r = copy_file(what, where, O_TRUNC|O_NOFOLLOW, 0644, 0, 0, COPY_REFLINK);
         if (r < 0) {
                 /* If the file already exists as symlink, let's suppress the warning, under the assumption that
                  * resolved or something similar runs inside and the symlink points there.
                  *
                  * If the disk image is read-only, there's also no point in complaining.
                  */
-                log_full_errno(!IN_SET(RESOLV_CONF_COPY_HOST, RESOLV_CONF_COPY_STATIC) && IN_SET(r, -ELOOP, -EROFS, -EACCES, -EPERM) ? LOG_DEBUG : LOG_WARNING, r,
+                log_full_errno(!IN_SET(RESOLV_CONF_COPY_HOST, RESOLV_CONF_COPY_STATIC, RESOLV_CONF_COPY_UPLINK, RESOLV_CONF_COPY_STUB) &&
+                               IN_SET(r, -ELOOP, -EROFS, -EACCES, -EPERM) ? LOG_DEBUG : LOG_WARNING, r,
                                "Failed to copy /etc/resolv.conf to %s, ignoring: %m", where);
                 return 0;
         }
@@ -2159,10 +2211,11 @@ static int setup_dev_console(const char *console) {
 static int setup_keyring(void) {
         key_serial_t keyring;
 
-        /* Allocate a new session keyring for the container. This makes sure the keyring of the session systemd-nspawn
-         * was invoked from doesn't leak into the container. Note that by default we block keyctl() and request_key()
-         * anyway via seccomp so doing this operation isn't strictly necessary, but in case people explicitly whitelist
-         * these system calls let's make sure we don't leak anything into the container. */
+        /* Allocate a new session keyring for the container. This makes sure the keyring of the session
+         * systemd-nspawn was invoked from doesn't leak into the container. Note that by default we block
+         * keyctl() and request_key() anyway via seccomp so doing this operation isn't strictly necessary,
+         * but in case people explicitly allow-list these system calls let's make sure we don't leak anything
+         * into the container. */
 
         keyring = keyctl(KEYCTL_JOIN_SESSION_KEYRING, 0, 0, 0, 0);
         if (keyring == -1) {
@@ -2878,7 +2931,8 @@ static int inner_child(
                 int kmsg_socket,
                 int rtnl_socket,
                 int master_pty_socket,
-                FDSet *fds) {
+                FDSet *fds,
+                char **os_release_pairs) {
 
         _cleanup_free_ char *home = NULL;
         char as_uuid[ID128_UUID_STRING_MAX];
@@ -2976,13 +3030,10 @@ static int inner_child(
                                 arg_uid_range,
                                 arg_selinux_apifs_context,
                                 true);
-                if (r < 0)
-                        return r;
-        } else {
+        } else
                 r = mount_systemd_cgroup_writable("", arg_unified_cgroup_hierarchy);
-                if (r < 0)
-                        return r;
-        }
+        if (r < 0)
+                return r;
 
         r = setup_boot_id();
         if (r < 0)
@@ -3083,7 +3134,7 @@ static int inner_child(
         } else
 #endif
         {
-                r = setup_seccomp(arg_caps_retain, arg_syscall_whitelist, arg_syscall_blacklist);
+                r = setup_seccomp(arg_caps_retain, arg_syscall_allow_list, arg_syscall_deny_list);
                 if (r < 0)
                         return r;
         }
@@ -3147,7 +3198,7 @@ static int inner_child(
         if (asprintf((char **)(envp + n_env++), "NOTIFY_SOCKET=%s", NSPAWN_NOTIFY_SOCKET_PATH) < 0)
                 return log_oom();
 
-        env_use = strv_env_merge(2, envp, arg_setenv);
+        env_use = strv_env_merge(3, envp, os_release_pairs, arg_setenv);
         if (!env_use)
                 return log_oom();
 
@@ -3273,6 +3324,7 @@ static int outer_child(
                 FDSet *fds,
                 int netns_fd) {
 
+        _cleanup_strv_free_ char **os_release_pairs = NULL;
         _cleanup_close_ int fd = -1;
         const char *p;
         pid_t pid;
@@ -3294,6 +3346,10 @@ static int outer_child(
 
         log_debug("Outer child is initializing.");
 
+        r = load_os_release_pairs_with_prefix("/", "container_host_", &os_release_pairs);
+        if (r < 0)
+                log_debug_errno(r, "Failed to read os-release from host for container, ignoring: %m");
+
         if (prctl(PR_SET_PDEATHSIG, SIGKILL) < 0)
                 return log_error_errno(errno, "PR_SET_PDEATHSIG failed: %m");
 
@@ -3475,7 +3531,7 @@ static int outer_child(
 
         (void) dev_setup(directory, arg_uid_shift, arg_uid_shift);
 
-        p = prefix_roota(directory, "/run/systemd");
+        p = prefix_roota(directory, "/run");
         (void) make_inaccessible_nodes(p, arg_uid_shift, arg_uid_shift);
 
         r = setup_pts(directory);
@@ -3557,7 +3613,7 @@ static int outer_child(
                                 return log_error_errno(r, "Failed to join network namespace: %m");
                 }
 
-                r = inner_child(barrier, directory, secondary, kmsg_socket, rtnl_socket, master_pty_socket, fds);
+                r = inner_child(barrier, directory, secondary, kmsg_socket, rtnl_socket, master_pty_socket, fds, os_release_pairs);
                 if (r < 0)
                         _exit(EXIT_FAILURE);
 
@@ -3690,19 +3746,15 @@ static int nspawn_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t r
                 .iov_base = buf,
                 .iov_len = sizeof(buf)-1,
         };
-        union {
-                struct cmsghdr cmsghdr;
-                uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) +
-                            CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX)];
-        } control = {};
+        CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred)) +
+                         CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX)) control;
         struct msghdr msghdr = {
                 .msg_iov = &iovec,
                 .msg_iovlen = 1,
                 .msg_control = &control,
                 .msg_controllen = sizeof(control),
         };
-        struct cmsghdr *cmsg;
-        struct ucred *ucred = NULL;
+        struct ucred *ucred;
         ssize_t n;
         pid_t inner_child_pid;
         _cleanup_strv_free_ char **tags = NULL;
@@ -3724,15 +3776,7 @@ static int nspawn_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t r
 
         cmsg_close_all(&msghdr);
 
-        CMSG_FOREACH(cmsg, &msghdr) {
-                if (cmsg->cmsg_level == SOL_SOCKET &&
-                           cmsg->cmsg_type == SCM_CREDENTIALS &&
-                           cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
-
-                        ucred = (struct ucred*) CMSG_DATA(cmsg);
-                }
-        }
-
+        ucred = CMSG_FIND_DATA(&msghdr, SOL_SOCKET, SCM_CREDENTIALS, struct ucred);
         if (!ucred || ucred->pid != inner_child_pid) {
                 log_debug("Received notify message without valid credentials. Ignoring.");
                 return 0;
@@ -3958,11 +4002,11 @@ static int merge_settings(Settings *settings, const char *path) {
 
         if ((arg_settings_mask & SETTING_SYSCALL_FILTER) == 0) {
 
-                if (!arg_settings_trusted && !strv_isempty(settings->syscall_whitelist))
+                if (!arg_settings_trusted && !strv_isempty(settings->syscall_allow_list))
                         log_warning("Ignoring SystemCallFilter= settings, file %s is not trusted.", path);
                 else {
-                        strv_free_and_replace(arg_syscall_whitelist, settings->syscall_whitelist);
-                        strv_free_and_replace(arg_syscall_blacklist, settings->syscall_blacklist);
+                        strv_free_and_replace(arg_syscall_allow_list, settings->syscall_allow_list);
+                        strv_free_and_replace(arg_syscall_deny_list, settings->syscall_deny_list);
                 }
 
 #if HAVE_SECCOMP
@@ -4597,7 +4641,7 @@ static int run_container(
         if (!barrier_place_and_sync(&barrier)) /* #5 */
                 return log_error_errno(SYNTHETIC_ERRNO(ESRCH), "Child died too early.");
 
-        /* At this point we have made use of the UID we picked, and thus nss-mymachines
+        /* At this point we have made use of the UID we picked, and thus nss-systemd/systemd-machined.service
          * will make them appear in getpwuid(), thus we can release the /etc/passwd lock. */
         etc_passwd_lock = safe_close(etc_passwd_lock);
 
@@ -4822,6 +4866,58 @@ static int initialize_rlimits(void) {
         return 0;
 }
 
+static int cant_be_in_netns(void) {
+        union sockaddr_union sa = {
+                .un = {
+                        .sun_family = AF_UNIX,
+                        .sun_path = "/run/udev/control",
+                },
+        };
+        char udev_path[STRLEN("/proc//ns/net") + DECIMAL_STR_MAX(pid_t)];
+        _cleanup_free_ char *udev_ns = NULL, *our_ns = NULL;
+        _cleanup_close_ int fd = -1;
+        struct ucred ucred;
+        int r;
+
+        /* Check if we are in the same netns as udev. If we aren't, then device monitoring (and thus waiting
+         * for loopback block devices) won't work, and we will hang. Detect this case and exit early with a
+         * nice message. */
+
+        if (!arg_image) /* only matters if --image= us used, i.e. we actually need to use loopback devices */
+                return 0;
+
+        fd = socket(AF_UNIX, SOCK_SEQPACKET|SOCK_NONBLOCK|SOCK_CLOEXEC, 0);
+        if (fd < 0)
+                return log_error_errno(errno, "Failed to allocate udev control socket: %m");
+
+        if (connect(fd, &sa.un, SOCKADDR_UN_LEN(sa.un)) < 0) {
+
+                if (errno == ENOENT || ERRNO_IS_DISCONNECT(errno))
+                        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+                                               "Sorry, but --image= requires access to the host's /run/ hierarchy, since we need access to udev.");
+
+                return log_error_errno(errno, "Failed to connect socket to udev control socket: %m");
+        }
+
+        r = getpeercred(fd, &ucred);
+        if (r < 0)
+                return log_error_errno(r, "Failed to determine peer of udev control socket: %m");
+
+        xsprintf(udev_path, "/proc/" PID_FMT "/ns/net", ucred.pid);
+        r = readlink_malloc(udev_path, &udev_ns);
+        if (r < 0)
+                return log_error_errno(r, "Failed to read network namespace of udev: %m");
+
+        r = readlink_malloc("/proc/self/ns/net", &our_ns);
+        if (r < 0)
+                return log_error_errno(r, "Failed to read our own network namespace: %m");
+
+        if (!streq(our_ns, udev_ns))
+                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+                                       "Sorry, but --image= is only supported in the main network namespace, since we need access to udev/AF_NETLINK.");
+        return 0;
+}
+
 static int run(int argc, char *argv[]) {
         bool secondary = false, remove_directory = false, remove_image = false,
                 veth_created = false, remove_tmprootdir = false;
@@ -4848,6 +4944,10 @@ static int run(int argc, char *argv[]) {
         if (r < 0)
                 goto finish;
 
+        r = cant_be_in_netns();
+        if (r < 0)
+                goto finish;
+
         r = initialize_rlimits();
         if (r < 0)
                 goto finish;
@@ -4934,7 +5034,7 @@ static int run(int argc, char *argv[]) {
                         }
 
                         /* We take an exclusive lock on this image, since it's our private, ephemeral copy
-                         * only owned by us and noone else. */
+                         * only owned by us and no one else. */
                         r = image_path_lock(np, LOCK_EX|LOCK_NB, &tree_global_lock, &tree_local_lock);
                         if (r < 0) {
                                 log_error_errno(r, "Failed to lock %s: %m", np);
@@ -5039,6 +5139,7 @@ static int run(int argc, char *argv[]) {
                 }
 
         } else {
+                DissectImageFlags dissect_image_flags = DISSECT_IMAGE_REQUIRE_ROOT | DISSECT_IMAGE_RELAX_VAR_CHECK;
                 assert(arg_image);
                 assert(!arg_template);
 
@@ -5088,13 +5189,14 @@ static int run(int argc, char *argv[]) {
                                 goto finish;
                         }
 
-                        if (!arg_root_hash) {
-                                r = root_hash_load(arg_image, &arg_root_hash, &arg_root_hash_size);
-                                if (r < 0) {
-                                        log_error_errno(r, "Failed to load root hash file for %s: %m", arg_image);
-                                        goto finish;
-                                }
+                        r = verity_metadata_load(arg_image, NULL, arg_root_hash ? NULL : &arg_root_hash, &arg_root_hash_size,
+                                        arg_verity_data ? NULL : &arg_verity_data,
+                                        arg_root_hash_sig_path || arg_root_hash_sig ? NULL : &arg_root_hash_sig_path);
+                        if (r < 0) {
+                                log_error_errno(r, "Failed to read verity artefacts for %s: %m", arg_image);
+                                goto finish;
                         }
+                        dissect_image_flags |= arg_verity_data ? DISSECT_IMAGE_NO_PARTITION_TABLE : 0;
                 }
 
                 if (!mkdtemp(tmprootdir)) {
@@ -5120,7 +5222,8 @@ static int run(int argc, char *argv[]) {
                                 loop->fd,
                                 arg_image,
                                 arg_root_hash, arg_root_hash_size,
-                                DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_RELAX_VAR_CHECK,
+                                arg_verity_data,
+                                dissect_image_flags,
                                 &dissected_image);
                 if (r == -ENOPKG) {
                         /* dissected_image_and_warn() already printed a brief error message. Extend on that with more details */
@@ -5138,7 +5241,7 @@ static int run(int argc, char *argv[]) {
                 if (!arg_root_hash && dissected_image->can_verity)
                         log_notice("Note: image %s contains verity information, but no root hash specified! Proceeding without integrity checking.", arg_image);
 
-                r = dissected_image_decrypt_interactively(dissected_image, NULL, arg_root_hash, arg_root_hash_size, 0, &decrypted_image);
+                r = dissected_image_decrypt_interactively(dissected_image, NULL, arg_root_hash, arg_root_hash_size, arg_verity_data, arg_root_hash_sig_path, arg_root_hash_sig, arg_root_hash_sig_size, 0, &decrypted_image);
                 if (r < 0)
                         goto finish;
 
index 8c16b7f360c294704a802224b8b3c9a063bae6cd..9aa6debc164e945be224444f8238248e0a79c1e7 100644 (file)
@@ -77,7 +77,7 @@ enum nss_status _nss_myhostname_gethostbyname4_r(
                         return NSS_STATUS_TRYAGAIN;
                 }
 
-                /* We respond to our local host name, our hostname suffixed with a single dot. */
+                /* We respond to our local hostname, our hostname suffixed with a single dot. */
                 if (!streq(name, hn) && !streq_ptr(startswith(name, hn), "."))
                         goto not_found;
 
index 364356da56222df8cd1f150fb04b87c6aba39f5b..5db0dcef7687358331cc6eb1d70693522a171354 100644 (file)
@@ -8,6 +8,7 @@
 
 #include "alloc-util.h"
 #include "bus-common-errors.h"
+#include "bus-locator.h"
 #include "env-util.h"
 #include "errno-util.h"
 #include "format-util.h"
 #include "nss-util.h"
 #include "signal-util.h"
 #include "string-util.h"
-#include "user-util.h"
 
 NSS_GETHOSTBYNAME_PROTOTYPES(mymachines);
 NSS_GETPW_PROTOTYPES(mymachines);
 NSS_GETGR_PROTOTYPES(mymachines);
 
-#define HOST_UID_LIMIT ((uid_t) UINT32_C(0x10000))
-#define HOST_GID_LIMIT ((gid_t) UINT32_C(0x10000))
-
 static int count_addresses(sd_bus_message *m, int af, unsigned *ret) {
         unsigned c = 0;
         int r;
@@ -128,14 +125,7 @@ enum nss_status _nss_mymachines_gethostbyname4_r(
         if (r < 0)
                 goto fail;
 
-        r = sd_bus_call_method(bus,
-                               "org.freedesktop.machine1",
-                               "/org/freedesktop/machine1",
-                               "org.freedesktop.machine1.Manager",
-                               "GetMachineAddresses",
-                               NULL,
-                               &reply,
-                               "s", name);
+        r = bus_call_method(bus, bus_machine_mgr, "GetMachineAddresses", NULL, &reply, "s", name);
         if (r < 0)
                 goto fail;
 
@@ -287,14 +277,7 @@ enum nss_status _nss_mymachines_gethostbyname3_r(
         if (r < 0)
                 goto fail;
 
-        r = sd_bus_call_method(bus,
-                               "org.freedesktop.machine1",
-                               "/org/freedesktop/machine1",
-                               "org.freedesktop.machine1.Manager",
-                               "GetMachineAddresses",
-                               NULL,
-                               &reply,
-                               "s", name);
+        r = bus_call_method(bus, bus_machine_mgr, "GetMachineAddresses", NULL, &reply, "s", name);
         if (r < 0)
                 goto fail;
 
@@ -415,102 +398,7 @@ enum nss_status _nss_mymachines_getpwnam_r(
                 char *buffer, size_t buflen,
                 int *errnop) {
 
-        _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 *p, *e, *machine;
-        uint32_t mapped;
-        uid_t uid;
-        size_t l;
-        int r;
-
-        PROTECT_ERRNO;
-        BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
-
-        assert(name);
-        assert(pwd);
-
-        p = startswith(name, "vu-");
-        if (!p)
-                return NSS_STATUS_NOTFOUND;
-
-        e = strrchr(p, '-');
-        if (!e || e == p)
-                return NSS_STATUS_NOTFOUND;
-
-        if (e - p > HOST_NAME_MAX - 1) /* -1 for the last dash */
-                return NSS_STATUS_NOTFOUND;
-
-        r = parse_uid(e + 1, &uid);
-        if (r < 0)
-                return NSS_STATUS_NOTFOUND;
-
-        machine = strndupa(p, e - p);
-        if (!machine_name_is_valid(machine))
-                return NSS_STATUS_NOTFOUND;
-
-        if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0)
-                /* Make sure we can't deadlock if we are invoked by dbus-daemon. This way, it won't be able to resolve
-                 * these UIDs, but that should be unproblematic as containers should never be able to connect to a bus
-                 * running on the host. */
-                return NSS_STATUS_NOTFOUND;
-
-        if (avoid_deadlock()) {
-                r = -EDEADLK;
-                goto fail;
-        }
-
-        r = sd_bus_open_system(&bus);
-        if (r < 0)
-                goto fail;
-
-        r = sd_bus_call_method(bus,
-                               "org.freedesktop.machine1",
-                               "/org/freedesktop/machine1",
-                               "org.freedesktop.machine1.Manager",
-                               "MapFromMachineUser",
-                               &error,
-                               &reply,
-                               "su",
-                               machine, (uint32_t) uid);
-        if (r < 0) {
-                if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_USER_MAPPING))
-                        return NSS_STATUS_NOTFOUND;
-
-                goto fail;
-        }
-
-        r = sd_bus_message_read(reply, "u", &mapped);
-        if (r < 0)
-                goto fail;
-
-        /* Refuse to work if the mapped address is in the host UID range, or if there was no mapping at all. */
-        if (mapped < HOST_UID_LIMIT || mapped == uid)
-                return NSS_STATUS_NOTFOUND;
-
-        l = strlen(name);
-        if (buflen < l+1) {
-                UNPROTECT_ERRNO;
-                *errnop = ERANGE;
-                return NSS_STATUS_TRYAGAIN;
-        }
-
-        memcpy(buffer, name, l+1);
-
-        pwd->pw_name = buffer;
-        pwd->pw_uid = mapped;
-        pwd->pw_gid = GID_NOBODY;
-        pwd->pw_gecos = buffer;
-        pwd->pw_passwd = (char*) "*"; /* locked */
-        pwd->pw_dir = (char*) "/";
-        pwd->pw_shell = (char*) NOLOGIN;
-
-        return NSS_STATUS_SUCCESS;
-
-fail:
-        UNPROTECT_ERRNO;
-        *errnop = -r;
-        return NSS_STATUS_UNAVAIL;
+        return NSS_STATUS_NOTFOUND;
 }
 
 enum nss_status _nss_mymachines_getpwuid_r(
@@ -519,178 +407,16 @@ enum nss_status _nss_mymachines_getpwuid_r(
                 char *buffer, size_t buflen,
                 int *errnop) {
 
-        _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 *machine;
-        uint32_t mapped;
-        int r;
-
-        PROTECT_ERRNO;
-        BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
-
-        if (!uid_is_valid(uid))
-                return NSS_STATUS_NOTFOUND;
-
-        /* We consider all uids < 65536 host uids */
-        if (uid < HOST_UID_LIMIT)
-                return NSS_STATUS_NOTFOUND;
-
-        if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0)
-                return NSS_STATUS_NOTFOUND;
-
-        if (avoid_deadlock()) {
-                r = -EDEADLK;
-                goto fail;
-        }
-
-        r = sd_bus_open_system(&bus);
-        if (r < 0)
-                goto fail;
-
-        r = sd_bus_call_method(bus,
-                               "org.freedesktop.machine1",
-                               "/org/freedesktop/machine1",
-                               "org.freedesktop.machine1.Manager",
-                               "MapToMachineUser",
-                               &error,
-                               &reply,
-                               "u",
-                               (uint32_t) uid);
-        if (r < 0) {
-                if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_USER_MAPPING))
-                        return NSS_STATUS_NOTFOUND;
-
-                goto fail;
-        }
-
-        r = sd_bus_message_read(reply, "sou", &machine, NULL, &mapped);
-        if (r < 0)
-                goto fail;
-
-        if (mapped == uid)
-                return NSS_STATUS_NOTFOUND;
-
-        if (snprintf(buffer, buflen, "vu-%s-" UID_FMT, machine, (uid_t) mapped) >= (int) buflen) {
-                UNPROTECT_ERRNO;
-                *errnop = ERANGE;
-                return NSS_STATUS_TRYAGAIN;
-        }
-
-        pwd->pw_name = buffer;
-        pwd->pw_uid = uid;
-        pwd->pw_gid = GID_NOBODY;
-        pwd->pw_gecos = buffer;
-        pwd->pw_passwd = (char*) "*"; /* locked */
-        pwd->pw_dir = (char*) "/";
-        pwd->pw_shell = (char*) NOLOGIN;
-
-        return NSS_STATUS_SUCCESS;
-
-fail:
-        UNPROTECT_ERRNO;
-        *errnop = -r;
-        return NSS_STATUS_UNAVAIL;
+        return NSS_STATUS_NOTFOUND;
 }
 
-#pragma GCC diagnostic ignored "-Wsizeof-pointer-memaccess"
-
 enum nss_status _nss_mymachines_getgrnam_r(
                 const char *name,
                 struct group *gr,
                 char *buffer, size_t buflen,
                 int *errnop) {
 
-        _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 *p, *e, *machine;
-        uint32_t mapped;
-        uid_t gid;
-        size_t l;
-        int r;
-
-        PROTECT_ERRNO;
-        BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
-
-        assert(name);
-        assert(gr);
-
-        p = startswith(name, "vg-");
-        if (!p)
-                return NSS_STATUS_NOTFOUND;
-
-        e = strrchr(p, '-');
-        if (!e || e == p)
-                return NSS_STATUS_NOTFOUND;
-
-        if (e - p > HOST_NAME_MAX - 1)  /* -1 for the last dash */
-                return NSS_STATUS_NOTFOUND;
-
-        r = parse_gid(e + 1, &gid);
-        if (r < 0)
-                return NSS_STATUS_NOTFOUND;
-
-        machine = strndupa(p, e - p);
-        if (!machine_name_is_valid(machine))
-                return NSS_STATUS_NOTFOUND;
-
-        if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0)
-                return NSS_STATUS_NOTFOUND;
-
-        if (avoid_deadlock()) {
-                r = -EDEADLK;
-                goto fail;
-        }
-
-        r = sd_bus_open_system(&bus);
-        if (r < 0)
-                goto fail;
-
-        r = sd_bus_call_method(bus,
-                               "org.freedesktop.machine1",
-                               "/org/freedesktop/machine1",
-                               "org.freedesktop.machine1.Manager",
-                               "MapFromMachineGroup",
-                               &error,
-                               &reply,
-                               "su",
-                               machine, (uint32_t) gid);
-        if (r < 0) {
-                if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_GROUP_MAPPING))
-                        return NSS_STATUS_NOTFOUND;
-
-                goto fail;
-        }
-
-        r = sd_bus_message_read(reply, "u", &mapped);
-        if (r < 0)
-                goto fail;
-
-        if (mapped < HOST_GID_LIMIT || mapped == gid)
-                return NSS_STATUS_NOTFOUND;
-
-        l = sizeof(char*) + strlen(name) + 1;
-        if (buflen < l) {
-                UNPROTECT_ERRNO;
-                *errnop = ERANGE;
-                return NSS_STATUS_TRYAGAIN;
-        }
-
-        memzero(buffer, sizeof(char*));
-        strcpy(buffer + sizeof(char*), name);
-
-        gr->gr_name = buffer + sizeof(char*);
-        gr->gr_gid = mapped;
-        gr->gr_passwd = (char*) "*"; /* locked */
-        gr->gr_mem = (char**) buffer;
-
-        return NSS_STATUS_SUCCESS;
-
-fail:
-        UNPROTECT_ERRNO;
-        *errnop = -r;
-        return NSS_STATUS_UNAVAIL;
+        return NSS_STATUS_NOTFOUND;
 }
 
 enum nss_status _nss_mymachines_getgrgid_r(
@@ -699,80 +425,5 @@ enum nss_status _nss_mymachines_getgrgid_r(
                 char *buffer, size_t buflen,
                 int *errnop) {
 
-        _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 *machine;
-        uint32_t mapped;
-        int r;
-
-        PROTECT_ERRNO;
-        BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
-
-        if (!gid_is_valid(gid))
-                return NSS_STATUS_NOTFOUND;
-
-        /* We consider all gids < 65536 host gids */
-        if (gid < HOST_GID_LIMIT)
-                return NSS_STATUS_NOTFOUND;
-
-        if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS") > 0)
-                return NSS_STATUS_NOTFOUND;
-
-        if (avoid_deadlock()) {
-                r = -EDEADLK;
-                goto fail;
-        }
-
-        r = sd_bus_open_system(&bus);
-        if (r < 0)
-                goto fail;
-
-        r = sd_bus_call_method(bus,
-                               "org.freedesktop.machine1",
-                               "/org/freedesktop/machine1",
-                               "org.freedesktop.machine1.Manager",
-                               "MapToMachineGroup",
-                               &error,
-                               &reply,
-                               "u",
-                               (uint32_t) gid);
-        if (r < 0) {
-                if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_GROUP_MAPPING))
-                        return NSS_STATUS_NOTFOUND;
-
-                goto fail;
-        }
-
-        r = sd_bus_message_read(reply, "sou", &machine, NULL, &mapped);
-        if (r < 0)
-                goto fail;
-
-        if (mapped == gid)
-                return NSS_STATUS_NOTFOUND;
-
-        if (buflen < sizeof(char*) + 1) {
-                UNPROTECT_ERRNO;
-                *errnop = ERANGE;
-                return NSS_STATUS_TRYAGAIN;
-        }
-
-        memzero(buffer, sizeof(char*));
-        if (snprintf(buffer + sizeof(char*), buflen - sizeof(char*), "vg-%s-" GID_FMT, machine, (gid_t) mapped) >= (int) buflen) {
-                UNPROTECT_ERRNO;
-                *errnop = ERANGE;
-                return NSS_STATUS_TRYAGAIN;
-        }
-
-        gr->gr_name = buffer + sizeof(char*);
-        gr->gr_gid = gid;
-        gr->gr_passwd = (char*) "*"; /* locked */
-        gr->gr_mem = (char**) buffer;
-
-        return NSS_STATUS_SUCCESS;
-
-fail:
-        UNPROTECT_ERRNO;
-        *errnop = -r;
-        return NSS_STATUS_UNAVAIL;
+        return NSS_STATUS_NOTFOUND;
 }
index de46a8d4697c2d6bfc08ff63d37549620fa94585..43ab5216353d3bd421a9a70e71091957cd089506 100644 (file)
@@ -10,6 +10,7 @@
 #include "sd-bus.h"
 
 #include "bus-common-errors.h"
+#include "bus-locator.h"
 #include "errno-util.h"
 #include "in-addr-util.h"
 #include "macro.h"
@@ -142,13 +143,7 @@ enum nss_status _nss_resolve_gethostbyname4_r(
         if (r < 0)
                 goto fail;
 
-        r = sd_bus_message_new_method_call(
-                        bus,
-                        &req,
-                        "org.freedesktop.resolve1",
-                        "/org/freedesktop/resolve1",
-                        "org.freedesktop.resolve1.Manager",
-                        "ResolveHostname");
+        r = bus_message_new_method_call(bus, &req, bus_resolve_mgr, "ResolveHostname");
         if (r < 0)
                 goto fail;
 
@@ -322,13 +317,7 @@ enum nss_status _nss_resolve_gethostbyname3_r(
         if (r < 0)
                 goto fail;
 
-        r = sd_bus_message_new_method_call(
-                        bus,
-                        &req,
-                        "org.freedesktop.resolve1",
-                        "/org/freedesktop/resolve1",
-                        "org.freedesktop.resolve1.Manager",
-                        "ResolveHostname");
+        r = bus_message_new_method_call(bus, &req, bus_resolve_mgr, "ResolveHostname");
         if (r < 0)
                 goto fail;
 
@@ -514,13 +503,7 @@ enum nss_status _nss_resolve_gethostbyaddr2_r(
         if (r < 0)
                 goto fail;
 
-        r = sd_bus_message_new_method_call(
-                        bus,
-                        &req,
-                        "org.freedesktop.resolve1",
-                        "/org/freedesktop/resolve1",
-                        "org.freedesktop.resolve1.Manager",
-                        "ResolveAddress");
+        r = bus_message_new_method_call(bus, &req, bus_resolve_mgr, "ResolveAddress");
         if (r < 0)
                 goto fail;
 
index 6a38246f8636e3cb77d8a8f1d9e2875df54aad25..5dc5aacdff200a37fdb2829e4c0814c2472323b4 100644 (file)
@@ -8,7 +8,9 @@
 #include "fd-util.h"
 #include "group-record-nss.h"
 #include "macro.h"
+#include "nss-systemd.h"
 #include "nss-util.h"
+#include "pthread-util.h"
 #include "signal-util.h"
 #include "strv.h"
 #include "user-util.h"
@@ -277,10 +279,11 @@ static enum nss_status nss_systemd_endent(GetentData *p) {
 
         assert(p);
 
-        assert_se(pthread_mutex_lock(&p->mutex) == 0);
+        _cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = NULL;
+        _l = pthread_mutex_lock_assert(&p->mutex);
+
         p->iterator = userdb_iterator_free(p->iterator);
         p->by_membership = false;
-        assert_se(pthread_mutex_unlock(&p->mutex) == 0);
 
         return NSS_STATUS_SUCCESS;
 }
@@ -294,45 +297,47 @@ enum nss_status _nss_systemd_endgrent(void) {
 }
 
 enum nss_status _nss_systemd_setpwent(int stayopen) {
-        enum nss_status ret;
-
         PROTECT_ERRNO;
         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
 
-        if (userdb_nss_compat_is_enabled() <= 0)
+        if (_nss_systemd_is_blocked())
                 return NSS_STATUS_NOTFOUND;
 
-        assert_se(pthread_mutex_lock(&getpwent_data.mutex) == 0);
+        _cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = NULL;
+        int r;
+
+        _l = pthread_mutex_lock_assert(&getpwent_data.mutex);
 
         getpwent_data.iterator = userdb_iterator_free(getpwent_data.iterator);
         getpwent_data.by_membership = false;
 
-        ret = userdb_all(nss_glue_userdb_flags(), &getpwent_data.iterator) < 0 ?
-                NSS_STATUS_UNAVAIL : NSS_STATUS_SUCCESS;
-
-        assert_se(pthread_mutex_unlock(&getpwent_data.mutex) == 0);
-        return ret;
+        /* Don't synthesize root/nobody when iterating. Let nss-files take care of that. If the two records
+         * are missing there, then that's fine, after all getpwent() is known to be possibly incomplete
+         * (think: LDAP/NIS type situations), and our synthesizing of root/nobody is a robustness fallback
+         * only, which matters for getpwnam()/getpwuid() primarily, which are the main NSS entrypoints to the
+         * user database. */
+        r = userdb_all(nss_glue_userdb_flags() | USERDB_DONT_SYNTHESIZE, &getpwent_data.iterator);
+        return r < 0 ? NSS_STATUS_UNAVAIL : NSS_STATUS_SUCCESS;
 }
 
 enum nss_status _nss_systemd_setgrent(int stayopen) {
-        enum nss_status ret;
-
         PROTECT_ERRNO;
         BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
 
-        if (userdb_nss_compat_is_enabled() <= 0)
+        if (_nss_systemd_is_blocked())
                 return NSS_STATUS_NOTFOUND;
 
-        assert_se(pthread_mutex_lock(&getgrent_data.mutex) == 0);
+        _cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = NULL;
+        int r;
+
+        _l = pthread_mutex_lock_assert(&getgrent_data.mutex);
 
         getgrent_data.iterator = userdb_iterator_free(getgrent_data.iterator);
         getpwent_data.by_membership = false;
 
-        ret = groupdb_all(nss_glue_userdb_flags(), &getgrent_data.iterator) < 0 ?
-                NSS_STATUS_UNAVAIL : NSS_STATUS_SUCCESS;
-
-        assert_se(pthread_mutex_unlock(&getgrent_data.mutex) == 0);
-        return ret;
+        /* See _nss_systemd_setpwent() for an explanation why we use USERDB_DONT_SYNTHESIZE here */
+        r = groupdb_all(nss_glue_userdb_flags() | USERDB_DONT_SYNTHESIZE, &getgrent_data.iterator);
+        return r < 0 ? NSS_STATUS_UNAVAIL : NSS_STATUS_SUCCESS;
 }
 
 enum nss_status _nss_systemd_getpwent_r(
@@ -341,7 +346,6 @@ enum nss_status _nss_systemd_getpwent_r(
                 int *errnop) {
 
         _cleanup_(user_record_unrefp) UserRecord *ur = NULL;
-        enum nss_status ret;
         int r;
 
         PROTECT_ERRNO;
@@ -350,49 +354,36 @@ enum nss_status _nss_systemd_getpwent_r(
         assert(result);
         assert(errnop);
 
-        r = userdb_nss_compat_is_enabled();
-        if (r < 0) {
-                UNPROTECT_ERRNO;
-                *errnop = -r;
-                return NSS_STATUS_UNAVAIL;
-        }
-        if (!r)
+        if (_nss_systemd_is_blocked())
                 return NSS_STATUS_NOTFOUND;
 
-        assert_se(pthread_mutex_lock(&getpwent_data.mutex) == 0);
+        _cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = NULL;
+
+        _l = pthread_mutex_lock_assert(&getpwent_data.mutex);
 
         if (!getpwent_data.iterator) {
                 UNPROTECT_ERRNO;
                 *errnop = EHOSTDOWN;
-                ret = NSS_STATUS_UNAVAIL;
-                goto finish;
+                return NSS_STATUS_UNAVAIL;
         }
 
         r = userdb_iterator_get(getpwent_data.iterator, &ur);
-        if (r == -ESRCH) {
-                ret = NSS_STATUS_NOTFOUND;
-                goto finish;
-        }
+        if (r == -ESRCH)
+                return NSS_STATUS_NOTFOUND;
         if (r < 0) {
                 UNPROTECT_ERRNO;
                 *errnop = -r;
-                ret = NSS_STATUS_UNAVAIL;
-                goto finish;
+                return NSS_STATUS_UNAVAIL;
         }
 
         r = nss_pack_user_record(ur, result, buffer, buflen);
         if (r < 0) {
                 UNPROTECT_ERRNO;
                 *errnop = -r;
-                ret = NSS_STATUS_TRYAGAIN;
-                goto finish;
+                return NSS_STATUS_TRYAGAIN;
         }
 
-        ret = NSS_STATUS_SUCCESS;
-
-finish:
-        assert_se(pthread_mutex_unlock(&getpwent_data.mutex) == 0);
-        return ret;
+        return NSS_STATUS_SUCCESS;
 }
 
 enum nss_status _nss_systemd_getgrent_r(
@@ -402,7 +393,6 @@ enum nss_status _nss_systemd_getgrent_r(
 
         _cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
         _cleanup_free_ char **members = NULL;
-        enum nss_status ret;
         int r;
 
         PROTECT_ERRNO;
@@ -411,22 +401,17 @@ enum nss_status _nss_systemd_getgrent_r(
         assert(result);
         assert(errnop);
 
-        r = userdb_nss_compat_is_enabled();
-        if (r < 0) {
-                UNPROTECT_ERRNO;
-                *errnop = -r;
-                return NSS_STATUS_UNAVAIL;
-        }
-        if (!r)
-                return NSS_STATUS_UNAVAIL;
+        if (_nss_systemd_is_blocked())
+                return NSS_STATUS_NOTFOUND;
+
+        _cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = NULL;
 
-        assert_se(pthread_mutex_lock(&getgrent_data.mutex) == 0);
+        _l = pthread_mutex_lock_assert(&getgrent_data.mutex);
 
         if (!getgrent_data.iterator) {
                 UNPROTECT_ERRNO;
                 *errnop = EHOSTDOWN;
-                ret = NSS_STATUS_UNAVAIL;
-                goto finish;
+                return NSS_STATUS_UNAVAIL;
         }
 
         if (!getgrent_data.by_membership) {
@@ -444,43 +429,37 @@ enum nss_status _nss_systemd_getgrent_r(
                         if (r < 0) {
                                 UNPROTECT_ERRNO;
                                 *errnop = -r;
-                                ret = NSS_STATUS_UNAVAIL;
-                                goto finish;
+                                return NSS_STATUS_UNAVAIL;
                         }
 
                         getgrent_data.by_membership = true;
                 } else if (r < 0) {
                         UNPROTECT_ERRNO;
                         *errnop = -r;
-                        ret = NSS_STATUS_UNAVAIL;
-                        goto finish;
+                        return NSS_STATUS_UNAVAIL;
                 } else if (!STR_IN_SET(gr->group_name, root_group.gr_name, nobody_group.gr_name)) {
                         r = membershipdb_by_group_strv(gr->group_name, nss_glue_userdb_flags(), &members);
                         if (r < 0) {
                                 UNPROTECT_ERRNO;
                                 *errnop = -r;
-                                ret = NSS_STATUS_UNAVAIL;
-                                goto finish;
+                                return NSS_STATUS_UNAVAIL;
                         }
                 }
         }
 
         if (getgrent_data.by_membership) {
-                _cleanup_close_ int lock_fd = -1;
+                _cleanup_(_nss_systemd_unblockp) bool blocked = false;
 
                 for (;;) {
                         _cleanup_free_ char *user_name = NULL, *group_name = NULL;
 
                         r = membershipdb_iterator_get(getgrent_data.iterator, &user_name, &group_name);
-                        if (r == -ESRCH) {
-                                ret = NSS_STATUS_NOTFOUND;
-                                goto finish;
-                        }
+                        if (r == -ESRCH)
+                                return NSS_STATUS_NOTFOUND;
                         if (r < 0) {
                                 UNPROTECT_ERRNO;
                                 *errnop = -r;
-                                ret = NSS_STATUS_UNAVAIL;
-                                goto finish;
+                                return NSS_STATUS_UNAVAIL;
                         }
 
                         if (STR_IN_SET(user_name, root_passwd.pw_name, nobody_passwd.pw_name))
@@ -489,17 +468,18 @@ enum nss_status _nss_systemd_getgrent_r(
                                 continue;
 
                         /* We are about to recursively call into NSS, let's make sure we disable recursion into our own code. */
-                        if (lock_fd < 0) {
-                                lock_fd = userdb_nss_compat_disable();
-                                if (lock_fd < 0 && lock_fd != -EBUSY) {
+                        if (!blocked) {
+                                r = _nss_systemd_block(true);
+                                if (r < 0) {
                                         UNPROTECT_ERRNO;
-                                        *errnop = -lock_fd;
-                                        ret = NSS_STATUS_UNAVAIL;
-                                        goto finish;
+                                        *errnop = -r;
+                                        return NSS_STATUS_UNAVAIL;
                                 }
+
+                                blocked = true;
                         }
 
-                        r = nss_group_record_by_name(group_name, &gr);
+                        r = nss_group_record_by_name(group_name, false, &gr);
                         if (r == -ESRCH)
                                 continue;
                         if (r < 0) {
@@ -511,8 +491,7 @@ enum nss_status _nss_systemd_getgrent_r(
                         if (!members) {
                                 UNPROTECT_ERRNO;
                                 *errnop = ENOMEM;
-                                ret = NSS_STATUS_TRYAGAIN;
-                                goto finish;
+                                return NSS_STATUS_TRYAGAIN;
                         }
 
                         /* Note that we currently generate one group entry per user that is part of a
@@ -526,15 +505,10 @@ enum nss_status _nss_systemd_getgrent_r(
         if (r < 0) {
                 UNPROTECT_ERRNO;
                 *errnop = -r;
-                ret = NSS_STATUS_TRYAGAIN;
-                goto finish;
+                return NSS_STATUS_TRYAGAIN;
         }
 
-        ret = NSS_STATUS_SUCCESS;
-
-finish:
-        assert_se(pthread_mutex_unlock(&getgrent_data.mutex) == 0);
-        return ret;
+        return NSS_STATUS_SUCCESS;
 }
 
 enum nss_status _nss_systemd_initgroups_dyn(
@@ -566,13 +540,7 @@ enum nss_status _nss_systemd_initgroups_dyn(
         if (STR_IN_SET(user_name, root_passwd.pw_name, nobody_passwd.pw_name))
                 return NSS_STATUS_NOTFOUND;
 
-        r = userdb_nss_compat_is_enabled();
-        if (r < 0) {
-                UNPROTECT_ERRNO;
-                *errnop = -r;
-                return NSS_STATUS_UNAVAIL;
-        }
-        if (!r)
+        if (_nss_systemd_is_blocked())
                 return NSS_STATUS_NOTFOUND;
 
         r = membershipdb_by_user(user_name, nss_glue_userdb_flags(), &iterator);
@@ -598,7 +566,7 @@ enum nss_status _nss_systemd_initgroups_dyn(
                 /* The group might be defined via traditional NSS only, hence let's do a full look-up without
                  * disabling NSS. This means we are operating recursively here. */
 
-                r = groupdb_by_name(group_name, nss_glue_userdb_flags() & ~USERDB_AVOID_NSS, &g);
+                r = groupdb_by_name(group_name, (nss_glue_userdb_flags() & ~USERDB_AVOID_NSS) | USERDB_AVOID_SHADOW, &g);
                 if (r == -ESRCH)
                         continue;
                 if (r < 0) {
@@ -644,3 +612,29 @@ enum nss_status _nss_systemd_initgroups_dyn(
 
         return any ? NSS_STATUS_SUCCESS : NSS_STATUS_NOTFOUND;
 }
+
+static thread_local unsigned _blocked = 0;
+
+_public_ int _nss_systemd_block(bool b) {
+
+        /* This blocks recursively: it's blocked for as many times this function is called with `true` until
+         * it is called an equal time with `false`. */
+
+        if (b) {
+                if (_blocked >= UINT_MAX)
+                        return -EOVERFLOW;
+
+                _blocked++;
+        } else {
+                if (_blocked <= 0)
+                        return -EOVERFLOW;
+
+                _blocked--;
+        }
+
+        return b; /* Return what is passed in, i.e. the new state from the PoV of the caller */
+}
+
+_public_ bool _nss_systemd_is_blocked(void) {
+        return _blocked > 0;
+}
diff --git a/src/nss-systemd/nss-systemd.h b/src/nss-systemd/nss-systemd.h
new file mode 100644 (file)
index 0000000..ffa75c1
--- /dev/null
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <stdbool.h>
+
+int _nss_systemd_block(bool b);
+bool _nss_systemd_is_blocked(void);
+
+/* For use with the _cleanup_() macro */
+static inline void _nss_systemd_unblockp(bool *b) {
+        if (*b)
+                assert_se(_nss_systemd_block(false) >= 0);
+}
index 77e1fbe93f227e6e878c7847bb9f773b9dfa7769..f86d7643d1a412dc316c1f5633187c4b8c55a760 100644 (file)
@@ -20,5 +20,9 @@ global:
         _nss_systemd_setgrent;
         _nss_systemd_getgrent_r;
         _nss_systemd_initgroups_dyn;
+
+        /* These two are not used by glibc, but can be used by apps to explicitly disable nss-systemd for the calling thread. */
+        _nss_systemd_block;
+        _nss_systemd_is_blocked;
 local: *;
 };
index 58915c3d23dde6f75e301b6b4ce08fb72d06f12d..8e5b3eba6c08076eb533aff60bb25fdb9efad976 100644 (file)
@@ -3,6 +3,7 @@
 #include "env-util.h"
 #include "fd-util.h"
 #include "group-record-nss.h"
+#include "nss-systemd.h"
 #include "strv.h"
 #include "user-record.h"
 #include "userdb-glue.h"
@@ -74,12 +75,7 @@ enum nss_status userdb_getpwnam(
         assert(pwd);
         assert(errnop);
 
-        r = userdb_nss_compat_is_enabled();
-        if (r < 0) {
-                *errnop = -r;
-                return NSS_STATUS_UNAVAIL;
-        }
-        if (!r)
+        if (_nss_systemd_is_blocked())
                 return NSS_STATUS_NOTFOUND;
 
         r = userdb_by_name(name, nss_glue_userdb_flags(), &hr);
@@ -112,12 +108,7 @@ enum nss_status userdb_getpwuid(
         assert(pwd);
         assert(errnop);
 
-        r = userdb_nss_compat_is_enabled();
-        if (r < 0) {
-                *errnop = -r;
-                return NSS_STATUS_UNAVAIL;
-        }
-        if (!r)
+        if (_nss_systemd_is_blocked())
                 return NSS_STATUS_NOTFOUND;
 
         r = userdb_by_uid(uid, nss_glue_userdb_flags(), &hr);
@@ -214,12 +205,7 @@ enum nss_status userdb_getgrnam(
         assert(gr);
         assert(errnop);
 
-        r = userdb_nss_compat_is_enabled();
-        if (r < 0) {
-                *errnop = -r;
-                return NSS_STATUS_UNAVAIL;
-        }
-        if (!r)
+        if (_nss_systemd_is_blocked())
                 return NSS_STATUS_NOTFOUND;
 
         r = groupdb_by_name(name, nss_glue_userdb_flags(), &g);
@@ -235,7 +221,7 @@ enum nss_status userdb_getgrnam(
         }
 
         if (!g) {
-                _cleanup_close_ int lock_fd = -1;
+                _cleanup_(_nss_systemd_unblockp) bool blocked = false;
 
                 if (strv_isempty(members))
                         return NSS_STATUS_NOTFOUND;
@@ -245,13 +231,15 @@ enum nss_status userdb_getgrnam(
                  * acquire it, so that we can extend it (that's because glibc's group merging feature will
                  * merge groups only if both GID and name match and thus we need to have both first). It
                  * sucks behaving recursively likely this, but it's apparently what everybody does. We break
-                 * the recursion for ourselves via the userdb_nss_compat_disable() lock. */
+                 * the recursion for ourselves via the _nss_systemd_block_nss() lock. */
+
+                r = _nss_systemd_block(true);
+                if (r < 0)
+                        return r;
 
-                lock_fd = userdb_nss_compat_disable();
-                if (lock_fd < 0 && lock_fd != -EBUSY)
-                        return lock_fd;
+                blocked = true;
 
-                r = nss_group_record_by_name(name, &g);
+                r = nss_group_record_by_name(name, false, &g);
                 if (r == -ESRCH)
                         return NSS_STATUS_NOTFOUND;
                 if (r < 0) {
@@ -285,12 +273,7 @@ enum nss_status userdb_getgrgid(
         assert(gr);
         assert(errnop);
 
-        r = userdb_nss_compat_is_enabled();
-        if (r < 0) {
-                *errnop = -r;
-                return NSS_STATUS_UNAVAIL;
-        }
-        if (!r)
+        if (_nss_systemd_is_blocked())
                 return NSS_STATUS_NOTFOUND;
 
         r = groupdb_by_gid(gid, nss_glue_userdb_flags(), &g);
@@ -300,20 +283,21 @@ enum nss_status userdb_getgrgid(
         }
 
         if (!g) {
-                _cleanup_close_ int lock_fd = -1;
+                _cleanup_(_nss_systemd_unblockp) bool blocked = false;
 
                 /* So, quite possibly we have to extend an existing group record with additional members. But
                  * to do this we need to know the group name first. The group didn't exist via non-NSS
                  * queries though, hence let's try to acquire it here recursively via NSS. */
 
-                lock_fd = userdb_nss_compat_disable();
-                if (lock_fd < 0 && lock_fd != -EBUSY)
-                        return lock_fd;
+                r = _nss_systemd_block(true);
+                if (r < 0)
+                        return r;
 
-                r = nss_group_record_by_gid(gid, &g);
+                blocked = true;
+
+                r = nss_group_record_by_gid(gid, false, &g);
                 if (r == -ESRCH)
                         return NSS_STATUS_NOTFOUND;
-
                 if (r < 0) {
                         *errnop = -r;
                         return NSS_STATUS_UNAVAIL;
@@ -329,7 +313,7 @@ enum nss_status userdb_getgrgid(
                 return NSS_STATUS_UNAVAIL;
         }
 
-        /* If we acquired the record via NSS then there's no reason to respond unless we have to agument the
+        /* If we acquired the record via NSS then there's no reason to respond unless we have to augment the
          * list of members of the group */
         if (from_nss && strv_isempty(members))
                 return NSS_STATUS_NOTFOUND;
index 7e2452a5d15d17bc2e0f5e9958b6246cd3041cb4..98a7e4d31de1037ef541d5e592b574a1de45831f 100644 (file)
@@ -79,7 +79,7 @@ static int resize_crypt_luks_device(dev_t devno, const char *fstype, dev_t main_
 }
 #endif
 
-static int maybe_resize_slave_device(const char *mountpath, dev_t main_devno) {
+static int maybe_resize_underlying_device(const char *mountpath, dev_t main_devno) {
         _cleanup_free_ char *fstype = NULL, *devpath = NULL;
         dev_t devno;
         int r;
@@ -213,7 +213,7 @@ static int run(int argc, char *argv[]) {
         if (r < 0)
                 return log_error_errno(r, "Failed to determine block device of \"%s\": %m", arg_target);
 
-        r = maybe_resize_slave_device(arg_target, devno);
+        r = maybe_resize_underlying_device(arg_target, devno);
         if (r < 0)
                 return r;
 
index df08a5fea654c2ede00c5671d3eccd82316cdc5b..97f50c90335bcd426ef4158b4fa5ad533e0bd495 100644 (file)
@@ -7,7 +7,9 @@
 #include <unistd.h>
 
 #include "alloc-util.h"
+#include "blockdev-util.h"
 #include "dissect-image.h"
+#include "fd-util.h"
 #include "main-func.h"
 #include "process-util.h"
 #include "signal-util.h"
@@ -42,6 +44,7 @@ static int makefs(const char *type, const char *device) {
 
 static int run(int argc, char *argv[]) {
         _cleanup_free_ char *device = NULL, *type = NULL, *detected = NULL;
+        _cleanup_close_ int lock_fd = -1;
         struct stat st;
         int r;
 
@@ -63,19 +66,22 @@ static int run(int argc, char *argv[]) {
         if (stat(device, &st) < 0)
                 return log_error_errno(errno, "Failed to stat \"%s\": %m", device);
 
-        if (!S_ISBLK(st.st_mode))
+        if (S_ISBLK(st.st_mode)) {
+                /* Lock the device so that udev doesn't interfere with our work */
+
+                lock_fd = lock_whole_block_device(st.st_rdev, LOCK_EX);
+                if (lock_fd < 0)
+                        return log_error_errno(lock_fd, "Failed to lock whole block device of \"%s\": %m", device);
+        } else
                 log_info("%s is not a block device.", device);
 
         r = probe_filesystem(device, &detected);
+        if (r == -EUCLEAN)
+                return log_error_errno(r, "Ambiguous results of probing for file system on \"%s\", refusing to proceed.", device);
         if (r < 0)
-                return log_warning_errno(r,
-                                         r == -EUCLEAN ?
-                                         "Cannot reliably determine probe \"%s\", refusing to proceed." :
-                                         "Failed to probe \"%s\": %m",
-                                         device);
-
+                return log_error_errno(r, "Failed to probe \"%s\": %m", device);
         if (detected) {
-                log_info("%s is not empty (type %s), exiting", device, detected);
+                log_info("'%s' is not empty (contains file system of type %s), exiting.", device, detected);
                 return 0;
         }
 
index d0c111a473bd2c52349d245149cdf4ba7a78304e..3a75d5712d5f03868c185b34f11c2dcc6498bed4 100644 (file)
@@ -3,3 +3,5 @@
 systemd_repart_sources = files('''
         repart.c
 '''.split())
+
+test_repart_sh = find_program('test-repart.sh')
index 376ec33706bb8fa161652c9f33932d66ca4a1fec..2e5f5d13455d63ad5980863e7c4d2537a57a1cd6 100644 (file)
@@ -41,6 +41,7 @@
 #include "pretty-print.h"
 #include "proc-cmdline.h"
 #include "sort-util.h"
+#include "specifier.h"
 #include "stat-util.h"
 #include "stdio-util.h"
 #include "string-util.h"
@@ -64,6 +65,7 @@ static enum {
         EMPTY_ALLOW,    /* allow empty disks, create partition table if necessary */
         EMPTY_REQUIRE,  /* require an empty disk, create a partition table */
         EMPTY_FORCE,    /* make disk empty, erase everything, create a partition table always */
+        EMPTY_CREATE,   /* create disk as loopback file, create a partition table always */
 } arg_empty = EMPTY_REFUSE;
 
 static bool arg_dry_run = true;
@@ -76,6 +78,7 @@ static int arg_factory_reset = -1;
 static sd_id128_t arg_seed = SD_ID128_NULL;
 static bool arg_randomize = false;
 static int arg_pretty = -1;
+static uint64_t arg_size = UINT64_MAX;
 
 STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_definitions, freep);
@@ -111,6 +114,10 @@ struct Partition {
         FreeArea *padding_area;
         FreeArea *allocated_to_area;
 
+        char *copy_blocks_path;
+        int copy_blocks_fd;
+        uint64_t copy_blocks_size;
+
         LIST_FIELDS(Partition, partitions);
 };
 
@@ -171,6 +178,8 @@ static Partition *partition_new(void) {
                 .padding_max = UINT64_MAX,
                 .partno = UINT64_MAX,
                 .offset = UINT64_MAX,
+                .copy_blocks_fd = -1,
+                .copy_blocks_size = UINT64_MAX,
         };
 
         return p;
@@ -189,6 +198,9 @@ static Partition* partition_free(Partition *p) {
         if (p->new_partition)
                 fdisk_unref_partition(p->new_partition);
 
+        free(p->copy_blocks_path);
+        safe_close(p->copy_blocks_fd);
+
         return mfree(p);
 }
 
@@ -336,7 +348,11 @@ static uint64_t partition_min_size(const Partition *p) {
         }
 
         sz = p->current_size != UINT64_MAX ? p->current_size : HARD_MIN_SIZE;
-        return MAX(p->size_min == UINT64_MAX ? DEFAULT_MIN_SIZE : p->size_min, sz);
+
+        if (p->copy_blocks_size != UINT64_MAX)
+                sz = MAX(p->copy_blocks_size, sz);
+
+        return MAX(p->size_min != UINT64_MAX ? p->size_min : DEFAULT_MIN_SIZE, sz);
 }
 
 static uint64_t partition_max_size(const Partition *p) {
@@ -391,7 +407,7 @@ static uint64_t free_area_available_for_new_partitions(const FreeArea *a) {
         uint64_t avail;
 
         /* Similar to free_area_available(), but takes into account that the required size and padding of the
-         * preceeding partition is honoured. */
+         * preceding partition is honoured. */
 
         avail = free_area_available(a);
         if (a->after) {
@@ -558,7 +574,7 @@ static int context_grow_partitions_phase(
         LIST_FOREACH(partitions, p, context->partitions) {
 
                 /* Look only at partitions associated with this free area, i.e. immediately
-                 * preceeding it, or allocated into it */
+                 * preceding it, or allocated into it */
                 if (p->allocated_to_area != a && p->padding_area != a)
                         continue;
 
@@ -687,7 +703,7 @@ static int context_grow_partitions_on_free_area(Context *context, FreeArea *a) {
                         break;
         }
 
-        /* We still have space left over? Donate to preceeding partition if we have one */
+        /* We still have space left over? Donate to preceding partition if we have one */
         if (span > 0 && a->after && !PARTITION_IS_FOREIGN(a->after)) {
                 uint64_t m, xsz;
 
@@ -702,7 +718,7 @@ static int context_grow_partitions_on_free_area(Context *context, FreeArea *a) {
                 a->after->new_size = m;
         }
 
-        /* What? Even still some space left (maybe because there was no preceeding partition, or it had a
+        /* What? Even still some space left (maybe because there was no preceding partition, or it had a
          * size limit), then let's donate it to whoever wants it. */
         if (span > 0) {
                 Partition *p;
@@ -731,7 +747,7 @@ static int context_grow_partitions_on_free_area(Context *context, FreeArea *a) {
                 }
         }
 
-        /* Yuck, still noone? Then make it padding */
+        /* Yuck, still no one? Then make it padding */
         if (span > 0 && a->after) {
                 assert(a->after->new_padding != UINT64_MAX);
                 a->after->new_padding += span;
@@ -860,33 +876,53 @@ static int config_parse_label(
                 void *data,
                 void *userdata) {
 
+        static const Specifier specifier_table[] = {
+                { 'm', specifier_machine_id,      NULL },
+                { 'b', specifier_boot_id,         NULL },
+                { 'H', specifier_host_name,       NULL },
+                { 'l', specifier_short_host_name, NULL },
+                { 'v', specifier_kernel_release,  NULL },
+                { 'a', specifier_architecture,    NULL },
+                { 'o', specifier_os_id,           NULL },
+                { 'w', specifier_os_version_id,   NULL },
+                { 'B', specifier_os_build_id,     NULL },
+                { 'W', specifier_os_variant_id,   NULL },
+                {}
+        };
+
         _cleanup_free_ char16_t *recoded = NULL;
+        _cleanup_free_ char *resolved = NULL;
         char **label = data;
         int r;
 
         assert(rvalue);
         assert(label);
 
-        if (!utf8_is_valid(rvalue)) {
+        r = specifier_printf(rvalue, specifier_table, NULL, &resolved);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, r,
+                           "Failed to expand specifiers in Label=, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        if (!utf8_is_valid(resolved)) {
                 log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "Partition label not valid UTF-8, ignoring: %s", rvalue);
                 return 0;
         }
 
-        recoded = utf8_to_utf16(rvalue, strlen(rvalue));
+        recoded = utf8_to_utf16(resolved, strlen(resolved));
         if (!recoded)
                 return log_oom();
 
         if (char16_strlen(recoded) > 36) {
                 log_syntax(unit, LOG_WARNING, filename, line, 0,
-                           "Partition label too long for GPT table, ignoring: %s", rvalue);
+                           "Partition label too long for GPT table, ignoring: \"%s\" (from \"%s\")",
+                           resolved, rvalue);
                 return 0;
         }
 
-        r = free_and_strdup(label, rvalue);
-        if (r < 0)
-                return log_oom();
-
+        free_and_replace(*label, resolved);
         return 0;
 }
 
@@ -916,7 +952,7 @@ static int config_parse_weight(
         }
 
         if (v > 1000U*1000U) {
-                log_syntax(unit, LOG_WARNING, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
                            "Weight needs to be in range 0…10000000, ignoring: %" PRIu32, v);
                 return 0;
         }
@@ -945,7 +981,7 @@ static int config_parse_size4096(
 
         r = parse_size(rvalue, 1024, &parsed);
         if (r < 0)
-                return log_syntax(unit, LOG_WARNING, filename, line, r,
+                return log_syntax(unit, LOG_ERR, filename, line, r,
                                   "Failed to parse size value: %s", rvalue);
 
         if (ltype > 0)
@@ -964,21 +1000,28 @@ static int config_parse_size4096(
 static int partition_read_definition(Partition *p, const char *path) {
 
         ConfigTableItem table[] = {
-                { "Partition", "Type",            config_parse_type,     0,  &p->type_uuid      },
-                { "Partition", "Label",           config_parse_label,    0,  &p->new_label      },
-                { "Partition", "Priority",        config_parse_int32,    0,  &p->priority       },
-                { "Partition", "Weight",          config_parse_weight,   0,  &p->weight         },
-                { "Partition", "PaddingWeight",   config_parse_weight,   0,  &p->padding_weight },
-                { "Partition", "SizeMinBytes",    config_parse_size4096, 1,  &p->size_min       },
-                { "Partition", "SizeMaxBytes",    config_parse_size4096, -1, &p->size_max       },
-                { "Partition", "PaddingMinBytes", config_parse_size4096, 1,  &p->padding_min    },
-                { "Partition", "PaddingMaxBytes", config_parse_size4096, -1, &p->padding_max    },
-                { "Partition", "FactoryReset",    config_parse_bool,     0,  &p->factory_reset  },
+                { "Partition", "Type",            config_parse_type,     0,  &p->type_uuid        },
+                { "Partition", "Label",           config_parse_label,    0,  &p->new_label        },
+                { "Partition", "UUID",            config_parse_id128,    0,  &p->new_uuid         },
+                { "Partition", "Priority",        config_parse_int32,    0,  &p->priority         },
+                { "Partition", "Weight",          config_parse_weight,   0,  &p->weight           },
+                { "Partition", "PaddingWeight",   config_parse_weight,   0,  &p->padding_weight   },
+                { "Partition", "SizeMinBytes",    config_parse_size4096, 1,  &p->size_min         },
+                { "Partition", "SizeMaxBytes",    config_parse_size4096, -1, &p->size_max         },
+                { "Partition", "PaddingMinBytes", config_parse_size4096, 1,  &p->padding_min      },
+                { "Partition", "PaddingMaxBytes", config_parse_size4096, -1, &p->padding_max      },
+                { "Partition", "FactoryReset",    config_parse_bool,     0,  &p->factory_reset    },
+                { "Partition", "CopyBlocks",      config_parse_path,     0,  &p->copy_blocks_path },
                 {}
         };
         int r;
 
-        r = config_parse(NULL, path, NULL, "Partition\0", config_item_table_lookup, table, CONFIG_PARSE_WARN, p);
+        r = config_parse(NULL, path, NULL,
+                         "Partition\0",
+                         config_item_table_lookup, table,
+                         CONFIG_PARSE_WARN,
+                         p,
+                         NULL);
         if (r < 0)
                 return r;
 
@@ -1171,7 +1214,11 @@ static int disk_acquire_uuid(Context *context, sd_id128_t *ret) {
         return 0;
 }
 
-static int context_load_partition_table(Context *context, const char *node) {
+static int context_load_partition_table(
+                Context *context,
+                const char *node,
+                int *backing_fd) {
+
         _cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
         _cleanup_(fdisk_unref_tablep) struct fdisk_table *t = NULL;
         uint64_t left_boundary = UINT64_MAX, first_lba, last_lba, nsectors;
@@ -1183,14 +1230,31 @@ static int context_load_partition_table(Context *context, const char *node) {
 
         assert(context);
         assert(node);
+        assert(backing_fd);
 
         c = fdisk_new_context();
         if (!c)
                 return log_oom();
 
-        r = fdisk_assign_device(c, node, arg_dry_run);
+        /* libfdisk doesn't have an API to operate on arbitrary fds, hence reopen the fd going via the
+         * /proc/self/fd/ magic path if we have an existing fd. Open the original file otherwise. */
+        if (*backing_fd < 0)
+                r = fdisk_assign_device(c, node, arg_dry_run);
+        else {
+                char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
+                xsprintf(procfs_path, "/proc/self/fd/%i", *backing_fd);
+
+                r = fdisk_assign_device(c, procfs_path, arg_dry_run);
+        }
         if (r < 0)
-                return log_error_errno(r, "Failed to open device: %m");
+                return log_error_errno(r, "Failed to open device '%s': %m", node);
+
+        if (*backing_fd < 0) {
+                /* If we have no fd referencing the device yet, make a copy of the fd now, so that we have one */
+                *backing_fd = fcntl(fdisk_get_devfd(c), F_DUPFD_CLOEXEC, 3);
+                if (*backing_fd < 0)
+                        return log_error_errno(errno, "Failed to duplicate fdisk fd: %m");
+        }
 
         /* Tell udev not to interfere while we are processing the device */
         if (flock(fdisk_get_devfd(c), arg_dry_run ? LOCK_SH : LOCK_EX) < 0)
@@ -1230,6 +1294,7 @@ static int context_load_partition_table(Context *context, const char *node) {
                 break;
 
         case EMPTY_FORCE:
+        case EMPTY_CREATE:
                 /* Always reinitiaize the disk, don't consider what there was on the disk before */
                 from_scratch = true;
                 break;
@@ -1602,7 +1667,7 @@ static int context_dump_partitions(Context *context, const char *node) {
                                 TABLE_UINT64, p->new_padding,
                                 TABLE_STRING, padding_change, TABLE_SET_COLOR, !p->partitions_next && sum_padding > 0 ? ansi_underline() : NULL);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to add row to table: %m");
+                        return table_log_add_error(r);
         }
 
         if (sum_padding > 0 || sum_size > 0) {
@@ -1625,7 +1690,7 @@ static int context_dump_partitions(Context *context, const char *node) {
                                 TABLE_EMPTY,
                                 TABLE_STRING, b);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to add row to table: %m");
+                        return table_log_add_error(r);
         }
 
         r = table_print(t, stdout);
@@ -1905,8 +1970,7 @@ static int context_discard_range(Context *context, uint64_t offset, uint64_t siz
         if (size <= 0)
                 return 0;
 
-        fd = fdisk_get_devfd(context->fdisk_context);
-        assert(fd >= 0);
+        assert_se((fd = fdisk_get_devfd(context->fdisk_context)) >= 0);
 
         if (fstat(fd, &st) < 0)
                 return -errno;
@@ -2077,6 +2141,48 @@ static int context_wipe_and_discard(Context *context, bool from_scratch) {
         return 0;
 }
 
+static int context_copy_blocks(Context *context) {
+        Partition *p;
+        int fd = -1, r;
+
+        assert(context);
+
+        /* Copy in file systems on the block level */
+
+        LIST_FOREACH(partitions, p, context->partitions) {
+                char buf[FORMAT_BYTES_MAX];
+
+                if (p->copy_blocks_fd < 0)
+                        continue;
+
+                if (p->dropped)
+                        continue;
+
+                if (PARTITION_EXISTS(p)) /* Never copy over existing partitions */
+                        continue;
+
+                assert(p->new_size != UINT64_MAX);
+                assert(p->copy_blocks_size != UINT64_MAX);
+                assert(p->new_size >= p->copy_blocks_size);
+
+                if (fd < 0)
+                        assert_se((fd = fdisk_get_devfd(context->fdisk_context)) >= 0);
+
+                if (lseek(fd, p->offset, SEEK_SET) == (off_t) -1)
+                        return log_error_errno(errno, "Failed to seek to partition offset: %m");
+
+                log_info("Copying in '%s' (%s) on block level into partition %" PRIu64 ".", p->copy_blocks_path, format_bytes(buf, sizeof(buf), p->copy_blocks_size), p->partno);
+
+                r = copy_bytes_full(p->copy_blocks_fd, fd, p->copy_blocks_size, 0, NULL, NULL, NULL, NULL);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to copy in data from '%s': %m", p->copy_blocks_path);
+
+                log_info("Copying in of '%s' on block level completed.", p->copy_blocks_path);
+        }
+
+        return 0;
+}
+
 static int partition_acquire_uuid(Context *context, Partition *p, sd_id128_t *ret) {
         struct {
                 sd_id128_t type_uuid;
@@ -2214,13 +2320,12 @@ static int context_acquire_partition_uuids_and_labels(Context *context) {
         assert(context);
 
         LIST_FOREACH(partitions, p, context->partitions) {
-                assert(sd_id128_is_null(p->new_uuid));
-
                 /* Never touch foreign partitions */
                 if (PARTITION_IS_FOREIGN(p)) {
                         p->new_uuid = p->current_uuid;
 
                         if (p->current_label) {
+                                free(p->new_label);
                                 p->new_label = strdup(p->current_label);
                                 if (!p->new_label)
                                         return log_oom();
@@ -2231,20 +2336,21 @@ static int context_acquire_partition_uuids_and_labels(Context *context) {
 
                 if (!sd_id128_is_null(p->current_uuid))
                         p->new_uuid = p->current_uuid; /* Never change initialized UUIDs */
-                else {
+                else if (sd_id128_is_null(p->new_uuid)) {
+                        /* Not explicitly set by user! */
                         r = partition_acquire_uuid(context, p, &p->new_uuid);
                         if (r < 0)
                                 return r;
                 }
 
-                if (p->new_label) /* Explicitly set by user? */
-                        continue;
-
                 if (!isempty(p->current_label)) {
+                        free(p->new_label);
                         p->new_label = strdup(p->current_label); /* never change initialized labels */
                         if (!p->new_label)
                                 return log_oom();
-                } else {
+                } else if (!p->new_label) {
+                        /* Not explicitly set by user! */
+
                         r = partition_acquire_label(context, p, &p->new_label);
                         if (r < 0)
                                 return r;
@@ -2339,6 +2445,10 @@ static int context_write_partition_table(
         if (r < 0)
                 return r;
 
+        r = context_copy_blocks(context);
+        if (r < 0)
+                return r;
+
         LIST_FOREACH(partitions, p, context->partitions) {
                 if (p->dropped)
                         continue;
@@ -2584,6 +2694,87 @@ static int context_can_factory_reset(Context *context) {
         return false;
 }
 
+static int context_open_copy_block_paths(Context *context) {
+        Partition *p;
+        int r;
+
+        assert(context);
+
+        LIST_FOREACH(partitions, p, context->partitions) {
+                _cleanup_close_ int source_fd = -1;
+                uint64_t size;
+                struct stat st;
+
+                assert(p->copy_blocks_fd < 0);
+                assert(p->copy_blocks_size == UINT64_MAX);
+
+                if (PARTITION_EXISTS(p)) /* Never copy over partitions that already exist! */
+                        continue;
+
+                if (!p->copy_blocks_path)
+                        continue;
+
+                source_fd = open(p->copy_blocks_path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
+                if (source_fd < 0)
+                        return log_error_errno(errno, "Failed to open block copy file '%s': %m", p->copy_blocks_path);
+
+                if (fstat(source_fd, &st) < 0)
+                        return log_error_errno(errno, "Failed to stat block copy file '%s': %m", p->copy_blocks_path);
+
+                if (S_ISDIR(st.st_mode)) {
+                        _cleanup_free_ char *bdev = NULL;
+
+                        /* If the file is a directory, automatically find the backing block device */
+
+                        if (major(st.st_dev) != 0)
+                                r = device_path_make_major_minor(S_IFBLK, st.st_dev, &bdev);
+                        else {
+                                dev_t devt;
+
+                                /* Special support for btrfs */
+
+                                r = btrfs_get_block_device_fd(source_fd, &devt);
+                                if (r < 0)
+                                        return log_error_errno(r, "Unable to determine backing block device of '%s': %m", p->copy_blocks_path);
+
+                                r = device_path_make_major_minor(S_IFBLK, devt, &bdev);
+                        }
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to determine block device path for block device backing '%s': %m", p->copy_blocks_path);
+
+                        safe_close(source_fd);
+
+                        source_fd = open(bdev, O_RDONLY|O_CLOEXEC|O_NOCTTY);
+                        if (source_fd < 0)
+                                return log_error_errno(errno, "Failed to open block device '%s': %m", bdev);
+
+                        if (fstat(source_fd, &st) < 0)
+                                return log_error_errno(errno, "Failed to stat block device '%s': %m", bdev);
+
+                        if (!S_ISBLK(st.st_mode))
+                                return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "Block device '%s' is not actually a block device, refusing.", bdev);
+                }
+
+                if (S_ISREG(st.st_mode))
+                        size = st.st_size;
+                else if (S_ISBLK(st.st_mode)) {
+                        if (ioctl(source_fd, BLKGETSIZE64, &size) != 0)
+                                return log_error_errno(errno, "Failed to determine size of block device to copy from: %m");
+                } else
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specified path to copy blocks from '%s' is not a regular file, block device or directory, refusing: %m", p->copy_blocks_path);
+
+                if (size <= 0)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File to copy bytes from '%s' has zero size, refusing.", p->copy_blocks_path);
+                if (size % 512 != 0)
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File to copy bytes from '%s' has size that is not multiple of 512, refusing.", p->copy_blocks_path);
+
+                p->copy_blocks_fd = TAKE_FD(source_fd);
+                p->copy_blocks_size = size;
+        }
+
+        return 0;
+}
+
 static int help(void) {
         _cleanup_free_ char *link = NULL;
         int r;
@@ -2597,8 +2788,8 @@ static int help(void) {
                "  -h --help               Show this help\n"
                "     --version            Show package version\n"
                "     --dry-run=BOOL       Whether to run dry-run operation\n"
-               "     --empty=MODE         One of refuse, allow, require, force; controls how to\n"
-               "                          handle empty disks lacking partition table\n"
+               "     --empty=MODE         One of refuse, allow, require, force, create; controls\n"
+               "                          how to handle empty disks lacking partition tables\n"
                "     --discard=BOOL       Whether to discard backing blocks for new partitions\n"
                "     --pretty=BOOL        Whether to show pretty summary before executing operation\n"
                "     --factory-reset=BOOL Whether to remove data partitions before recreating\n"
@@ -2607,6 +2798,7 @@ static int help(void) {
                "     --root=PATH          Operate relative to root path\n"
                "     --definitions=DIR    Find partitions in specified directory\n"
                "     --seed=UUID          128bit seed UUID to derive all UUIDs from\n"
+               "     --size=BYTES         Grow loopback file to specified size\n"
                "\nSee the %s for details.\n"
                , program_invocation_short_name
                , ansi_highlight(), ansi_normal()
@@ -2629,6 +2821,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_SEED,
                 ARG_PRETTY,
                 ARG_DEFINITIONS,
+                ARG_SIZE,
         };
 
         static const struct option options[] = {
@@ -2643,10 +2836,11 @@ static int parse_argv(int argc, char *argv[]) {
                 { "seed",              required_argument, NULL, ARG_SEED              },
                 { "pretty",            required_argument, NULL, ARG_PRETTY            },
                 { "definitions",       required_argument, NULL, ARG_DEFINITIONS       },
+                { "size",              required_argument, NULL, ARG_SIZE              },
                 {}
         };
 
-        int c, r;
+        int c, r, dry_run = -1;
 
         assert(argc >= 0);
         assert(argv);
@@ -2666,7 +2860,7 @@ static int parse_argv(int argc, char *argv[]) {
                         if (r < 0)
                                 return log_error_errno(r, "Failed to parse --dry-run= parameter: %s", optarg);
 
-                        arg_dry_run = r;
+                        dry_run = r;
                         break;
 
                 case ARG_EMPTY:
@@ -2678,7 +2872,14 @@ static int parse_argv(int argc, char *argv[]) {
                                 arg_empty = EMPTY_REQUIRE;
                         else if (streq(optarg, "force"))
                                 arg_empty = EMPTY_FORCE;
-                        else
+                        else if (streq(optarg, "create")) {
+                                arg_empty = EMPTY_CREATE;
+
+                                if (dry_run < 0)
+                                        dry_run = false; /* Imply --dry-run=no if we create the loopback file
+                                                          * anew. After all we cannot really break anyone's
+                                                          * partition tables that way. */
+                        } else
                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                                        "Failed to parse --empty= parameter: %s", optarg);
                         break;
@@ -2739,6 +2940,27 @@ static int parse_argv(int argc, char *argv[]) {
                                 return r;
                         break;
 
+                case ARG_SIZE: {
+                        uint64_t parsed, rounded;
+
+                        r = parse_size(optarg, 1024, &parsed);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse --size= parameter: %s", optarg);
+
+                        rounded = round_up_size(parsed, 4096);
+                        if (rounded == 0)
+                                return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Specified image size too small, refusing.");
+                        if (rounded == UINT64_MAX)
+                                return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Specified image size too large, refusing.");
+
+                        if (rounded != parsed)
+                                log_warning("Specified size is not a multiple of 4096, rounding up automatically. (%" PRIu64 " → %" PRIu64 ")",
+                                            parsed, rounded);
+
+                        arg_size = rounded;
+                        break;
+                }
+
                 case '?':
                         return -EINVAL;
 
@@ -2750,14 +2972,27 @@ static int parse_argv(int argc, char *argv[]) {
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                        "Expected at most one argument, the path to the block device.");
 
-        if (arg_factory_reset > 0 && IN_SET(arg_empty, EMPTY_FORCE, EMPTY_REQUIRE))
+        if (arg_factory_reset > 0 && IN_SET(arg_empty, EMPTY_FORCE, EMPTY_REQUIRE, EMPTY_CREATE))
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "Combination of --factory-reset=yes and --empty=force/--empty=require is invalid.");
+                                       "Combination of --factory-reset=yes and --empty=force/--empty=require/--empty=create is invalid.");
 
         if (arg_can_factory_reset)
-                arg_dry_run = true;
+                arg_dry_run = true; /* When --can-factory-reset is specified we don't make changes, hence
+                                     * non-dry-run mode makes no sense. Thus, imply dry run mode so that we
+                                     * open things strictly read-only. */
+        else if (dry_run >= 0)
+                arg_dry_run = dry_run;
+
+        if (arg_empty == EMPTY_CREATE && arg_size == UINT64_MAX)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "If --empty=create is specified, --size= must be specified, too.");
 
         arg_node = argc > optind ? argv[optind] : NULL;
+
+        if (IN_SET(arg_empty, EMPTY_FORCE, EMPTY_REQUIRE, EMPTY_CREATE) && !arg_node)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "A path to a device node or loopback file must be specified when --empty=force, --empty=require or --empty=create are used.");
+
         return 1;
 }
 
@@ -2824,12 +3059,16 @@ static int remove_efi_variable_factory_reset(void) {
         return 0;
 }
 
-static int acquire_root_devno(const char *p, int mode, char **ret) {
+static int acquire_root_devno(const char *p, int mode, char **ret, int *ret_fd) {
         _cleanup_close_ int fd = -1;
         struct stat st;
-        dev_t devno;
+        dev_t devno, fd_devno = (mode_t) -1;
         int r;
 
+        assert(p);
+        assert(ret);
+        assert(ret_fd);
+
         fd = open(p, mode);
         if (fd < 0)
                 return -errno;
@@ -2845,23 +3084,23 @@ static int acquire_root_devno(const char *p, int mode, char **ret) {
                         return log_oom();
 
                 *ret = s;
+                *ret_fd = TAKE_FD(fd);
+
                 return 0;
         }
 
         if (S_ISBLK(st.st_mode))
-                devno = st.st_rdev;
+                fd_devno = devno = st.st_rdev;
         else if (S_ISDIR(st.st_mode)) {
 
                 devno = st.st_dev;
-
-                if (major(st.st_dev) == 0) {
+                if (major(devno) == 0) {
                         r = btrfs_get_block_device_fd(fd, &devno);
                         if (r == -ENOTTY) /* not btrfs */
                                 return -ENODEV;
                         if (r < 0)
                                 return r;
                 }
-
         } else
                 return -ENOTBLK;
 
@@ -2873,23 +3112,52 @@ static int acquire_root_devno(const char *p, int mode, char **ret) {
         /* From partition to whole disk containing it */
         r = block_get_whole_disk(devno, &devno);
         if (r < 0)
-                log_debug_errno(r, "Failed to find whole disk block device for '%s', ingoring: %m", p);
+                log_debug_errno(r, "Failed to find whole disk block device for '%s', ignoring: %m", p);
+
+        r = device_path_make_canonical(S_IFBLK, devno, ret);
+        if (r < 0)
+                return log_debug_errno(r, "Failed to determine canonical path for '%s': %m", p);
 
-        return device_path_make_canonical(S_IFBLK, devno, ret);
+        /* Only if we still lock at the same block device we can reuse the fd. Otherwise return an
+         * invalidated fd. */
+        *ret_fd = fd_devno != (mode_t) -1 && fd_devno == devno ? TAKE_FD(fd) : -1;
+        return 0;
 }
 
-static int find_root(char **ret) {
+static int find_root(char **ret, int *ret_fd) {
         const char *t;
         int r;
 
+        assert(ret);
+        assert(ret_fd);
+
         if (arg_node) {
-                r = acquire_root_devno(arg_node, O_RDONLY|O_CLOEXEC, ret);
+                if (arg_empty == EMPTY_CREATE) {
+                        _cleanup_close_ int fd = -1;
+                        _cleanup_free_ char *s = NULL;
+
+                        s = strdup(arg_node);
+                        if (!s)
+                                return log_oom();
+
+                        fd = open(arg_node, O_RDONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOFOLLOW, 0777);
+                        if (fd < 0)
+                                return log_error_errno(errno, "Failed to create '%s': %m", arg_node);
+
+                        *ret = TAKE_PTR(s);
+                        *ret_fd = TAKE_FD(fd);
+                        return 0;
+                }
+
+                r = acquire_root_devno(arg_node, O_RDONLY|O_CLOEXEC, ret, ret_fd);
                 if (r < 0)
                         return log_error_errno(r, "Failed to determine backing device of %s: %m", arg_node);
 
                 return 0;
         }
 
+        assert(IN_SET(arg_empty, EMPTY_REFUSE, EMPTY_ALLOW));
+
         /* Let's search for the root device. We look for two cases here: first in /, and then in /usr. The
          * latter we check for cases where / is a tmpfs and only /usr is an actual persistent block device
          * (think: volatile setups) */
@@ -2907,7 +3175,7 @@ static int find_root(char **ret) {
                 } else
                         p = t;
 
-                r = acquire_root_devno(p, O_RDONLY|O_DIRECTORY|O_CLOEXEC, ret);
+                r = acquire_root_devno(p, O_RDONLY|O_DIRECTORY|O_CLOEXEC, ret, ret_fd);
                 if (r < 0) {
                         if (r != -ENODEV)
                                 return log_error_errno(r, "Failed to determine backing device of %s: %m", p);
@@ -2918,9 +3186,83 @@ static int find_root(char **ret) {
         return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "Failed to discover root block device.");
 }
 
+static int resize_backing_fd(const char *node, int *fd) {
+        char buf1[FORMAT_BYTES_MAX], buf2[FORMAT_BYTES_MAX];
+        _cleanup_close_ int writable_fd = -1;
+        struct stat st;
+        int r;
+
+        assert(node);
+        assert(fd);
+
+        if (arg_size == UINT64_MAX) /* Nothing to do */
+                return 0;
+
+        if (*fd < 0) {
+                /* Open the file if we haven't opened it yet. Note that we open it read-only here, just to
+                 * keep a reference to the file we can pass around. */
+                *fd = open(node, O_RDONLY|O_CLOEXEC);
+                if (*fd < 0)
+                        return log_error_errno(errno, "Failed to open '%s' in order to adjust size: %m", node);
+        }
+
+        if (fstat(*fd, &st) < 0)
+                return log_error_errno(errno, "Failed to stat '%s': %m", node);
+
+        r = stat_verify_regular(&st);
+        if (r < 0)
+                return log_error_errno(r, "Specified path '%s' is not a regular file, cannot resize: %m", node);
+
+        assert_se(format_bytes(buf1, sizeof(buf1), st.st_size));
+        assert_se(format_bytes(buf2, sizeof(buf2), arg_size));
+
+        if ((uint64_t) st.st_size >= arg_size) {
+                log_info("File '%s' already is of requested size or larger, not growing. (%s >= %s)", node, buf1, buf2);
+                return 0;
+        }
+
+        /* The file descriptor is read-only. In order to grow the file we need to have a writable fd. We
+         * reopen the file for that temporarily. We keep the writable fd only open for this operation though,
+         * as fdisk can't accept it anyway. */
+
+        writable_fd = fd_reopen(*fd, O_WRONLY|O_CLOEXEC);
+        if (writable_fd < 0)
+                return log_error_errno(writable_fd, "Failed to reopen backing file '%s' writable: %m", node);
+
+        if (!arg_discard) {
+                if (fallocate(writable_fd, 0, 0, arg_size) < 0) {
+                        if (!ERRNO_IS_NOT_SUPPORTED(errno))
+                                return log_error_errno(errno, "Failed to grow '%s' from %s to %s by allocation: %m",
+                                                       node, buf1, buf2);
+
+                        /* Fallback to truncation, if fallocate() is not supported. */
+                        log_debug("Backing file system does not support fallocate(), falling back to ftruncate().");
+                } else {
+                        if (st.st_size == 0) /* Likely regular file just created by us */
+                                log_info("Allocated %s for '%s'.", buf2, node);
+                        else
+                                log_info("File '%s' grown from %s to %s by allocation.", node, buf1, buf2);
+
+                        return 1;
+                }
+        }
+
+        if (ftruncate(writable_fd, arg_size) < 0)
+                return log_error_errno(errno, "Failed to grow '%s' from %s to %s by truncation: %m",
+                                       node, buf1, buf2);
+
+        if (st.st_size == 0) /* Likely regular file just created by us */
+                log_info("Sized '%s' to %s.", node, buf2);
+        else
+                log_info("File '%s' grown from %s to %s by truncation.", node, buf1, buf2);
+
+        return 1;
+}
+
 static int run(int argc, char *argv[]) {
         _cleanup_(context_freep) Context* context = NULL;
         _cleanup_free_ char *node = NULL;
+        _cleanup_close_ int backing_fd = -1;
         bool from_scratch;
         int r;
 
@@ -2955,16 +3297,22 @@ static int run(int argc, char *argv[]) {
         if (r < 0)
                 return r;
 
-        if (context->n_partitions <= 0 && arg_empty != EMPTY_FORCE) {
+        if (context->n_partitions <= 0 && arg_empty == EMPTY_REFUSE) {
                 log_info("Didn't find any partition definition files, nothing to do.");
                 return 0;
         }
 
-        r = find_root(&node);
+        r = find_root(&node, &backing_fd);
         if (r < 0)
                 return r;
 
-        r = context_load_partition_table(context, node);
+        if (arg_size != UINT64_MAX) {
+                r = resize_backing_fd(node, &backing_fd);
+                if (r < 0)
+                        return r;
+        }
+
+        r = context_load_partition_table(context, node, &backing_fd);
         if (r == -EHWPOISON)
                 return 77; /* Special return value which means "Not GPT, so not doing anything". This isn't
                             * really an error when called at boot. */
@@ -2993,7 +3341,7 @@ static int run(int argc, char *argv[]) {
 
                 /* Reload the reduced partition table */
                 context_unload_partition_table(context);
-                r = context_load_partition_table(context, node);
+                r = context_load_partition_table(context, node, &backing_fd);
                 if (r < 0)
                         return r;
         }
@@ -3007,6 +3355,11 @@ static int run(int argc, char *argv[]) {
         if (r < 0)
                 return r;
 
+        /* Open all files to copy blocks from now, since we want to take their size into consideration */
+        r = context_open_copy_block_paths(context);
+        if (r < 0)
+                return r;
+
         /* First try to fit new partitions in, dropping by priority until it fits */
         for (;;) {
                 if (context_allocate_partitions(context))
diff --git a/src/partition/test-repart.sh b/src/partition/test-repart.sh
new file mode 100755 (executable)
index 0000000..bfb9bcb
--- /dev/null
@@ -0,0 +1,145 @@
+#!/usr/bin/env bash
+set -ex
+
+repart=$1
+test -x $repart
+
+D=$(mktemp --directory)
+trap "rm -rf '$D'" EXIT INT QUIT PIPE
+mkdir -p $D/definitions
+
+SEED=e2a40bf9-73f1-4278-9160-49c031e7aef8
+
+$repart $D/zzz --empty=create --size=1G --seed=$SEED
+
+sfdisk -d $D/zzz | grep -v -e 'sector-size' -e '^$' >$D/empty
+
+cmp $D/empty - <<EOF
+label: gpt
+label-id: EF7F7EE2-47B3-4251-B1A1-09EA8BF12D5D
+device: $D/zzz
+unit: sectors
+first-lba: 2048
+last-lba: 2097118
+EOF
+
+cat >$D/definitions/root.conf <<EOF
+[Partition]
+Type=root-x86-64
+EOF
+
+ln -s root.conf $D/definitions/root2.conf
+
+cat >$D/definitions/home.conf <<EOF
+[Partition]
+Type=home
+Label=home-first
+Label=home-always-too-long-xxxxxxxxxxxxxx-%v
+EOF
+
+cat >$D/definitions/swap.conf <<EOF
+[Partition]
+Type=swap
+SizeMaxBytes=64M
+PaddingMinBytes=92M
+EOF
+
+$repart $D/zzz --dry-run=no --seed=$SEED --definitions=$D/definitions
+
+sfdisk -d $D/zzz | grep -v -e 'sector-size' -e '^$' >$D/populated
+
+cmp $D/populated - <<EOF
+label: gpt
+label-id: EF7F7EE2-47B3-4251-B1A1-09EA8BF12D5D
+device: $D/zzz
+unit: sectors
+first-lba: 2048
+last-lba: 2097118
+$D/zzz1 : start=        2048, size=      591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=A6005774-F558-4330-A8E5-D6D2C01C01D6, name="home-first"
+$D/zzz2 : start=      593904, size=      591856, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=CE9C76EB-A8F1-40FF-813C-11DCA6C0A55B, name="root-x86-64"
+$D/zzz3 : start=     1185760, size=      591864, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=AC60A837-550C-43BD-B5C4-9CB73B884E79, name="root-x86-64-2"
+$D/zzz4 : start=     1777624, size=      131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=2AA78CDB-59C7-4173-AF11-C7453737A5D1, name="swap"
+EOF
+
+cat >$D/definitions/swap.conf <<EOF
+[Partition]
+Type=swap
+SizeMaxBytes=64M
+EOF
+
+cat >$D/definitions/extra.conf <<EOF
+[Partition]
+Type=linux-generic
+Label=custom_label
+UUID=a0a1a2a3a4a5a6a7a8a9aaabacadaeaf
+EOF
+
+echo "Label=ignored_label" >>$D/definitions/home.conf
+echo "UUID=b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" >>$D/definitions/home.conf
+
+$repart $D/zzz --dry-run=no --seed=$SEED --definitions=$D/definitions
+
+sfdisk -d $D/zzz | grep -v -e 'sector-size' -e '^$' >$D/populated2
+
+cmp $D/populated2 - <<EOF
+label: gpt
+label-id: EF7F7EE2-47B3-4251-B1A1-09EA8BF12D5D
+device: $D/zzz
+unit: sectors
+first-lba: 2048
+last-lba: 2097118
+$D/zzz1 : start=        2048, size=      591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=A6005774-F558-4330-A8E5-D6D2C01C01D6, name="home-first"
+$D/zzz2 : start=      593904, size=      591856, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=CE9C76EB-A8F1-40FF-813C-11DCA6C0A55B, name="root-x86-64"
+$D/zzz3 : start=     1185760, size=      591864, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=AC60A837-550C-43BD-B5C4-9CB73B884E79, name="root-x86-64-2"
+$D/zzz4 : start=     1777624, size=      131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=2AA78CDB-59C7-4173-AF11-C7453737A5D1, name="swap"
+$D/zzz5 : start=     1908696, size=      188416, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name="custom_label"
+EOF
+
+$repart $D/zzz --size=2G --dry-run=no --seed=$SEED --definitions=$D/definitions
+
+sfdisk -d $D/zzz | grep -v -e 'sector-size' -e '^$' >$D/populated3
+
+cmp $D/populated3 - <<EOF
+label: gpt
+label-id: EF7F7EE2-47B3-4251-B1A1-09EA8BF12D5D
+device: $D/zzz
+unit: sectors
+first-lba: 2048
+last-lba: 4194270
+$D/zzz1 : start=        2048, size=      591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=A6005774-F558-4330-A8E5-D6D2C01C01D6, name="home-first"
+$D/zzz2 : start=      593904, size=      591856, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=CE9C76EB-A8F1-40FF-813C-11DCA6C0A55B, name="root-x86-64"
+$D/zzz3 : start=     1185760, size=      591864, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=AC60A837-550C-43BD-B5C4-9CB73B884E79, name="root-x86-64-2"
+$D/zzz4 : start=     1777624, size=      131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=2AA78CDB-59C7-4173-AF11-C7453737A5D1, name="swap"
+$D/zzz5 : start=     1908696, size=     2285568, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name="custom_label"
+EOF
+
+dd if=/dev/urandom of=$D/block-copy bs=4096 count=10240
+
+cat >$D/definitions/extra2.conf <<EOF
+[Partition]
+Type=linux-generic
+Label=block-copy
+UUID=2a1d97e1d0a346cca26eadc643926617
+CopyBlocks=$D/block-copy
+EOF
+
+$repart $D/zzz --size=3G --dry-run=no --seed=$SEED --definitions=$D/definitions
+
+sfdisk -d $D/zzz | grep -v -e 'sector-size' -e '^$' >$D/populated4
+
+cmp $D/populated4 - <<EOF
+label: gpt
+label-id: EF7F7EE2-47B3-4251-B1A1-09EA8BF12D5D
+device: $D/zzz
+unit: sectors
+first-lba: 2048
+last-lba: 6291422
+$D/zzz1 : start=        2048, size=      591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=A6005774-F558-4330-A8E5-D6D2C01C01D6, name="home-first"
+$D/zzz2 : start=      593904, size=      591856, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=CE9C76EB-A8F1-40FF-813C-11DCA6C0A55B, name="root-x86-64"
+$D/zzz3 : start=     1185760, size=      591864, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=AC60A837-550C-43BD-B5C4-9CB73B884E79, name="root-x86-64-2"
+$D/zzz4 : start=     1777624, size=      131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=2AA78CDB-59C7-4173-AF11-C7453737A5D1, name="swap"
+$D/zzz5 : start=     1908696, size=     2285568, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=A0A1A2A3-A4A5-A6A7-A8A9-AAABACADAEAF, name="custom_label"
+$D/zzz6 : start=     4194264, size=     2097152, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=2A1D97E1-D0A3-46CC-A26E-ADC643926617, name="block-copy"
+EOF
+
+cmp --bytes=41943040 --ignore-initial=0:$((512*4194264)) $D/block-copy $D/zzz
index 568b5fb04bee61c81e3810c7ab78137ca42d9dae..b1c5442d009b23692685bda1458c536a15848ae1 100644 (file)
 static const char *arg_suffix = NULL;
 
 static const char* const path_table[_SD_PATH_MAX] = {
-        [SD_PATH_TEMPORARY] = "temporary",
-        [SD_PATH_TEMPORARY_LARGE] = "temporary-large",
-        [SD_PATH_SYSTEM_BINARIES] = "system-binaries",
-        [SD_PATH_SYSTEM_INCLUDE] = "system-include",
-        [SD_PATH_SYSTEM_LIBRARY_PRIVATE] = "system-library-private",
-        [SD_PATH_SYSTEM_LIBRARY_ARCH] = "system-library-arch",
-        [SD_PATH_SYSTEM_SHARED] = "system-shared",
-        [SD_PATH_SYSTEM_CONFIGURATION_FACTORY] = "system-configuration-factory",
-        [SD_PATH_SYSTEM_STATE_FACTORY] = "system-state-factory",
-        [SD_PATH_SYSTEM_CONFIGURATION] = "system-configuration",
-        [SD_PATH_SYSTEM_RUNTIME] = "system-runtime",
-        [SD_PATH_SYSTEM_RUNTIME_LOGS] = "system-runtime-logs",
-        [SD_PATH_SYSTEM_STATE_PRIVATE] = "system-state-private",
-        [SD_PATH_SYSTEM_STATE_LOGS] = "system-state-logs",
-        [SD_PATH_SYSTEM_STATE_CACHE] = "system-state-cache",
-        [SD_PATH_SYSTEM_STATE_SPOOL] = "system-state-spool",
-        [SD_PATH_USER_BINARIES] = "user-binaries",
-        [SD_PATH_USER_LIBRARY_PRIVATE] = "user-library-private",
-        [SD_PATH_USER_LIBRARY_ARCH] = "user-library-arch",
-        [SD_PATH_USER_SHARED] = "user-shared",
-        [SD_PATH_USER_CONFIGURATION] = "user-configuration",
-        [SD_PATH_USER_RUNTIME] = "user-runtime",
-        [SD_PATH_USER_STATE_CACHE] = "user-state-cache",
-        [SD_PATH_USER] = "user",
-        [SD_PATH_USER_DOCUMENTS] = "user-documents",
-        [SD_PATH_USER_MUSIC] = "user-music",
-        [SD_PATH_USER_PICTURES] = "user-pictures",
-        [SD_PATH_USER_VIDEOS] = "user-videos",
-        [SD_PATH_USER_DOWNLOAD] = "user-download",
-        [SD_PATH_USER_PUBLIC] = "user-public",
-        [SD_PATH_USER_TEMPLATES] = "user-templates",
-        [SD_PATH_USER_DESKTOP] = "user-desktop",
-        [SD_PATH_SEARCH_BINARIES] = "search-binaries",
-        [SD_PATH_SEARCH_BINARIES_DEFAULT] = "search-binaries-default",
-        [SD_PATH_SEARCH_LIBRARY_PRIVATE] = "search-library-private",
-        [SD_PATH_SEARCH_LIBRARY_ARCH] = "search-library-arch",
-        [SD_PATH_SEARCH_SHARED] = "search-shared",
-        [SD_PATH_SEARCH_CONFIGURATION_FACTORY] = "search-configuration-factory",
-        [SD_PATH_SEARCH_STATE_FACTORY] = "search-state-factory",
-        [SD_PATH_SEARCH_CONFIGURATION] = "search-configuration",
+        [SD_PATH_TEMPORARY]                       = "temporary",
+        [SD_PATH_TEMPORARY_LARGE]                 = "temporary-large",
+        [SD_PATH_SYSTEM_BINARIES]                 = "system-binaries",
+        [SD_PATH_SYSTEM_INCLUDE]                  = "system-include",
+        [SD_PATH_SYSTEM_LIBRARY_PRIVATE]          = "system-library-private",
+        [SD_PATH_SYSTEM_LIBRARY_ARCH]             = "system-library-arch",
+        [SD_PATH_SYSTEM_SHARED]                   = "system-shared",
+        [SD_PATH_SYSTEM_CONFIGURATION_FACTORY]    = "system-configuration-factory",
+        [SD_PATH_SYSTEM_STATE_FACTORY]            = "system-state-factory",
+        [SD_PATH_SYSTEM_CONFIGURATION]            = "system-configuration",
+        [SD_PATH_SYSTEM_RUNTIME]                  = "system-runtime",
+        [SD_PATH_SYSTEM_RUNTIME_LOGS]             = "system-runtime-logs",
+        [SD_PATH_SYSTEM_STATE_PRIVATE]            = "system-state-private",
+        [SD_PATH_SYSTEM_STATE_LOGS]               = "system-state-logs",
+        [SD_PATH_SYSTEM_STATE_CACHE]              = "system-state-cache",
+        [SD_PATH_SYSTEM_STATE_SPOOL]              = "system-state-spool",
+        [SD_PATH_USER_BINARIES]                   = "user-binaries",
+        [SD_PATH_USER_LIBRARY_PRIVATE]            = "user-library-private",
+        [SD_PATH_USER_LIBRARY_ARCH]               = "user-library-arch",
+        [SD_PATH_USER_SHARED]                     = "user-shared",
+        [SD_PATH_USER_CONFIGURATION]              = "user-configuration",
+        [SD_PATH_USER_RUNTIME]                    = "user-runtime",
+        [SD_PATH_USER_STATE_CACHE]                = "user-state-cache",
+        [SD_PATH_USER]                            = "user",
+        [SD_PATH_USER_DOCUMENTS]                  = "user-documents",
+        [SD_PATH_USER_MUSIC]                      = "user-music",
+        [SD_PATH_USER_PICTURES]                   = "user-pictures",
+        [SD_PATH_USER_VIDEOS]                     = "user-videos",
+        [SD_PATH_USER_DOWNLOAD]                   = "user-download",
+        [SD_PATH_USER_PUBLIC]                     = "user-public",
+        [SD_PATH_USER_TEMPLATES]                  = "user-templates",
+        [SD_PATH_USER_DESKTOP]                    = "user-desktop",
+        [SD_PATH_SEARCH_BINARIES]                 = "search-binaries",
+        [SD_PATH_SEARCH_BINARIES_DEFAULT]         = "search-binaries-default",
+        [SD_PATH_SEARCH_LIBRARY_PRIVATE]          = "search-library-private",
+        [SD_PATH_SEARCH_LIBRARY_ARCH]             = "search-library-arch",
+        [SD_PATH_SEARCH_SHARED]                   = "search-shared",
+        [SD_PATH_SEARCH_CONFIGURATION_FACTORY]    = "search-configuration-factory",
+        [SD_PATH_SEARCH_STATE_FACTORY]            = "search-state-factory",
+        [SD_PATH_SEARCH_CONFIGURATION]            = "search-configuration",
+
+        [SD_PATH_SYSTEMD_UTIL]                    = "systemd-util",
+        [SD_PATH_SYSTEMD_SYSTEM_UNIT]             = "systemd-system-unit",
+        [SD_PATH_SYSTEMD_SYSTEM_PRESET]           = "systemd-system-preset",
+        [SD_PATH_SYSTEMD_SYSTEM_CONF]             = "systemd-system-conf",
+        [SD_PATH_SYSTEMD_SEARCH_SYSTEM_UNIT]      = "systemd-system-unit",
+        [SD_PATH_SYSTEMD_SYSTEM_GENERATOR]        = "systemd-system-generator",
+        [SD_PATH_SYSTEMD_SEARCH_SYSTEM_GENERATOR] = "systemd-system-generator",
+        [SD_PATH_SYSTEMD_USER_UNIT]               = "systemd-user-unit",
+        [SD_PATH_SYSTEMD_USER_PRESET]             = "systemd-user-preset",
+        [SD_PATH_SYSTEMD_USER_CONF]               = "systemd-user-conf",
+        [SD_PATH_SYSTEMD_SEARCH_USER_UNIT]        = "systemd-user-unit",
+        [SD_PATH_SYSTEMD_SEARCH_USER_GENERATOR]   = "systemd-user-generator",
+        [SD_PATH_SYSTEMD_USER_GENERATOR]          = "systemd-user-generator",
+        [SD_PATH_SYSTEMD_SLEEP]                   = "systemd-sleep",
+        [SD_PATH_SYSTEMD_SHUTDOWN]                = "systemd-shutdown",
+
+        [SD_PATH_TMPFILES]                        = "tmpfiles",
+        [SD_PATH_SYSUSERS]                        = "sysusers",
+        [SD_PATH_SYSCTL]                          = "sysctl",
+        [SD_PATH_BINFMT]                          = "binfmt",
+        [SD_PATH_MODULES_LOAD]                    = "modules-load",
+        [SD_PATH_CATALOG]                         = "catalog",
+
+        [SD_PATH_SYSTEMD_SEARCH_NETWORK]          = "systemd-search-network",
 };
 
 static int list_homes(void) {
@@ -68,12 +93,12 @@ static int list_homes(void) {
                 _cleanup_free_ char *p = NULL;
                 int q;
 
-                q = sd_path_home(i, arg_suffix, &p);
-                if (q == -ENXIO)
-                        continue;
+                q = sd_path_lookup(i, arg_suffix, &p);
                 if (q < 0) {
-                        log_error_errno(r, "Failed to query %s: %m", path_table[i]);
-                        r = q;
+                        log_full_errno(q == -ENXIO ? LOG_DEBUG : LOG_ERR,
+                                       q, "Failed to query %s: %m", path_table[i]);
+                        if (q != -ENXIO)
+                                r = q;
                         continue;
                 }
 
@@ -91,7 +116,7 @@ static int print_home(const char *n) {
                 if (streq(path_table[i], n)) {
                         _cleanup_free_ char *p = NULL;
 
-                        r = sd_path_home(i, arg_suffix, &p);
+                        r = sd_path_lookup(i, arg_suffix, &p);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to query %s: %m", n);
 
@@ -126,7 +151,6 @@ static int help(void) {
 }
 
 static int parse_argv(int argc, char *argv[]) {
-
         enum {
                 ARG_VERSION = 0x100,
                 ARG_SUFFIX,
index 57384fb4f80f6131f594e1701370d68193b27647..3a1367ec2b0af217fd5892a939dc92ae03720073 100644 (file)
@@ -127,10 +127,7 @@ static int send_item(
                 const char *name,
                 int fd) {
 
-        union {
-                struct cmsghdr cmsghdr;
-                uint8_t buf[CMSG_SPACE(sizeof(int))];
-        } control = {};
+        CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int))) control = {};
         struct iovec iovec;
         struct msghdr mh = {
                 .msg_control = &control,
@@ -155,7 +152,6 @@ static int send_item(
         cmsg->cmsg_len = CMSG_LEN(sizeof(int));
         memcpy(CMSG_DATA(cmsg), &data_fd, sizeof(int));
 
-        mh.msg_controllen = CMSG_SPACE(sizeof(int));
         iovec = IOVEC_MAKE_STRING(name);
 
         if (sendmsg(socket_fd, &mh, MSG_NOSIGNAL) < 0)
@@ -169,10 +165,7 @@ static int recv_item(
                 char **ret_name,
                 int *ret_fd) {
 
-        union {
-                struct cmsghdr cmsghdr;
-                uint8_t buf[CMSG_SPACE(sizeof(int))];
-        } control = {};
+        CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(int))) control;
         char buffer[PATH_MAX+2];
         struct iovec iov = IOVEC_INIT(buffer, sizeof(buffer)-1);
         struct msghdr mh = {
@@ -387,7 +380,7 @@ static int portable_extract_by_path(
                 if (r < 0)
                         return log_debug_errno(r, "Failed to create temporary directory: %m");
 
-                r = dissect_image(d->fd, NULL, 0, DISSECT_IMAGE_READ_ONLY|DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_DISCARD_ON_LOOP|DISSECT_IMAGE_RELAX_VAR_CHECK, &m);
+                r = dissect_image(d->fd, NULL, 0, NULL, DISSECT_IMAGE_READ_ONLY|DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_DISCARD_ON_LOOP|DISSECT_IMAGE_RELAX_VAR_CHECK, &m);
                 if (r == -ENOPKG)
                         sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Couldn't identify a suitable partition table or file system in '%s'.", path);
                 else if (r == -EADDRNOTAVAIL)
@@ -702,16 +695,28 @@ static int install_chroot_dropin(
         if (!text)
                 return -ENOMEM;
 
-        if (endswith(m->name, ".service"))
+        if (endswith(m->name, ".service")) {
+                const char *os_release_source;
+
+                if (access("/etc/os-release", F_OK) < 0) {
+                        if (errno != ENOENT)
+                                return log_debug_errno(errno, "Failed to check if /etc/os-release exists: %m");
+
+                        os_release_source = "/usr/lib/os-release";
+                } else
+                        os_release_source = "/etc/os-release";
+
                 if (!strextend(&text,
                                "\n"
                                "[Service]\n",
                                IN_SET(type, IMAGE_DIRECTORY, IMAGE_SUBVOLUME) ? "RootDirectory=" : "RootImage=", image_path, "\n"
                                "Environment=PORTABLE=", basename(image_path), "\n"
+                               "BindReadOnlyPaths=", os_release_source, ":/run/host/os-release\n"
                                "LogExtraFields=PORTABLE=", basename(image_path), "\n",
                                NULL))
 
                         return -ENOMEM;
+        }
 
         r = write_string_file(dropin, text, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
         if (r < 0)
@@ -1092,10 +1097,9 @@ static int test_chroot_dropin(
                 return log_debug_errno(errno, "Failed to open %s/%s: %m", where, p);
         }
 
-        r = fdopen_unlocked(fd, "r", &f);
+        r = take_fdopen_unlocked(&fd, "r", &f);
         if (r < 0)
                 return log_debug_errno(r, "Failed to convert file handle: %m");
-        TAKE_FD(fd);
 
         r = read_line(f, LONG_LINE_MAX, &line);
         if (r < 0)
@@ -1133,7 +1137,7 @@ int portable_detach(
                 sd_bus_error *error) {
 
         _cleanup_(lookup_paths_free) LookupPaths paths = {};
-        _cleanup_set_free_free_ Set *unit_files = NULL, *markers = NULL;
+        _cleanup_set_free_ Set *unit_files = NULL, *markers = NULL;
         _cleanup_closedir_ DIR *d = NULL;
         const char *where, *item;
         Iterator iterator;
@@ -1157,14 +1161,6 @@ int portable_detach(
                 return log_debug_errno(errno, "Failed to open '%s' directory: %m", where);
         }
 
-        unit_files = set_new(&string_hash_ops);
-        if (!unit_files)
-                return -ENOMEM;
-
-        markers = set_new(&path_hash_ops);
-        if (!markers)
-                return -ENOMEM;
-
         FOREACH_DIRENT(de, d, return log_debug_errno(errno, "Failed to enumerate '%s' directory: %m", where)) {
                 _cleanup_free_ char *marker = NULL;
                 UnitFileState state;
@@ -1173,7 +1169,7 @@ int portable_detach(
                         continue;
 
                 /* Filter out duplicates */
-                if (set_get(unit_files, de->d_name))
+                if (set_contains(unit_files, de->d_name))
                         continue;
 
                 dirent_ensure_type(d, de);
@@ -1198,22 +1194,16 @@ int portable_detach(
                 if (r > 0)
                         return sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS, "Unit file '%s' is active, can't detach.", de->d_name);
 
-                r = set_put_strdup(unit_files, de->d_name);
+                r = set_put_strdup(&unit_files, de->d_name);
                 if (r < 0)
                         return log_debug_errno(r, "Failed to add unit name '%s' to set: %m", de->d_name);
 
                 if (path_is_absolute(marker) &&
                     !image_in_search_path(IMAGE_PORTABLE, marker)) {
 
-                        r = set_ensure_allocated(&markers, &path_hash_ops);
+                        r = set_ensure_consume(&markers, &path_hash_ops_free, TAKE_PTR(marker));
                         if (r < 0)
                                 return r;
-
-                        r = set_put(markers, marker);
-                        if (r >= 0)
-                                marker = NULL;
-                        else if (r != -EEXIST)
-                                return r;
                 }
         }
 
@@ -1311,7 +1301,7 @@ static int portable_get_state_internal(
 
         _cleanup_(lookup_paths_free) LookupPaths paths = {};
         bool found_enabled = false, found_running = false;
-        _cleanup_set_free_free_ Set *unit_files = NULL;
+        _cleanup_set_free_ Set *unit_files = NULL;
         _cleanup_closedir_ DIR *d = NULL;
         const char *where;
         struct dirent *de;
@@ -1337,10 +1327,6 @@ static int portable_get_state_internal(
                 return log_debug_errno(errno, "Failed to open '%s' directory: %m", where);
         }
 
-        unit_files = set_new(&string_hash_ops);
-        if (!unit_files)
-                return -ENOMEM;
-
         FOREACH_DIRENT(de, d, return log_debug_errno(errno, "Failed to enumerate '%s' directory: %m", where)) {
                 UnitFileState state;
 
@@ -1348,7 +1334,7 @@ static int portable_get_state_internal(
                         continue;
 
                 /* Filter out duplicates */
-                if (set_get(unit_files, de->d_name))
+                if (set_contains(unit_files, de->d_name))
                         continue;
 
                 dirent_ensure_type(d, de);
@@ -1373,7 +1359,7 @@ static int portable_get_state_internal(
                 if (r > 0)
                         found_running = true;
 
-                r = set_put_strdup(unit_files, de->d_name);
+                r = set_put_strdup(&unit_files, de->d_name);
                 if (r < 0)
                         return log_debug_errno(r, "Failed to add unit name '%s' to set: %m", de->d_name);
         }
index bf5badd699c3b29979e53eeb8f319de56abc2dd9..aa6369864a4e4240cdbded6083d3cb5111ee8f36 100644 (file)
@@ -7,8 +7,8 @@
 
 #include "alloc-util.h"
 #include "bus-error.h"
+#include "bus-locator.h"
 #include "bus-unit-util.h"
-#include "bus-util.h"
 #include "bus-wait-for-jobs.h"
 #include "def.h"
 #include "dirent-util.h"
@@ -239,13 +239,7 @@ static int inspect_image(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return r;
 
-        r = sd_bus_message_new_method_call(
-                                bus,
-                                &m,
-                                "org.freedesktop.portable1",
-                                "/org/freedesktop/portable1",
-                                "org.freedesktop.portable1.Manager",
-                                "GetImageMetadata");
+        r = bus_message_new_method_call(bus, &m, bus_portable_mgr, "GetImageMetadata");
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -554,13 +548,7 @@ static int maybe_stop_disable(sd_bus *bus, char *image, char *argv[]) {
         if (r < 0)
                 return log_error_errno(r, "Could not watch jobs: %m");
 
-        r = sd_bus_message_new_method_call(
-                                bus,
-                                &m,
-                                "org.freedesktop.portable1",
-                                "/org/freedesktop/portable1",
-                                "org.freedesktop.portable1.Manager",
-                                "GetImageMetadata");
+        r = bus_message_new_method_call(bus, &m, bus_portable_mgr, "GetImageMetadata");
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -643,13 +631,7 @@ static int attach_image(int argc, char *argv[], void *userdata) {
 
         (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
 
-        r = sd_bus_message_new_method_call(
-                                bus,
-                                &m,
-                                "org.freedesktop.portable1",
-                                "/org/freedesktop/portable1",
-                                "org.freedesktop.portable1.Manager",
-                                "AttachImage");
+        r = bus_message_new_method_call(bus, &m, bus_portable_mgr, "AttachImage");
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -697,15 +679,7 @@ static int detach_image(int argc, char *argv[], void *userdata) {
 
         (void) maybe_stop_disable(bus, image, argv);
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.portable1",
-                        "/org/freedesktop/portable1",
-                        "org.freedesktop.portable1.Manager",
-                        "DetachImage",
-                        &error,
-                        &reply,
-                        "sb", image, arg_runtime);
+        r = bus_call_method(bus, bus_portable_mgr, "DetachImage", &error, &reply, "sb", image, arg_runtime);
         if (r < 0)
                 return log_error_errno(r, "Failed to detach image: %s", bus_error_message(&error, r));
 
@@ -726,15 +700,7 @@ static int list_images(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return r;
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.portable1",
-                        "/org/freedesktop/portable1",
-                        "org.freedesktop.portable1.Manager",
-                        "ListImages",
-                        &error,
-                        &reply,
-                        NULL);
+        r = bus_call_method(bus, bus_portable_mgr, "ListImages", &error, &reply, NULL);
         if (r < 0)
                 return log_error_errno(r, "Failed to list images: %s", bus_error_message(&error, r));
 
@@ -778,13 +744,13 @@ static int list_images(int argc, char *argv[], void *userdata) {
         if (table_get_rows(table) > 1) {
                 r = table_set_sort(table, (size_t) 0, (size_t) -1);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to sort table: %m");
+                        return table_log_sort_error(r);
 
                 table_set_header(table, arg_legend);
 
                 r = table_print(table, NULL);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to show table: %m");
+                        return table_log_print_error(r);
         }
 
         if (arg_legend) {
@@ -811,13 +777,7 @@ static int remove_image(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 *m = NULL;
 
-                r = sd_bus_message_new_method_call(
-                                bus,
-                                &m,
-                                "org.freedesktop.portable1",
-                                "/org/freedesktop/portable1",
-                                "org.freedesktop.portable1.Manager",
-                                "RemoveImage");
+                r = bus_message_new_method_call(bus, &m, bus_portable_mgr, "RemoveImage");
                 if (r < 0)
                         return bus_log_create_error(r);
 
@@ -851,15 +811,7 @@ static int read_only_image(int argc, char *argv[], void *userdata) {
 
         (void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.portable1",
-                        "/org/freedesktop/portable1",
-                        "org.freedesktop.portable1.Manager",
-                        "MarkImageReadOnly",
-                        &error,
-                        NULL,
-                        "sb", argv[1], b);
+        r = bus_call_method(bus, bus_portable_mgr, "MarkImageReadOnly", &error, NULL, "sb", argv[1], b);
         if (r < 0)
                 return log_error_errno(r, "Could not mark image read-only: %s", bus_error_message(&error, r));
 
@@ -888,26 +840,10 @@ static int set_limit(int argc, char *argv[], void *userdata) {
 
         if (argc > 2)
                 /* With two arguments changes the quota limit of the specified image */
-                r = sd_bus_call_method(
-                                bus,
-                                "org.freedesktop.portable1",
-                                "/org/freedesktop/portable1",
-                                "org.freedesktop.portable1.Manager",
-                                "SetImageLimit",
-                                &error,
-                                NULL,
-                                "st", argv[1], limit);
+                r = bus_call_method(bus, bus_portable_mgr, "SetImageLimit", &error, NULL, "st", argv[1], limit);
         else
                 /* With one argument changes the pool quota limit */
-                r = sd_bus_call_method(
-                                bus,
-                                "org.freedesktop.portable1",
-                                "/org/freedesktop/portable1",
-                                "org.freedesktop.portable1.Manager",
-                                "SetPoolLimit",
-                                &error,
-                                NULL,
-                                "t", limit);
+                r = bus_call_method(bus, bus_portable_mgr, "SetPoolLimit", &error, NULL, "t", limit);
 
         if (r < 0)
                 return log_error_errno(r, "Could not set limit: %s", bus_error_message(&error, r));
@@ -931,15 +867,7 @@ static int is_image_attached(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return r;
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.portable1",
-                        "/org/freedesktop/portable1",
-                        "org.freedesktop.portable1.Manager",
-                        "GetImageState",
-                        &error,
-                        &reply,
-                        "s", image);
+        r = bus_call_method(bus, bus_portable_mgr, "GetImageState", &error, &reply, "s", image);
         if (r < 0)
                 return log_error_errno(r, "Failed to get image state: %s", bus_error_message(&error, r));
 
@@ -964,14 +892,7 @@ static int dump_profiles(void) {
         if (r < 0)
                 return r;
 
-        r = sd_bus_get_property_strv(
-                        bus,
-                        "org.freedesktop.portable1",
-                        "/org/freedesktop/portable1",
-                        "org.freedesktop.portable1.Manager",
-                        "Profiles",
-                        &error,
-                        &l);
+        r = bus_get_property_strv(bus, bus_portable_mgr, "Profiles", &error, &l);
         if (r < 0)
                 return log_error_errno(r, "Failed to acquire list of profiles: %s", bus_error_message(&error, r));
 
@@ -1198,9 +1119,7 @@ static int run(int argc, char *argv[]) {
 
         int r;
 
-        log_show_color(true);
-        log_parse_environment();
-        log_open();
+        log_setup_cli();
 
         r = parse_argv(argc, argv);
         if (r <= 0)
index 2bd1c495e4e0ee540202780230480a74be27886a..9646601491ed43026d4968b5361babb30d6c9564 100644 (file)
@@ -7,6 +7,7 @@
 
 #include "alloc-util.h"
 #include "bus-common-errors.h"
+#include "bus-get-properties.h"
 #include "bus-label.h"
 #include "bus-polkit.h"
 #include "bus-util.h"
@@ -79,12 +80,10 @@ static int append_fd(sd_bus_message *m, PortableMetadata *d) {
         assert(d);
         assert(d->fd >= 0);
 
-        f = fdopen(d->fd, "r");
+        f = take_fdopen(&d->fd, "r");
         if (!f)
                 return -errno;
 
-        d->fd = -1;
-
         r = read_full_stream(f, &buf, &n);
         if (r < 0)
                 return r;
index 75b76926e5565be0e0b4370d17ff6cfcb59e6bb7..265f7a7440d3910f3d3d151918ec988533094e67 100644 (file)
@@ -7,6 +7,7 @@
 #include "sd-daemon.h"
 
 #include "alloc-util.h"
+#include "bus-log-control-api.h"
 #include "bus-polkit.h"
 #include "def.h"
 #include "main-func.h"
@@ -84,6 +85,10 @@ static int manager_connect_bus(Manager *m) {
         if (r < 0)
                 return log_error_errno(r, "Failed to add image enumerator: %m");
 
+        r = bus_log_control_api_register(m->bus);
+        if (r < 0)
+                return r;
+
         r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.portable1", 0, NULL, NULL);
         if (r < 0)
                 return log_error_errno(r, "Failed to request name: %m");
index 5c812b5d5b49b9768e0a0ae9d884d65ca0b6f765..9b888a2baacdfb89451e4679fd5318cd1b808092 100644 (file)
@@ -78,11 +78,14 @@ static int parse_config(void) {
                 {}
         };
 
-        return config_parse_many_nulstr(PKGSYSCONFDIR "/pstore.conf",
-                                        CONF_PATHS_NULSTR("systemd/pstore.conf.d"),
-                                        "PStore\0",
-                                        config_item_table_lookup, items,
-                                        CONFIG_PARSE_WARN, NULL);
+        return config_parse_many_nulstr(
+                        PKGSYSCONFDIR "/pstore.conf",
+                        CONF_PATHS_NULSTR("systemd/pstore.conf.d"),
+                        "PStore\0",
+                        config_item_table_lookup, items,
+                        CONFIG_PARSE_WARN,
+                        NULL,
+                        NULL);
 }
 
 /* File list handling - PStoreEntry is the struct and
@@ -98,8 +101,8 @@ typedef struct PStoreEntry {
 
 typedef struct PStoreList {
         PStoreEntry *entries;
+        size_t n_allocated;
         size_t n_entries;
-        size_t n_entries_allocated;
 } PStoreList;
 
 static void pstore_entries_reset(PStoreList *list) {
@@ -109,8 +112,7 @@ static void pstore_entries_reset(PStoreList *list) {
         list->n_entries = 0;
 }
 
-static int compare_pstore_entries(const void *_a, const void *_b) {
-        PStoreEntry *a = (PStoreEntry *)_a, *b = (PStoreEntry *)_b;
+static int compare_pstore_entries(const PStoreEntry *a, const PStoreEntry *b) {
         return strcmp(a->dirent.d_name, b->dirent.d_name);
 }
 
@@ -346,7 +348,7 @@ static int list_files(PStoreList *list, const char *sourcepath) {
                         continue;
                 }
 
-                if (!GREEDY_REALLOC(list->entries, list->n_entries_allocated, list->n_entries + 1))
+                if (!GREEDY_REALLOC(list->entries, list->n_allocated, list->n_entries + 1))
                         return log_oom();
 
                 list->entries[list->n_entries++] = (PStoreEntry) {
@@ -391,7 +393,7 @@ static int run(int argc, char *argv[]) {
 
         /* Handle each pstore file */
         /* Sort files lexigraphically ascending, generally needed by all */
-        qsort_safe(list.entries, list.n_entries, sizeof(PStoreEntry), compare_pstore_entries);
+        typesafe_qsort(list.entries, list.n_entries, compare_pstore_entries);
 
         /* Process known file types */
         process_dmesg_files(&list);
index 596bff98f1478bd65e303db5db79ba47aafc6a55..63ad977514d8eabdeb1dcaa4bcb41766506c439d 100644 (file)
@@ -236,24 +236,10 @@ static int run(int argc, char *argv[]) {
                                 }
                         }
 
-                        if (IN_SET(lets_credit, CREDIT_ENTROPY_YES_PLEASE, CREDIT_ENTROPY_YES_FORCED)) {
-                                _cleanup_free_ struct rand_pool_info *info = NULL;
-
-                                info = malloc(offsetof(struct rand_pool_info, buf) + k);
-                                if (!info)
-                                        return log_oom();
-
-                                info->entropy_count = k * 8;
-                                info->buf_size = k;
-                                memcpy(info->buf, buf, k);
-
-                                if (ioctl(random_fd, RNDADDENTROPY, info) < 0)
-                                        return log_warning_errno(errno, "Failed to credit entropy, ignoring: %m");
-                        } else {
-                                r = loop_write(random_fd, buf, (size_t) k, false);
-                                if (r < 0)
-                                        log_error_errno(r, "Failed to write seed to /dev/urandom: %m");
-                        }
+                        r = random_write_entropy(random_fd, buf, k,
+                                                 IN_SET(lets_credit, CREDIT_ENTROPY_YES_PLEASE, CREDIT_ENTROPY_YES_FORCED));
+                        if (r < 0)
+                                log_error_errno(r, "Failed to write seed to /dev/urandom: %m");
                 }
         }
 
@@ -305,7 +291,7 @@ static int run(int argc, char *argv[]) {
                  * entropy later on. Let's keep that in mind by setting an extended attribute. on the file */
                 if (getrandom_worked)
                         if (fsetxattr(seed_fd, "user.random-seed-creditable", "1", 1, 0) < 0)
-                                log_full_errno(IN_SET(errno, ENOSYS, EOPNOTSUPP) ? LOG_DEBUG : LOG_WARNING, errno,
+                                log_full_errno(ERRNO_IS_NOT_SUPPORTED(errno) ? LOG_DEBUG : LOG_WARNING, errno,
                                                "Failed to mark seed file as creditable, ignoring: %m");
         }
 
index c4d8d4e5d9a3bb309017553f7b6c813b22b6bfc8..92b67b6333d764ce474aeb05a7085fcfebd178ae 100644 (file)
@@ -64,8 +64,6 @@ systemd_resolved_sources = files('''
         resolved-etc-hosts.h
         resolved-etc-hosts.c
         resolved-dnstls.h
-        resolved-util.c
-        resolved-util.h
 '''.split())
 
 resolvectl_sources = files('''
@@ -230,10 +228,4 @@ tests += [
          [],
          [],
          'ENABLE_RESOLVE', 'manual'],
-
-        [['src/resolve/test-resolved-util.c',
-          'src/resolve/resolved-util.c',
-          'src/resolve/resolved-util.h'],
-         [],
-         []],
 ]
index f20e8c44b8bc3bf512dd64073ba8b9dad27c1600..3072b984e5d52a3ecbbbf8545a131cb15bf97fbd 100644 (file)
@@ -11,7 +11,9 @@
 #include "alloc-util.h"
 #include "bus-common-errors.h"
 #include "bus-error.h"
-#include "bus-util.h"
+#include "bus-locator.h"
+#include "bus-map-properties.h"
+#include "bus-message-util.h"
 #include "dns-domain.h"
 #include "escape.h"
 #include "format-table.h"
@@ -28,6 +30,7 @@
 #include "resolved-def.h"
 #include "resolved-dns-packet.h"
 #include "socket-netlink.h"
+#include "sort-util.h"
 #include "stdio-util.h"
 #include "string-table.h"
 #include "strv.h"
@@ -79,6 +82,21 @@ typedef enum StatusMode {
         STATUS_NTA,
 } StatusMode;
 
+typedef struct InterfaceInfo {
+        int index;
+        const char *name;
+} InterfaceInfo;
+
+static int interface_info_compare(const InterfaceInfo *a, const InterfaceInfo *b) {
+        int r;
+
+        r = CMP(a->index, b->index);
+        if (r != 0)
+                return r;
+
+        return strcmp_ptr(a->name, b->name);
+}
+
 int ifname_mangle(const char *s) {
         _cleanup_free_ char *iface = NULL;
         const char *dot;
@@ -169,13 +187,7 @@ static int resolve_host(sd_bus *bus, const char *name) {
 
         log_debug("Resolving %s (family %s, interface %s).", name, af_to_name(arg_family) ?: "*", isempty(arg_ifname) ? "*" : arg_ifname);
 
-        r = sd_bus_message_new_method_call(
-                        bus,
-                        &req,
-                        "org.freedesktop.resolve1",
-                        "/org/freedesktop/resolve1",
-                        "org.freedesktop.resolve1.Manager",
-                        "ResolveHostname");
+        r = bus_message_new_method_call(bus, &req, bus_resolve_mgr, "ResolveHostname");
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -198,34 +210,29 @@ static int resolve_host(sd_bus *bus, const char *name) {
         while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) {
                 _cleanup_free_ char *pretty = NULL;
                 int ifindex, family, k;
-                const void *a;
-                size_t sz;
+                union in_addr_union a;
 
                 assert_cc(sizeof(int) == sizeof(int32_t));
 
-                r = sd_bus_message_read(reply, "ii", &ifindex, &family);
+                r = sd_bus_message_read(reply, "i", &ifindex);
                 if (r < 0)
                         return bus_log_parse_error(r);
 
-                r = sd_bus_message_read_array(reply, 'y', &a, &sz);
-                if (r < 0)
-                        return bus_log_parse_error(r);
+                sd_bus_error_free(&error);
+                r = bus_message_read_in_addr_auto(reply, &error, &family, &a);
+                if (r < 0 && !sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS))
+                        return log_error_errno(r, "%s: systemd-resolved returned invalid result: %s", name, bus_error_message(&error, r));
 
                 r = sd_bus_message_exit_container(reply);
                 if (r < 0)
                         return bus_log_parse_error(r);
 
-                if (!IN_SET(family, AF_INET, AF_INET6)) {
-                        log_debug("%s: skipping entry with family %d (%s)", name, family, af_to_name(family) ?: "unknown");
+                if (sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS)) {
+                        log_debug_errno(r, "%s: systemd-resolved returned invalid result, ignoring: %s", name, bus_error_message(&error, r));
                         continue;
                 }
 
-                if (sz != FAMILY_ADDRESS_SIZE(family)) {
-                        log_error("%s: systemd-resolved returned address of invalid size %zu for family %s", name, sz, af_to_name(family) ?: "unknown");
-                        return -EINVAL;
-                }
-
-                r = in_addr_ifindex_to_string(family, a, ifindex, &pretty);
+                r = in_addr_ifindex_to_string(family, &a, ifindex, &pretty);
                 if (r < 0)
                         return log_error_errno(r, "Failed to print address for %s: %m", name);
 
@@ -286,13 +293,7 @@ static int resolve_address(sd_bus *bus, int family, const union in_addr_union *a
 
         log_debug("Resolving %s.", pretty);
 
-        r = sd_bus_message_new_method_call(
-                        bus,
-                        &req,
-                        "org.freedesktop.resolve1",
-                        "/org/freedesktop/resolve1",
-                        "org.freedesktop.resolve1.Manager",
-                        "ResolveAddress");
+        r = bus_message_new_method_call(bus, &req, bus_resolve_mgr, "ResolveAddress");
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -421,13 +422,7 @@ static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_
 
         log_debug("Resolving %s %s %s (interface %s).", name, dns_class_to_string(class), dns_type_to_string(type), isempty(arg_ifname) ? "*" : arg_ifname);
 
-        r = sd_bus_message_new_method_call(
-                        bus,
-                        &req,
-                        "org.freedesktop.resolve1",
-                        "/org/freedesktop/resolve1",
-                        "org.freedesktop.resolve1.Manager",
-                        "ResolveRecord");
+        r = bus_message_new_method_call(bus, &req, bus_resolve_mgr, "ResolveRecord");
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -689,13 +684,7 @@ static int resolve_service(sd_bus *bus, const char *name, const char *type, cons
         else
                 log_debug("Resolving service type %s (family %s, interface %s).", domain, af_to_name(arg_family) ?: "*", isempty(arg_ifname) ? "*" : arg_ifname);
 
-        r = sd_bus_message_new_method_call(
-                        bus,
-                        &req,
-                        "org.freedesktop.resolve1",
-                        "/org/freedesktop/resolve1",
-                        "org.freedesktop.resolve1.Manager",
-                        "ResolveService");
+        r = bus_message_new_method_call(bus, &req, bus_resolve_mgr, "ResolveService");
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -747,33 +736,29 @@ static int resolve_service(sd_bus *bus, const char *name, const char *type, cons
                 while ((r = sd_bus_message_enter_container(reply, 'r', "iiay")) > 0) {
                         _cleanup_free_ char *pretty = NULL;
                         int ifindex, family, k;
-                        const void *a;
+                        union in_addr_union a;;
 
                         assert_cc(sizeof(int) == sizeof(int32_t));
 
-                        r = sd_bus_message_read(reply, "ii", &ifindex, &family);
+                        r = sd_bus_message_read(reply, "i", &ifindex);
                         if (r < 0)
                                 return bus_log_parse_error(r);
 
-                        r = sd_bus_message_read_array(reply, 'y', &a, &sz);
-                        if (r < 0)
-                                return bus_log_parse_error(r);
+                        sd_bus_error_free(&error);
+                        r = bus_message_read_in_addr_auto(reply, &error, &family, &a);
+                        if (r < 0 && !sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS))
+                                return log_error_errno(r, "%s: systemd-resolved returned invalid result: %s", name, bus_error_message(&error, r));
 
                         r = sd_bus_message_exit_container(reply);
                         if (r < 0)
                                 return bus_log_parse_error(r);
 
-                        if (!IN_SET(family, AF_INET, AF_INET6)) {
-                                log_debug("%s: skipping entry with family %d (%s)", name, family, af_to_name(family) ?: "unknown");
+                        if (sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS)) {
+                                log_debug_errno(r, "%s: systemd-resolved returned invalid result, ignoring: %s", name, bus_error_message(&error, r));
                                 continue;
                         }
 
-                        if (sz != FAMILY_ADDRESS_SIZE(family)) {
-                                log_error("%s: systemd-resolved returned address of invalid size %zu for family %s", name, sz, af_to_name(family) ?: "unknown");
-                                return -EINVAL;
-                        }
-
-                        r = in_addr_ifindex_to_string(family, a, ifindex, &pretty);
+                        r = in_addr_ifindex_to_string(family, &a, ifindex, &pretty);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to print address for %s: %m", name);
 
@@ -994,14 +979,7 @@ static int show_statistics(int argc, char **argv, void *userdata) {
 
         assert(bus);
 
-        r = sd_bus_get_property_trivial(bus,
-                                        "org.freedesktop.resolve1",
-                                        "/org/freedesktop/resolve1",
-                                        "org.freedesktop.resolve1.Manager",
-                                        "DNSSECSupported",
-                                        &error,
-                                        'b',
-                                        &dnssec_supported);
+        r = bus_get_property_trivial(bus, bus_resolve_mgr, "DNSSECSupported", &error, 'b', &dnssec_supported);
         if (r < 0)
                 return log_error_errno(r, "Failed to get DNSSEC supported state: %s", bus_error_message(&error, r));
 
@@ -1010,14 +988,7 @@ static int show_statistics(int argc, char **argv, void *userdata) {
                yes_no(dnssec_supported),
                ansi_normal());
 
-        r = sd_bus_get_property(bus,
-                                "org.freedesktop.resolve1",
-                                "/org/freedesktop/resolve1",
-                                "org.freedesktop.resolve1.Manager",
-                                "TransactionStatistics",
-                                &error,
-                                &reply,
-                                "(tt)");
+        r = bus_get_property(bus, bus_resolve_mgr, "TransactionStatistics", &error, &reply, "(tt)");
         if (r < 0)
                 return log_error_errno(r, "Failed to get transaction statistics: %s", bus_error_message(&error, r));
 
@@ -1029,14 +1000,7 @@ static int show_statistics(int argc, char **argv, void *userdata) {
 
         reply = sd_bus_message_unref(reply);
 
-        r = sd_bus_get_property(bus,
-                                "org.freedesktop.resolve1",
-                                "/org/freedesktop/resolve1",
-                                "org.freedesktop.resolve1.Manager",
-                                "CacheStatistics",
-                                &error,
-                                &reply,
-                                "(ttt)");
+        r = bus_get_property(bus, bus_resolve_mgr, "CacheStatistics", &error, &reply, "(ttt)");
         if (r < 0)
                 return log_error_errno(r, "Failed to get cache statistics: %s", bus_error_message(&error, r));
 
@@ -1049,14 +1013,7 @@ static int show_statistics(int argc, char **argv, void *userdata) {
 
         reply = sd_bus_message_unref(reply);
 
-        r = sd_bus_get_property(bus,
-                                "org.freedesktop.resolve1",
-                                "/org/freedesktop/resolve1",
-                                "org.freedesktop.resolve1.Manager",
-                                "DNSSECStatistics",
-                                &error,
-                                &reply,
-                                "(tttt)");
+        r = bus_get_property(bus, bus_resolve_mgr, "DNSSECStatistics", &error, &reply, "(tttt)");
         if (r < 0)
                 return log_error_errno(r, "Failed to get DNSSEC statistics: %s", bus_error_message(&error, r));
 
@@ -1114,7 +1071,7 @@ static int show_statistics(int argc, char **argv, void *userdata) {
 
         r = table_print(table, NULL);
         if (r < 0)
-                return log_error_errno(r, "Failed to print table: %m");
+                return table_log_print_error(r);
 
         return 0;
 }
@@ -1124,14 +1081,7 @@ static int reset_statistics(int argc, char **argv, void *userdata) {
         sd_bus *bus = userdata;
         int r;
 
-        r = sd_bus_call_method(bus,
-                               "org.freedesktop.resolve1",
-                               "/org/freedesktop/resolve1",
-                               "org.freedesktop.resolve1.Manager",
-                               "ResetStatistics",
-                               &error,
-                               NULL,
-                               NULL);
+        r = bus_call_method(bus, bus_resolve_mgr, "ResetStatistics", &error, NULL, NULL);
         if (r < 0)
                 return log_error_errno(r, "Failed to reset statistics: %s", bus_error_message(&error, r));
 
@@ -1143,14 +1093,7 @@ static int flush_caches(int argc, char **argv, void *userdata) {
         sd_bus *bus = userdata;
         int r;
 
-        r = sd_bus_call_method(bus,
-                               "org.freedesktop.resolve1",
-                               "/org/freedesktop/resolve1",
-                               "org.freedesktop.resolve1.Manager",
-                               "FlushCaches",
-                               &error,
-                               NULL,
-                               NULL);
+        r = bus_call_method(bus, bus_resolve_mgr, "FlushCaches", &error, NULL, NULL);
         if (r < 0)
                 return log_error_errno(r, "Failed to flush caches: %s", bus_error_message(&error, r));
 
@@ -1162,30 +1105,25 @@ static int reset_server_features(int argc, char **argv, void *userdata) {
         sd_bus *bus = userdata;
         int r;
 
-        r = sd_bus_call_method(bus,
-                               "org.freedesktop.resolve1",
-                               "/org/freedesktop/resolve1",
-                               "org.freedesktop.resolve1.Manager",
-                               "ResetServerFeatures",
-                               &error,
-                               NULL,
-                               NULL);
+        r = bus_call_method(bus, bus_resolve_mgr, "ResetServerFeatures", &error, NULL, NULL);
         if (r < 0)
                 return log_error_errno(r, "Failed to reset server features: %s", bus_error_message(&error, r));
 
         return 0;
 }
 
-static int read_dns_server_one(sd_bus_message *m, bool with_ifindex, char **ret) {
+static int read_dns_server_one(sd_bus_message *m, bool with_ifindex, bool extended, char **ret) {
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         _cleanup_free_ char *pretty = NULL;
-        int ifindex, family, r;
-        const void *a;
-        size_t sz;
+        int ifindex, family, r, k;
+        union in_addr_union a;
+        const char *name = NULL;
+        uint16_t port = 0;
 
         assert(m);
         assert(ret);
 
-        r = sd_bus_message_enter_container(m, 'r', with_ifindex ? "iiay" : "iay");
+        r = sd_bus_message_enter_container(m, 'r', with_ifindex ? (extended ? "iiayqs" : "iiay") : (extended ? "iayqs" : "iay"));
         if (r <= 0)
                 return r;
 
@@ -1195,39 +1133,37 @@ static int read_dns_server_one(sd_bus_message *m, bool with_ifindex, char **ret)
                         return r;
         }
 
-        r = sd_bus_message_read(m, "i", &family);
-        if (r < 0)
-                return r;
+        k = bus_message_read_in_addr_auto(m, &error, &family, &a);
+        if (k < 0 && !sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS))
+                return k;
 
-        r = sd_bus_message_read_array(m, 'y', &a, &sz);
-        if (r < 0)
-                return r;
+        if (extended) {
+                r = sd_bus_message_read(m, "q", &port);
+                if (r < 0)
+                        return r;
+
+                r = sd_bus_message_read(m, "s", &name);
+                if (r < 0)
+                        return r;
+        }
 
         r = sd_bus_message_exit_container(m);
         if (r < 0)
                 return r;
 
-        if (with_ifindex && ifindex != 0) {
-                /* only show the global ones here */
+        if (k < 0) {
+                log_debug("Invalid DNS server, ignoring: %s", bus_error_message(&error, k));
                 *ret = NULL;
                 return 1;
         }
 
-        if (!IN_SET(family, AF_INET, AF_INET6)) {
-                log_debug("Unexpected family, ignoring: %i", family);
-
-                *ret = NULL;
-                return 1;
-        }
-
-        if (sz != FAMILY_ADDRESS_SIZE(family)) {
-                log_debug("Address size mismatch, ignoring.");
-
+        if (with_ifindex && ifindex != 0) {
+                /* only show the global ones here */
                 *ret = NULL;
                 return 1;
         }
 
-        r = in_addr_to_string(family, a, &pretty);
+        r = in_addr_port_ifindex_name_to_string(family, &a, port, ifindex, name, &pretty);
         if (r < 0)
                 return r;
 
@@ -1236,7 +1172,7 @@ static int read_dns_server_one(sd_bus_message *m, bool with_ifindex, char **ret)
         return 1;
 }
 
-static int map_link_dns_servers(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
+static int map_link_dns_servers_internal(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata, bool extended) {
         char ***l = userdata;
         int r;
 
@@ -1245,14 +1181,14 @@ static int map_link_dns_servers(sd_bus *bus, const char *member, sd_bus_message
         assert(m);
         assert(l);
 
-        r = sd_bus_message_enter_container(m, 'a', "(iay)");
+        r = sd_bus_message_enter_container(m, 'a', extended ? "(iayqs)" : "(iay)");
         if (r < 0)
                 return r;
 
         for (;;) {
                 _cleanup_free_ char *pretty = NULL;
 
-                r = read_dns_server_one(m, false, &pretty);
+                r = read_dns_server_one(m, false, extended, &pretty);
                 if (r < 0)
                         return r;
                 if (r == 0)
@@ -1273,11 +1209,26 @@ static int map_link_dns_servers(sd_bus *bus, const char *member, sd_bus_message
         return 0;
 }
 
+static int map_link_dns_servers(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
+        return map_link_dns_servers_internal(bus, member, m, error, userdata, false);
+}
+
+static int map_link_dns_servers_ex(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
+        return map_link_dns_servers_internal(bus, member, m, error, userdata, true);
+}
+
 static int map_link_current_dns_server(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
         assert(m);
         assert(userdata);
 
-        return read_dns_server_one(m, false, userdata);
+        return read_dns_server_one(m, false, false, userdata);
+}
+
+static int map_link_current_dns_server_ex(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
+        assert(m);
+        assert(userdata);
+
+        return read_dns_server_one(m, false, true, userdata);
 }
 
 static int read_domain_one(sd_bus_message *m, bool with_ifindex, char **ret) {
@@ -1371,7 +1322,9 @@ struct link_info {
         const char *dns_over_tls;
         const char *dnssec;
         char *current_dns;
+        char *current_dns_ex;
         char **dns;
+        char **dns_ex;
         char **domains;
         char **ntas;
         bool dnssec_supported;
@@ -1380,7 +1333,9 @@ struct link_info {
 
 static void link_info_clear(struct link_info *p) {
         free(p->current_dns);
+        free(p->current_dns_ex);
         strv_free(p->dns);
+        strv_free(p->dns_ex);
         strv_free(p->domains);
         strv_free(p->ntas);
 }
@@ -1402,17 +1357,19 @@ static int dump_list(Table *table, const char *prefix, char * const *l) {
 
 static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode mode, bool *empty_line) {
         static const struct bus_properties_map property_map[] = {
-                { "ScopesMask",                 "t",      NULL,                        offsetof(struct link_info, scopes_mask)      },
-                { "DNS",                        "a(iay)", map_link_dns_servers,        offsetof(struct link_info, dns)              },
-                { "CurrentDNSServer",           "(iay)",  map_link_current_dns_server, offsetof(struct link_info, current_dns)      },
-                { "Domains",                    "a(sb)",  map_link_domains,            offsetof(struct link_info, domains)          },
-                { "DefaultRoute",               "b",      NULL,                        offsetof(struct link_info, default_route)    },
-                { "LLMNR",                      "s",      NULL,                        offsetof(struct link_info, llmnr)            },
-                { "MulticastDNS",               "s",      NULL,                        offsetof(struct link_info, mdns)             },
-                { "DNSOverTLS",                 "s",      NULL,                        offsetof(struct link_info, dns_over_tls)     },
-                { "DNSSEC",                     "s",      NULL,                        offsetof(struct link_info, dnssec)           },
-                { "DNSSECNegativeTrustAnchors", "as",     NULL,                        offsetof(struct link_info, ntas)             },
-                { "DNSSECSupported",            "b",      NULL,                        offsetof(struct link_info, dnssec_supported) },
+                { "ScopesMask",                 "t",        NULL,                           offsetof(struct link_info, scopes_mask)      },
+                { "DNS",                        "a(iay)",   map_link_dns_servers,           offsetof(struct link_info, dns)              },
+                { "DNSEx",                      "a(iayqs)", map_link_dns_servers_ex,        offsetof(struct link_info, dns_ex)           },
+                { "CurrentDNSServer",           "(iay)",    map_link_current_dns_server,    offsetof(struct link_info, current_dns)      },
+                { "CurrentDNSServerEx",         "(iayqs)",  map_link_current_dns_server_ex, offsetof(struct link_info, current_dns_ex)   },
+                { "Domains",                    "a(sb)",    map_link_domains,               offsetof(struct link_info, domains)          },
+                { "DefaultRoute",               "b",        NULL,                           offsetof(struct link_info, default_route)    },
+                { "LLMNR",                      "s",        NULL,                           offsetof(struct link_info, llmnr)            },
+                { "MulticastDNS",               "s",        NULL,                           offsetof(struct link_info, mdns)             },
+                { "DNSOverTLS",                 "s",        NULL,                           offsetof(struct link_info, dns_over_tls)     },
+                { "DNSSEC",                     "s",        NULL,                           offsetof(struct link_info, dnssec)           },
+                { "DNSSECNegativeTrustAnchors", "as",       NULL,                           offsetof(struct link_info, ntas)             },
+                { "DNSSECSupported",            "b",        NULL,                           offsetof(struct link_info, dnssec_supported) },
                 {}
         };
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
@@ -1452,7 +1409,7 @@ static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode
         (void) pager_open(arg_pager_flags);
 
         if (mode == STATUS_DNS)
-                return status_print_strv_ifindex(ifindex, name, link_info.dns);
+                return status_print_strv_ifindex(ifindex, name, link_info.dns_ex ?: link_info.dns);
 
         if (mode == STATUS_DOMAIN)
                 return status_print_strv_ifindex(ifindex, name, link_info.domains);
@@ -1560,12 +1517,12 @@ static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode
         if (link_info.current_dns) {
                 r = table_add_many(table,
                                    TABLE_STRING, "Current DNS Server:",
-                                   TABLE_STRING, link_info.current_dns);
+                                   TABLE_STRING, link_info.current_dns_ex ?: link_info.current_dns);
                 if (r < 0)
                         return table_log_add_error(r);
         }
 
-        r = dump_list(table, "DNS Servers:", link_info.dns);
+        r = dump_list(table, "DNS Servers:", link_info.dns_ex ?: link_info.dns);
         if (r < 0)
                 return r;
 
@@ -1573,13 +1530,9 @@ static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode
         if (r < 0)
                 return r;
 
-        r = dump_list(table, "DNSSEC NTA:", link_info.ntas);
-        if (r < 0)
-                return r;
-
         r = table_print(table, NULL);
         if (r < 0)
-                return log_error_errno(r, "Failed to print table: %m");
+                return table_log_print_error(r);
 
         if (empty_line)
                 *empty_line = true;
@@ -1587,7 +1540,7 @@ static int status_ifindex(sd_bus *bus, int ifindex, const char *name, StatusMode
         return 0;
 }
 
-static int map_global_dns_servers(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
+static int map_global_dns_servers_internal(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata, bool extended) {
         char ***l = userdata;
         int r;
 
@@ -1596,14 +1549,14 @@ static int map_global_dns_servers(sd_bus *bus, const char *member, sd_bus_messag
         assert(m);
         assert(l);
 
-        r = sd_bus_message_enter_container(m, 'a', "(iiay)");
+        r = sd_bus_message_enter_container(m, 'a', extended ? "(iiayqs)" : "(iiay)");
         if (r < 0)
                 return r;
 
         for (;;) {
                 _cleanup_free_ char *pretty = NULL;
 
-                r = read_dns_server_one(m, true, &pretty);
+                r = read_dns_server_one(m, true, extended, &pretty);
                 if (r < 0)
                         return r;
                 if (r == 0)
@@ -1624,11 +1577,26 @@ static int map_global_dns_servers(sd_bus *bus, const char *member, sd_bus_messag
         return 0;
 }
 
+static int map_global_dns_servers(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
+        return map_global_dns_servers_internal(bus, member, m, error, userdata, false);
+}
+
+static int map_global_dns_servers_ex(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
+        return map_global_dns_servers_internal(bus, member, m, error, userdata, true);
+}
+
 static int map_global_current_dns_server(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
         assert(m);
         assert(userdata);
 
-        return read_dns_server_one(m, true, userdata);
+        return read_dns_server_one(m, true, false, userdata);
+}
+
+static int map_global_current_dns_server_ex(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
+        assert(m);
+        assert(userdata);
+
+        return read_dns_server_one(m, true, true, userdata);
 }
 
 static int map_global_domains(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
@@ -1683,8 +1651,11 @@ static int status_print_strv_global(char **p) {
 
 struct global_info {
         char *current_dns;
+        char *current_dns_ex;
         char **dns;
+        char **dns_ex;
         char **fallback_dns;
+        char **fallback_dns_ex;
         char **domains;
         char **ntas;
         const char *llmnr;
@@ -1696,24 +1667,30 @@ struct global_info {
 
 static void global_info_clear(struct global_info *p) {
         free(p->current_dns);
+        free(p->current_dns_ex);
         strv_free(p->dns);
+        strv_free(p->dns_ex);
         strv_free(p->fallback_dns);
+        strv_free(p->fallback_dns_ex);
         strv_free(p->domains);
         strv_free(p->ntas);
 }
 
 static int status_global(sd_bus *bus, StatusMode mode, bool *empty_line) {
         static const struct bus_properties_map property_map[] = {
-                { "DNS",                        "a(iiay)", map_global_dns_servers,        offsetof(struct global_info, dns)              },
-                { "FallbackDNS",                "a(iiay)", map_global_dns_servers,        offsetof(struct global_info, fallback_dns)     },
-                { "CurrentDNSServer",           "(iiay)",  map_global_current_dns_server, offsetof(struct global_info, current_dns)      },
-                { "Domains",                    "a(isb)",  map_global_domains,            offsetof(struct global_info, domains)          },
-                { "DNSSECNegativeTrustAnchors", "as",      NULL,                          offsetof(struct global_info, ntas)             },
-                { "LLMNR",                      "s",       NULL,                          offsetof(struct global_info, llmnr)            },
-                { "MulticastDNS",               "s",       NULL,                          offsetof(struct global_info, mdns)             },
-                { "DNSOverTLS",                 "s",       NULL,                          offsetof(struct global_info, dns_over_tls)     },
-                { "DNSSEC",                     "s",       NULL,                          offsetof(struct global_info, dnssec)           },
-                { "DNSSECSupported",            "b",       NULL,                          offsetof(struct global_info, dnssec_supported) },
+                { "DNS",                        "a(iiay)",   map_global_dns_servers,           offsetof(struct global_info, dns)              },
+                { "DNSEx",                      "a(iiayqs)", map_global_dns_servers_ex,        offsetof(struct global_info, dns_ex)           },
+                { "FallbackDNS",                "a(iiay)",   map_global_dns_servers,           offsetof(struct global_info, fallback_dns)     },
+                { "FallbackDNSEx",              "a(iiayqs)", map_global_dns_servers_ex,        offsetof(struct global_info, fallback_dns_ex)  },
+                { "CurrentDNSServer",           "(iiay)",    map_global_current_dns_server,    offsetof(struct global_info, current_dns)      },
+                { "CurrentDNSServerEx",         "(iiayqs)",  map_global_current_dns_server_ex, offsetof(struct global_info, current_dns_ex)   },
+                { "Domains",                    "a(isb)",    map_global_domains,               offsetof(struct global_info, domains)          },
+                { "DNSSECNegativeTrustAnchors", "as",        NULL,                             offsetof(struct global_info, ntas)             },
+                { "LLMNR",                      "s",         NULL,                             offsetof(struct global_info, llmnr)            },
+                { "MulticastDNS",               "s",         NULL,                             offsetof(struct global_info, mdns)             },
+                { "DNSOverTLS",                 "s",         NULL,                             offsetof(struct global_info, dns_over_tls)     },
+                { "DNSSEC",                     "s",         NULL,                             offsetof(struct global_info, dnssec)           },
+                { "DNSSECSupported",            "b",         NULL,                             offsetof(struct global_info, dnssec_supported) },
                 {}
         };
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
@@ -1739,7 +1716,7 @@ static int status_global(sd_bus *bus, StatusMode mode, bool *empty_line) {
         (void) pager_open(arg_pager_flags);
 
         if (mode == STATUS_DNS)
-                return status_print_strv_global(global_info.dns);
+                return status_print_strv_global(global_info.dns_ex ?: global_info.dns);
 
         if (mode == STATUS_DOMAIN)
                 return status_print_strv_global(global_info.domains);
@@ -1801,16 +1778,16 @@ static int status_global(sd_bus *bus, StatusMode mode, bool *empty_line) {
         if (global_info.current_dns) {
                 r = table_add_many(table,
                                    TABLE_STRING, "Current DNS Server:",
-                                   TABLE_STRING, global_info.current_dns);
+                                   TABLE_STRING, global_info.current_dns_ex ?: global_info.current_dns);
                 if (r < 0)
                         return table_log_add_error(r);
         }
 
-        r = dump_list(table, "DNS Servers:", global_info.dns);
+        r = dump_list(table, "DNS Servers:", global_info.dns_ex ?: global_info.dns);
         if (r < 0)
                 return r;
 
-        r = dump_list(table, "Fallback DNS Servers:", global_info.fallback_dns);
+        r = dump_list(table, "Fallback DNS Servers:", global_info.fallback_dns_ex ?: global_info.fallback_dns);
         if (r < 0)
                 return r;
 
@@ -1818,14 +1795,9 @@ static int status_global(sd_bus *bus, StatusMode mode, bool *empty_line) {
         if (r < 0)
                 return r;
 
-        strv_sort(global_info.ntas);
-        r = dump_list(table, "DNSSEC NTA:", global_info.ntas);
-        if (r < 0)
-                return r;
-
         r = table_print(table, NULL);
         if (r < 0)
-                return log_error_errno(r, "Failed to print table: %m");
+                return table_log_print_error(r);
 
         *empty_line = true;
 
@@ -1835,7 +1807,6 @@ static int status_global(sd_bus *bus, StatusMode mode, bool *empty_line) {
 static int status_all(sd_bus *bus, StatusMode mode) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
         _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
-        sd_netlink_message *i;
         bool empty_line = false;
         int r;
 
@@ -1861,31 +1832,43 @@ static int status_all(sd_bus *bus, StatusMode mode) {
         if (r < 0)
                 return log_error_errno(r, "Failed to enumerate links: %m");
 
-        r = 0;
-        for (i = reply; i; i = sd_netlink_message_next(i)) {
+        _cleanup_free_ InterfaceInfo *infos = NULL;
+        size_t n_allocated = 0, n_infos = 0;
+
+        for (sd_netlink_message *i = reply; i; i = sd_netlink_message_next(i)) {
                 const char *name;
-                int ifindex, q;
+                int ifindex;
                 uint16_t type;
 
-                q = sd_netlink_message_get_type(i, &type);
-                if (q < 0)
-                        return rtnl_log_parse_error(q);
+                r = sd_netlink_message_get_type(i, &type);
+                if (r < 0)
+                        return rtnl_log_parse_error(r);
 
                 if (type != RTM_NEWLINK)
                         continue;
 
-                q = sd_rtnl_message_link_get_ifindex(i, &ifindex);
-                if (q < 0)
-                        return rtnl_log_parse_error(q);
+                r = sd_rtnl_message_link_get_ifindex(i, &ifindex);
+                if (r < 0)
+                        return rtnl_log_parse_error(r);
 
                 if (ifindex == LOOPBACK_IFINDEX)
                         continue;
 
-                q = sd_netlink_message_read_string(i, IFLA_IFNAME, &name);
-                if (q < 0)
-                        return rtnl_log_parse_error(q);
+                r = sd_netlink_message_read_string(i, IFLA_IFNAME, &name);
+                if (r < 0)
+                        return rtnl_log_parse_error(r);
+
+                if (!GREEDY_REALLOC(infos, n_allocated, n_infos + 1))
+                        return log_oom();
+
+                infos[n_infos++] = (InterfaceInfo) { ifindex, name };
+        }
+
+        typesafe_qsort(infos, n_infos, interface_info_compare);
 
-                q = status_ifindex(bus, ifindex, name, mode, &empty_line);
+        r = 0;
+        for (size_t i = 0; i < n_infos; i++) {
+                int q = status_ifindex(bus, infos[i].index, infos[i].name, mode, &empty_line);
                 if (q < 0 && r >= 0)
                         r = q;
         }
@@ -1921,18 +1904,12 @@ static int verb_status(int argc, char **argv, void *userdata) {
         return r;
 }
 
-static int call_dns(sd_bus *bus, char **dns, const char *destination, const char *path, const char *interface, sd_bus_error *error) {
+static int call_dns(sd_bus *bus, char **dns, const BusLocator *locator, sd_bus_error *error, bool extended) {
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL;
         char **p;
         int r;
 
-        r = sd_bus_message_new_method_call(
-                        bus,
-                        &req,
-                        destination,
-                        path,
-                        interface,
-                        "SetLinkDNS");
+        r = bus_message_new_method_call(bus, &req, locator, extended ? "SetLinkDNSEx" : "SetLinkDNS");
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -1940,7 +1917,7 @@ static int call_dns(sd_bus *bus, char **dns, const char *destination, const char
         if (r < 0)
                 return bus_log_create_error(r);
 
-        r = sd_bus_message_open_container(req, 'a', "(iay)");
+        r = sd_bus_message_open_container(req, 'a', extended ? "(iayqs)" : "(iay)");
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -1948,13 +1925,19 @@ static int call_dns(sd_bus *bus, char **dns, const char *destination, const char
          * empty list, which will clear the list of domains for an interface. */
         if (!strv_equal(dns, STRV_MAKE("")))
                 STRV_FOREACH(p, dns) {
+                        _cleanup_free_ char *name = NULL;
                         struct in_addr_data data;
+                        uint16_t port;
+                        int ifindex;
 
-                        r = in_addr_from_string_auto(*p, &data.family, &data.address);
+                        r = in_addr_port_ifindex_name_from_string_auto(*p, &data.family, &data.address, &port, &ifindex, &name);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to parse DNS server address: %s", *p);
 
-                        r = sd_bus_message_open_container(req, 'r', "iay");
+                        if (ifindex != 0 && ifindex != arg_ifindex)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid ifindex: %i", ifindex);
+
+                        r = sd_bus_message_open_container(req, 'r', extended ? "iayqs" : "iay");
                         if (r < 0)
                                 return bus_log_create_error(r);
 
@@ -1966,6 +1949,16 @@ static int call_dns(sd_bus *bus, char **dns, const char *destination, const char
                         if (r < 0)
                                 return bus_log_create_error(r);
 
+                        if (extended) {
+                                r = sd_bus_message_append(req, "q", port);
+                                if (r < 0)
+                                        return bus_log_create_error(r);
+
+                                r = sd_bus_message_append(req, "s", name);
+                                if (r < 0)
+                                        return bus_log_create_error(r);
+                        }
+
                         r = sd_bus_message_close_container(req);
                         if (r < 0)
                                 return bus_log_create_error(r);
@@ -1975,7 +1968,12 @@ static int call_dns(sd_bus *bus, char **dns, const char *destination, const char
         if (r < 0)
                 return bus_log_create_error(r);
 
-        return sd_bus_call(bus, req, 0, error, NULL);
+        r = sd_bus_call(bus, req, 0, error, NULL);
+        if (r < 0 && extended && sd_bus_error_has_name(error, SD_BUS_ERROR_UNKNOWN_METHOD)) {
+                sd_bus_error_free(error);
+                return call_dns(bus, dns, locator, error, false);
+        }
+        return r;
 }
 
 static int verb_dns(int argc, char **argv, void *userdata) {
@@ -1997,19 +1995,11 @@ static int verb_dns(int argc, char **argv, void *userdata) {
         if (argc < 3)
                 return status_ifindex(bus, arg_ifindex, NULL, STATUS_DNS, NULL);
 
-        r = call_dns(bus, argv + 2,
-                     "org.freedesktop.resolve1",
-                     "/org/freedesktop/resolve1",
-                     "org.freedesktop.resolve1.Manager",
-                     &error);
+        r = call_dns(bus, argv + 2, bus_resolve_mgr, &error, true);
         if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
                 sd_bus_error_free(&error);
 
-                r = call_dns(bus, argv + 2,
-                             "org.freedesktop.network1",
-                             "/org/freedesktop/network1",
-                             "org.freedesktop.network1.Manager",
-                             &error);
+                r = call_dns(bus, argv + 2, bus_network_mgr, &error, true);
         }
         if (r < 0) {
                 if (arg_ifindex_permissive &&
@@ -2022,18 +2012,12 @@ static int verb_dns(int argc, char **argv, void *userdata) {
         return 0;
 }
 
-static int call_domain(sd_bus *bus, char **domain, const char *destination, const char *path, const char *interface, sd_bus_error *error) {
+static int call_domain(sd_bus *bus, char **domain, const BusLocator *locator, sd_bus_error *error) {
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL;
         char **p;
         int r;
 
-        r = sd_bus_message_new_method_call(
-                        bus,
-                        &req,
-                        destination,
-                        path,
-                        interface,
-                        "SetLinkDomains");
+        r = bus_message_new_method_call(bus, &req, locator, "SetLinkDomains");
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -2092,19 +2076,11 @@ static int verb_domain(int argc, char **argv, void *userdata) {
         if (argc < 3)
                 return status_ifindex(bus, arg_ifindex, NULL, STATUS_DOMAIN, NULL);
 
-        r = call_domain(bus, argv + 2,
-                        "org.freedesktop.resolve1",
-                        "/org/freedesktop/resolve1",
-                        "org.freedesktop.resolve1.Manager",
-                        &error);
+        r = call_domain(bus, argv + 2, bus_resolve_mgr, &error);
         if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
                 sd_bus_error_free(&error);
 
-                r = call_domain(bus, argv + 2,
-                                "org.freedesktop.network1",
-                                "/org/freedesktop/network1",
-                                "org.freedesktop.network1.Manager",
-                                &error);
+                r = call_domain(bus, argv + 2, bus_network_mgr, &error);
         }
         if (r < 0) {
                 if (arg_ifindex_permissive &&
@@ -2140,27 +2116,11 @@ static int verb_default_route(int argc, char **argv, void *userdata) {
         if (b < 0)
                 return log_error_errno(b, "Failed to parse boolean argument: %s", argv[2]);
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.resolve1",
-                        "/org/freedesktop/resolve1",
-                        "org.freedesktop.resolve1.Manager",
-                        "SetLinkDefaultRoute",
-                        &error,
-                        NULL,
-                        "ib", arg_ifindex, b);
+        r = bus_call_method(bus, bus_resolve_mgr, "SetLinkDefaultRoute", &error, NULL, "ib", arg_ifindex, b);
         if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
                 sd_bus_error_free(&error);
 
-                r = sd_bus_call_method(
-                                bus,
-                                "org.freedesktop.network1",
-                                "/org/freedesktop/network1",
-                                "org.freedesktop.network1.Manager",
-                                "SetLinkDefaultRoute",
-                                &error,
-                                NULL,
-                                "ib", arg_ifindex, b);
+                r = bus_call_method(bus, bus_network_mgr, "SetLinkDefaultRoute", &error, NULL, "ib", arg_ifindex, b);
         }
         if (r < 0) {
                 if (arg_ifindex_permissive &&
@@ -2192,27 +2152,11 @@ static int verb_llmnr(int argc, char **argv, void *userdata) {
         if (argc < 3)
                 return status_ifindex(bus, arg_ifindex, NULL, STATUS_LLMNR, NULL);
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.resolve1",
-                        "/org/freedesktop/resolve1",
-                        "org.freedesktop.resolve1.Manager",
-                        "SetLinkLLMNR",
-                        &error,
-                        NULL,
-                        "is", arg_ifindex, argv[2]);
+        r = bus_call_method(bus, bus_resolve_mgr, "SetLinkLLMNR", &error, NULL, "is", arg_ifindex, argv[2]);
         if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
                 sd_bus_error_free(&error);
 
-                r = sd_bus_call_method(
-                                bus,
-                                "org.freedesktop.network1",
-                                "/org/freedesktop/network1",
-                                "org.freedesktop.network1.Manager",
-                                "SetLinkLLMNR",
-                                &error,
-                                NULL,
-                                "is", arg_ifindex, argv[2]);
+                r = bus_call_method(bus, bus_network_mgr, "SetLinkLLMNR", &error, NULL, "is", arg_ifindex, argv[2]);
         }
         if (r < 0) {
                 if (arg_ifindex_permissive &&
@@ -2244,23 +2188,13 @@ static int verb_mdns(int argc, char **argv, void *userdata) {
         if (argc < 3)
                 return status_ifindex(bus, arg_ifindex, NULL, STATUS_MDNS, NULL);
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.resolve1",
-                        "/org/freedesktop/resolve1",
-                        "org.freedesktop.resolve1.Manager",
-                        "SetLinkMulticastDNS",
-                        &error,
-                        NULL,
-                        "is", arg_ifindex, argv[2]);
+        r = bus_call_method(bus, bus_resolve_mgr, "SetLinkMulticastDNS", &error, NULL, "is", arg_ifindex, argv[2]);
         if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
                 sd_bus_error_free(&error);
 
-                r = sd_bus_call_method(
+                r = bus_call_method(
                                 bus,
-                                "org.freedesktop.network1",
-                                "/org/freedesktop/network1",
-                                "org.freedesktop.network1.Manager",
+                                bus_network_mgr,
                                 "SetLinkMulticastDNS",
                                 &error,
                                 NULL,
@@ -2296,23 +2230,13 @@ static int verb_dns_over_tls(int argc, char **argv, void *userdata) {
         if (argc < 3)
                 return status_ifindex(bus, arg_ifindex, NULL, STATUS_PRIVATE, NULL);
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.resolve1",
-                        "/org/freedesktop/resolve1",
-                        "org.freedesktop.resolve1.Manager",
-                        "SetLinkDNSOverTLS",
-                        &error,
-                        NULL,
-                        "is", arg_ifindex, argv[2]);
+        r = bus_call_method(bus, bus_resolve_mgr, "SetLinkDNSOverTLS", &error, NULL, "is", arg_ifindex, argv[2]);
         if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
                 sd_bus_error_free(&error);
 
-                r = sd_bus_call_method(
+                r = bus_call_method(
                                 bus,
-                                "org.freedesktop.network1",
-                                "/org/freedesktop/network1",
-                                "org.freedesktop.network1.Manager",
+                                bus_network_mgr,
                                 "SetLinkDNSOverTLS",
                                 &error,
                                 NULL,
@@ -2348,27 +2272,11 @@ static int verb_dnssec(int argc, char **argv, void *userdata) {
         if (argc < 3)
                 return status_ifindex(bus, arg_ifindex, NULL, STATUS_DNSSEC, NULL);
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.resolve1",
-                        "/org/freedesktop/resolve1",
-                        "org.freedesktop.resolve1.Manager",
-                        "SetLinkDNSSEC",
-                        &error,
-                        NULL,
-                        "is", arg_ifindex, argv[2]);
+        r = bus_call_method(bus, bus_resolve_mgr, "SetLinkDNSSEC", &error, NULL, "is", arg_ifindex, argv[2]);
         if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
                 sd_bus_error_free(&error);
 
-                r = sd_bus_call_method(
-                                bus,
-                                "org.freedesktop.network1",
-                                "/org/freedesktop/network1",
-                                "org.freedesktop.network1.Manager",
-                                "SetLinkDNSSEC",
-                                &error,
-                                NULL,
-                                "is", arg_ifindex, argv[2]);
+                r = bus_call_method(bus, bus_network_mgr, "SetLinkDNSSEC", &error, NULL, "is", arg_ifindex, argv[2]);
         }
         if (r < 0) {
                 if (arg_ifindex_permissive &&
@@ -2381,17 +2289,11 @@ static int verb_dnssec(int argc, char **argv, void *userdata) {
         return 0;
 }
 
-static int call_nta(sd_bus *bus, char **nta, const char *destination, const char *path, const char *interface, sd_bus_error *error) {
+static int call_nta(sd_bus *bus, char **nta, const BusLocator *locator,  sd_bus_error *error) {
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL;
         int r;
 
-        r = sd_bus_message_new_method_call(
-                        bus,
-                        &req,
-                        destination,
-                        path,
-                        interface,
-                        "SetLinkDNSSECNegativeTrustAnchors");
+        r = bus_message_new_method_call(bus, &req, locator, "SetLinkDNSSECNegativeTrustAnchors");
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -2442,19 +2344,11 @@ static int verb_nta(int argc, char **argv, void *userdata) {
                         }
                 }
 
-        r = call_nta(bus, clear ? NULL : argv + 2,
-                     "org.freedesktop.resolve1",
-                     "/org/freedesktop/resolve1",
-                     "org.freedesktop.resolve1.Manager",
-                     &error);
+        r = call_nta(bus, clear ? NULL : argv + 2, bus_resolve_mgr, &error);
         if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
                 sd_bus_error_free(&error);
 
-                r = call_nta(bus, clear ? NULL : argv + 2,
-                             "org.freedesktop.network1",
-                             "/org/freedesktop/network1",
-                             "org.freedesktop.network1.Manager",
-                             &error);
+                r = call_nta(bus, clear ? NULL : argv + 2, bus_network_mgr, &error);
         }
         if (r < 0) {
                 if (arg_ifindex_permissive &&
@@ -2483,27 +2377,11 @@ static int verb_revert_link(int argc, char **argv, void *userdata) {
         if (arg_ifindex <= 0)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Interface argument required.");
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.resolve1",
-                        "/org/freedesktop/resolve1",
-                        "org.freedesktop.resolve1.Manager",
-                        "RevertLink",
-                        &error,
-                        NULL,
-                "i", arg_ifindex);
+        r = bus_call_method(bus, bus_resolve_mgr, "RevertLink", &error, NULL, "i", arg_ifindex);
         if (r < 0 && sd_bus_error_has_name(&error, BUS_ERROR_LINK_BUSY)) {
                 sd_bus_error_free(&error);
 
-                r = sd_bus_call_method(
-                                bus,
-                                "org.freedesktop.network1",
-                                "/org/freedesktop/network1",
-                                "org.freedesktop.network1.Manager",
-                                "RevertLinkDNS",
-                                &error,
-                                NULL,
-                                "i", arg_ifindex);
+                r = bus_call_method(bus, bus_network_mgr, "RevertLinkDNS", &error, NULL, "i", arg_ifindex);
         }
         if (r < 0) {
                 if (arg_ifindex_permissive &&
@@ -2516,6 +2394,48 @@ static int verb_revert_link(int argc, char **argv, void *userdata) {
         return 0;
 }
 
+static int verb_log_level(int argc, char *argv[], void *userdata) {
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        sd_bus *bus = userdata;
+        int r;
+
+        assert(bus);
+
+        if (argc == 1) {
+                _cleanup_free_ char *level = NULL;
+
+                r = sd_bus_get_property_string(
+                                bus,
+                                "org.freedesktop.resolve1",
+                                "/org/freedesktop/LogControl1",
+                                "org.freedesktop.LogControl1",
+                                "LogLevel",
+                                &error,
+                                &level);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to get log level: %s", bus_error_message(&error, r));
+
+                puts(level);
+
+        } else {
+                assert(argc == 2);
+
+                r = sd_bus_set_property(
+                                bus,
+                                "org.freedesktop.resolve1",
+                                "/org/freedesktop/LogControl1",
+                                "org.freedesktop.LogControl1",
+                                "LogLevel",
+                                &error,
+                                "s",
+                                argv[1]);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to set log level: %s", bus_error_message(&error, r));
+        }
+
+        return 0;
+}
+
 static void help_protocol_types(void) {
         if (arg_legend)
                 puts("Known protocol types:");
@@ -3190,6 +3110,7 @@ static int native_main(int argc, char *argv[], sd_bus *bus) {
                 { "dnssec",                VERB_ANY, 3,        0,            verb_dnssec           },
                 { "nta",                   VERB_ANY, VERB_ANY, 0,            verb_nta              },
                 { "revert",                VERB_ANY, 2,        0,            verb_revert_link      },
+                { "log-level",             VERB_ANY, 2,        0,            verb_log_level        },
                 {}
         };
 
@@ -3198,7 +3119,7 @@ static int native_main(int argc, char *argv[], sd_bus *bus) {
 
 static int translate(const char *verb, const char *single_arg, size_t num_args, char **args, sd_bus *bus) {
         char **fake, **p;
-        size_t num, i;
+        size_t num;
 
         assert(verb);
         assert(num_args == 0 || args);
@@ -3209,7 +3130,7 @@ static int translate(const char *verb, const char *single_arg, size_t num_args,
         *p++ = (char *) verb;
         if (single_arg)
                 *p++ = (char *) single_arg;
-        for (i = 0; i < num_args; i++)
+        for (size_t i = 0; i < num_args; i++)
                 *p++ = args[i];
 
         optind = 0;
@@ -3312,9 +3233,7 @@ static int run(int argc, char **argv) {
         int r;
 
         setlocale(LC_ALL, "");
-        log_show_color(true);
-        log_parse_environment();
-        log_open();
+        log_setup_cli();
 
         if (streq(program_invocation_short_name, "resolvconf"))
                 r = resolvconf_parse_argv(argc, argv);
index ffe61bdb9f5f85fe1d0645b926c380ebff65462b..dba1639a11cf9ed6803625b868e1cdbb14876e96 100644 (file)
@@ -2,8 +2,10 @@
 
 #include "alloc-util.h"
 #include "bus-common-errors.h"
+#include "bus-get-properties.h"
+#include "bus-log-control-api.h"
+#include "bus-message-util.h"
 #include "bus-polkit.h"
-#include "bus-util.h"
 #include "dns-domain.h"
 #include "memory-util.h"
 #include "missing_capability.h"
@@ -16,6 +18,7 @@
 #include "socket-netlink.h"
 #include "stdio-util.h"
 #include "strv.h"
+#include "syslog-util.h"
 #include "user-util.h"
 #include "utf8.h"
 
@@ -452,11 +455,10 @@ finish:
 static int bus_method_resolve_address(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL;
         Manager *m = userdata;
+        union in_addr_union a;
         int family, ifindex;
         uint64_t flags;
-        const void *d;
         DnsQuery *q;
-        size_t sz;
         int r;
 
         assert(message);
@@ -464,20 +466,14 @@ static int bus_method_resolve_address(sd_bus_message *message, void *userdata, s
 
         assert_cc(sizeof(int) == sizeof(int32_t));
 
-        r = sd_bus_message_read(message, "ii", &ifindex, &family);
+        r = sd_bus_message_read(message, "i", &ifindex);
         if (r < 0)
                 return r;
 
-        if (!IN_SET(family, AF_INET, AF_INET6))
-                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
-
-        r = sd_bus_message_read_array(message, 'y', &d, &sz);
+        r = bus_message_read_in_addr_auto(message, error, &family, &a);
         if (r < 0)
                 return r;
 
-        if (sz != FAMILY_ADDRESS_SIZE(family))
-                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size");
-
         r = sd_bus_message_read(message, "t", &flags);
         if (r < 0)
                 return r;
@@ -486,7 +482,7 @@ static int bus_method_resolve_address(sd_bus_message *message, void *userdata, s
         if (r < 0)
                 return r;
 
-        r = dns_question_new_reverse(&question, family, d);
+        r = dns_question_new_reverse(&question, family, &a);
         if (r < 0)
                 return r;
 
@@ -496,7 +492,7 @@ static int bus_method_resolve_address(sd_bus_message *message, void *userdata, s
 
         q->request = sd_bus_message_ref(message);
         q->request_family = family;
-        memcpy(&q->request_address, d, sz);
+        q->request_address = a;
         q->complete = bus_method_resolve_address_complete;
 
         r = dns_query_bus_track(q, message);
@@ -1114,7 +1110,7 @@ static void bus_method_resolve_service_complete(DnsQuery *q) {
 
         if (has_root_domain && found <= 0) {
                 /* If there's exactly one SRV RR and it uses
-                 * the root domain as host name, then the
+                 * the root domain as hostname, then the
                  * service is explicitly not offered on the
                  * domain. Report this as a recognizable
                  * error. See RFC 2782, Section "Usage
@@ -1216,19 +1212,26 @@ fail:
         return r;
 }
 
-int bus_dns_server_append(sd_bus_message *reply, DnsServer *s, bool with_ifindex) {
+int bus_dns_server_append(sd_bus_message *reply, DnsServer *s, bool with_ifindex, bool extended) {
         int r;
 
         assert(reply);
 
         if (!s) {
-                if (with_ifindex)
-                        return sd_bus_message_append(reply, "(iiay)", 0, AF_UNSPEC, 0);
-                else
-                        return sd_bus_message_append(reply, "(iay)", AF_UNSPEC, 0);
+                if (with_ifindex) {
+                        if (extended)
+                                return sd_bus_message_append(reply, "(iiayqs)", 0, AF_UNSPEC, 0, 0, NULL);
+                        else
+                                return sd_bus_message_append(reply, "(iiay)", 0, AF_UNSPEC, 0);
+                } else {
+                        if (extended)
+                                return sd_bus_message_append(reply, "(iayqs)", AF_UNSPEC, 0, 0, NULL);
+                        else
+                                return sd_bus_message_append(reply, "(iay)", AF_UNSPEC, 0);
+                }
         }
 
-        r = sd_bus_message_open_container(reply, 'r', with_ifindex ? "iiay" : "iay");
+        r = sd_bus_message_open_container(reply, 'r', with_ifindex ? (extended ? "iiayqs" : "iiay") : (extended ? "iayqs" : "iay"));
         if (r < 0)
                 return r;
 
@@ -1246,17 +1249,28 @@ int bus_dns_server_append(sd_bus_message *reply, DnsServer *s, bool with_ifindex
         if (r < 0)
                 return r;
 
+        if (extended) {
+                r = sd_bus_message_append(reply, "q", s->port);
+                if (r < 0)
+                        return r;
+
+                r = sd_bus_message_append(reply, "s", s->server_name);
+                if (r < 0)
+                        return r;
+        }
+
         return sd_bus_message_close_container(reply);
 }
 
-static int bus_property_get_dns_servers(
+static int bus_property_get_dns_servers_internal(
                 sd_bus *bus,
                 const char *path,
                 const char *interface,
                 const char *property,
                 sd_bus_message *reply,
                 void *userdata,
-                sd_bus_error *error) {
+                sd_bus_error *error,
+                bool extended) {
 
         Manager *m = userdata;
         DnsServer *s;
@@ -1267,19 +1281,19 @@ static int bus_property_get_dns_servers(
         assert(reply);
         assert(m);
 
-        r = sd_bus_message_open_container(reply, 'a', "(iiay)");
+        r = sd_bus_message_open_container(reply, 'a', extended ? "(iiayqs)" : "(iiay)");
         if (r < 0)
                 return r;
 
         LIST_FOREACH(servers, s, m->dns_servers) {
-                r = bus_dns_server_append(reply, s, true);
+                r = bus_dns_server_append(reply, s, true, extended);
                 if (r < 0)
                         return r;
         }
 
         HASHMAP_FOREACH(l, m->links, i)
                 LIST_FOREACH(servers, s, l->dns_servers) {
-                        r = bus_dns_server_append(reply, s, true);
+                        r = bus_dns_server_append(reply, s, true, extended);
                         if (r < 0)
                                 return r;
                 }
@@ -1287,7 +1301,18 @@ static int bus_property_get_dns_servers(
         return sd_bus_message_close_container(reply);
 }
 
-static int bus_property_get_fallback_dns_servers(
+static int bus_property_get_dns_servers(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+        return bus_property_get_dns_servers_internal(bus, path, interface, property, reply, userdata, error, false);
+}
+
+static int bus_property_get_dns_servers_ex(
                 sd_bus *bus,
                 const char *path,
                 const char *interface,
@@ -1295,6 +1320,18 @@ static int bus_property_get_fallback_dns_servers(
                 sd_bus_message *reply,
                 void *userdata,
                 sd_bus_error *error) {
+        return bus_property_get_dns_servers_internal(bus, path, interface, property, reply, userdata, error, true);
+}
+
+static int bus_property_get_fallback_dns_servers_internal(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error,
+                bool extended) {
 
         DnsServer *s, **f = userdata;
         int r;
@@ -1302,12 +1339,12 @@ static int bus_property_get_fallback_dns_servers(
         assert(reply);
         assert(f);
 
-        r = sd_bus_message_open_container(reply, 'a', "(iiay)");
+        r = sd_bus_message_open_container(reply, 'a', extended ? "(iiayqs)" : "(iiay)");
         if (r < 0)
                 return r;
 
         LIST_FOREACH(servers, s, *f) {
-                r = bus_dns_server_append(reply, s, true);
+                r = bus_dns_server_append(reply, s, true, extended);
                 if (r < 0)
                         return r;
         }
@@ -1315,7 +1352,18 @@ static int bus_property_get_fallback_dns_servers(
         return sd_bus_message_close_container(reply);
 }
 
-static int bus_property_get_current_dns_server(
+static int bus_property_get_fallback_dns_servers(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+        return bus_property_get_fallback_dns_servers_internal(bus, path, interface, property, reply, userdata, error, false);
+}
+
+static int bus_property_get_fallback_dns_servers_ex(
                 sd_bus *bus,
                 const char *path,
                 const char *interface,
@@ -1323,6 +1371,18 @@ static int bus_property_get_current_dns_server(
                 sd_bus_message *reply,
                 void *userdata,
                 sd_bus_error *error) {
+        return bus_property_get_fallback_dns_servers_internal(bus, path, interface, property, reply, userdata, error, true);
+}
+
+static int bus_property_get_current_dns_server_internal(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error,
+                bool extended) {
 
         DnsServer *s;
 
@@ -1331,7 +1391,29 @@ static int bus_property_get_current_dns_server(
 
         s = *(DnsServer **) userdata;
 
-        return bus_dns_server_append(reply, s, true);
+        return bus_dns_server_append(reply, s, true, extended);
+}
+
+static int bus_property_get_current_dns_server(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+        return bus_property_get_current_dns_server_internal(bus, path, interface, property, reply, userdata, error, false);
+}
+
+static int bus_property_get_current_dns_server_ex(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+        return bus_property_get_current_dns_server_internal(bus, path, interface, property, reply, userdata, error, true);
 }
 
 static int bus_property_get_domains(
@@ -1495,9 +1577,6 @@ static int get_any_link(Manager *m, int ifindex, Link **ret, sd_bus_error *error
         assert(m);
         assert(ret);
 
-        if (ifindex <= 0)
-                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
-
         l = hashmap_get(m->links, INT_TO_PTR(ifindex));
         if (!l)
                 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_LINK, "Link %i not known", ifindex);
@@ -1514,8 +1593,7 @@ static int call_link_method(Manager *m, sd_bus_message *message, sd_bus_message_
         assert(message);
         assert(handler);
 
-        assert_cc(sizeof(int) == sizeof(int32_t));
-        r = sd_bus_message_read(message, "i", &ifindex);
+        r = bus_message_read_ifindex(message, error, &ifindex);
         if (r < 0)
                 return r;
 
@@ -1530,6 +1608,10 @@ static int bus_method_set_link_dns_servers(sd_bus_message *message, void *userda
         return call_link_method(userdata, message, bus_link_method_set_dns_servers, error);
 }
 
+static int bus_method_set_link_dns_servers_ex(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        return call_link_method(userdata, message, bus_link_method_set_dns_servers_ex, error);
+}
+
 static int bus_method_set_link_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         return call_link_method(userdata, message, bus_link_method_set_domains, error);
 }
@@ -1571,8 +1653,7 @@ static int bus_method_get_link(sd_bus_message *message, void *userdata, sd_bus_e
         assert(message);
         assert(m);
 
-        assert_cc(sizeof(int) == sizeof(int32_t));
-        r = sd_bus_message_read(message, "i", &ifindex);
+        r = bus_message_read_ifindex(message, error, &ifindex);
         if (r < 0)
                 return r;
 
@@ -1842,8 +1923,11 @@ static const sd_bus_vtable resolve_vtable[] = {
         SD_BUS_PROPERTY("MulticastDNS", "s", bus_property_get_resolve_support, offsetof(Manager, mdns_support), 0),
         SD_BUS_PROPERTY("DNSOverTLS", "s", bus_property_get_dns_over_tls_mode, 0, 0),
         SD_BUS_PROPERTY("DNS", "a(iiay)", bus_property_get_dns_servers, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("DNSEx", "a(iiayqs)", bus_property_get_dns_servers_ex, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("FallbackDNS", "a(iiay)", bus_property_get_fallback_dns_servers, offsetof(Manager, fallback_dns_servers), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("FallbackDNSEx", "a(iiayqs)", bus_property_get_fallback_dns_servers_ex, offsetof(Manager, fallback_dns_servers), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("CurrentDNSServer", "(iiay)", bus_property_get_current_dns_server, offsetof(Manager, current_dns_server), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+        SD_BUS_PROPERTY("CurrentDNSServerEx", "(iiayqs)", bus_property_get_current_dns_server_ex, offsetof(Manager, current_dns_server), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("Domains", "a(isb)", bus_property_get_domains, 0, 0),
         SD_BUS_PROPERTY("TransactionStatistics", "(tt)", bus_property_get_transaction_statistics, 0, 0),
         SD_BUS_PROPERTY("CacheStatistics", "(ttt)", bus_property_get_cache_statistics, 0, 0),
@@ -1853,29 +1937,134 @@ static const sd_bus_vtable resolve_vtable[] = {
         SD_BUS_PROPERTY("DNSSECNegativeTrustAnchors", "as", bus_property_get_ntas, 0, 0),
         SD_BUS_PROPERTY("DNSStubListener", "s", bus_property_get_dns_stub_listener_mode, offsetof(Manager, dns_stub_listener_mode), 0),
 
-        SD_BUS_METHOD("ResolveHostname", "isit", "a(iiay)st", bus_method_resolve_hostname, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("ResolveAddress", "iiayt", "a(is)t", bus_method_resolve_address, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("ResolveRecord", "isqqt", "a(iqqay)t", bus_method_resolve_record, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("ResolveService", "isssit", "a(qqqsa(iiay)s)aayssst", bus_method_resolve_service, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("ResetStatistics", NULL, NULL, bus_method_reset_statistics, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("FlushCaches", NULL, NULL, bus_method_flush_caches, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("ResetServerFeatures", NULL, NULL, bus_method_reset_server_features, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("GetLink", "i", "o", bus_method_get_link, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetLinkDNS", "ia(iay)", NULL, bus_method_set_link_dns_servers, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetLinkDomains", "ia(sb)", NULL, bus_method_set_link_domains, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetLinkDefaultRoute", "ib", NULL, bus_method_set_link_default_route, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetLinkLLMNR", "is", NULL, bus_method_set_link_llmnr, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetLinkMulticastDNS", "is", NULL, bus_method_set_link_mdns, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetLinkDNSOverTLS", "is", NULL, bus_method_set_link_dns_over_tls, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetLinkDNSSEC", "is", NULL, bus_method_set_link_dnssec, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetLinkDNSSECNegativeTrustAnchors", "ias", NULL, bus_method_set_link_dnssec_negative_trust_anchors, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("RevertLink", "i", NULL, bus_method_revert_link, SD_BUS_VTABLE_UNPRIVILEGED),
-
-        SD_BUS_METHOD("RegisterService", "sssqqqaa{say}", "o", bus_method_register_service, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("UnregisterService", "o", NULL, bus_method_unregister_service, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_ARGS("ResolveHostname",
+                                SD_BUS_ARGS("i", ifindex, "s", name, "i", family, "t", flags),
+                                SD_BUS_RESULT("a(iiay)", addresses, "s", canonical, "t", flags),
+                                bus_method_resolve_hostname,
+                                SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_ARGS("ResolveAddress",
+                                SD_BUS_ARGS("i",  ifindex, "i", family, "ay", address, "t", flags),
+                                SD_BUS_RESULT("a(is)", names, "t", flags),
+                                bus_method_resolve_address,
+                                SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_ARGS("ResolveRecord",
+                                SD_BUS_ARGS("i", ifindex, "s", name, "q", class, "q", type, "t", flags),
+                                SD_BUS_RESULT("a(iqqay)", records, "t", flags),
+                                bus_method_resolve_record,
+                                SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_ARGS("ResolveService",
+                                SD_BUS_ARGS("i", ifindex,
+                                            "s", name,
+                                            "s", type,
+                                            "s", domain,
+                                            "i", family,
+                                            "t", flags),
+                                SD_BUS_RESULT("a(qqqsa(iiay)s)", srv_data,
+                                              "aay", txt_data,
+                                              "s", canonical_name,
+                                              "s", canonical_type,
+                                              "s", canonical_domain,
+                                              "t", flags),
+                                bus_method_resolve_service,
+                                SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_ARGS("GetLink",
+                                SD_BUS_ARGS("i", ifindex),
+                                SD_BUS_RESULT("o", path),
+                                bus_method_get_link,
+                                SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_ARGS("SetLinkDNS",
+                                SD_BUS_ARGS("i", ifindex, "a(iay)", addresses),
+                                SD_BUS_NO_RESULT,
+                                bus_method_set_link_dns_servers,
+                                SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_ARGS("SetLinkDNSEx",
+                                SD_BUS_ARGS("i", ifindex, "a(iayqs)", addresses),
+                                SD_BUS_NO_RESULT,
+                                bus_method_set_link_dns_servers_ex,
+                                SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_ARGS("SetLinkDomains",
+                                SD_BUS_ARGS("i", ifindex, "a(sb)", domains),
+                                SD_BUS_NO_RESULT,
+                                bus_method_set_link_domains,
+                                SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_ARGS("SetLinkDefaultRoute",
+                                SD_BUS_ARGS("i", ifindex, "b", enable),
+                                SD_BUS_NO_RESULT,
+                                bus_method_set_link_default_route,
+                                SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_ARGS("SetLinkLLMNR",
+                                SD_BUS_ARGS("i", ifindex, "s", mode),
+                                SD_BUS_NO_RESULT,
+                                bus_method_set_link_llmnr,
+                                SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_ARGS("SetLinkMulticastDNS",
+                                SD_BUS_ARGS("i", ifindex, "s", mode),
+                                SD_BUS_NO_RESULT,
+                                bus_method_set_link_mdns,
+                                SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_ARGS("SetLinkDNSOverTLS",
+                                SD_BUS_ARGS("i", ifindex, "s", mode),
+                                SD_BUS_NO_RESULT,
+                                bus_method_set_link_dns_over_tls,
+                                SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_ARGS("SetLinkDNSSEC",
+                                SD_BUS_ARGS("i", ifindex, "s", mode),
+                                SD_BUS_NO_RESULT,
+                                bus_method_set_link_dnssec,
+                                SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_ARGS("SetLinkDNSSECNegativeTrustAnchors",
+                                SD_BUS_ARGS("i", ifindex, "as", names),
+                                SD_BUS_NO_RESULT,
+                                bus_method_set_link_dnssec_negative_trust_anchors,
+                                SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_ARGS("RevertLink",
+                                SD_BUS_ARGS("i", ifindex),
+                                SD_BUS_NO_RESULT,
+                                bus_method_revert_link,
+                                SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_ARGS("RegisterService",
+                                SD_BUS_ARGS("s", name,
+                                            "s", name_template,
+                                            "s", type,
+                                            "q", service_port,
+                                            "q", service_priority,
+                                            "q", service_weight,
+                                            "aa{say}", txt_datas),
+                                SD_BUS_RESULT("o", service_path),
+                                bus_method_register_service,
+                                SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_ARGS("UnregisterService",
+                                SD_BUS_ARGS("o", service_path),
+                                SD_BUS_NO_RESULT,
+                                bus_method_unregister_service,
+                                SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_ARGS("ResetStatistics",
+                                SD_BUS_NO_ARGS,
+                                SD_BUS_NO_RESULT,
+                                bus_method_reset_statistics,
+                                SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_ARGS("FlushCaches",
+                                SD_BUS_NO_ARGS,
+                                SD_BUS_NO_RESULT,
+                                bus_method_flush_caches,
+                                SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_ARGS("ResetServerFeatures",
+                                SD_BUS_NO_ARGS,
+                                SD_BUS_NO_RESULT,
+                                bus_method_reset_server_features,
+                                SD_BUS_VTABLE_UNPRIVILEGED),
+
         SD_BUS_VTABLE_END,
 };
 
+const BusObjectImplementation manager_object = {
+        "/org/freedesktop/resolve1",
+        "org.freedesktop.resolve1.Manager",
+        .vtables = BUS_VTABLES(resolve_vtable),
+        .children = BUS_IMPLEMENTATIONS(&link_object,
+                                        &dnssd_object),
+};
+
 static int match_prepare_for_sleep(sd_bus_message *message, void *userdata, sd_bus_error *ret_error) {
         Manager *m = userdata;
         int b, r;
@@ -1885,7 +2074,7 @@ static int match_prepare_for_sleep(sd_bus_message *message, void *userdata, sd_b
 
         r = sd_bus_message_read(message, "b", &b);
         if (r < 0) {
-                log_debug_errno(r, "Failed to parse PrepareForSleep signal: %m");
+                bus_log_parse_error(r);
                 return 0;
         }
 
@@ -1910,25 +2099,13 @@ int manager_connect_bus(Manager *m) {
         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);
+        r = bus_add_implementation(m->bus, &manager_object, m);
         if (r < 0)
-                return log_error_errno(r, "Failed to register object: %m");
-
-        r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/resolve1/link", "org.freedesktop.resolve1.Link", link_vtable, link_object_find, m);
-        if (r < 0)
-                return log_error_errno(r, "Failed to register link objects: %m");
-
-        r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/resolve1/link", link_node_enumerator, m);
-        if (r < 0)
-                return log_error_errno(r, "Failed to register link enumerator: %m");
-
-        r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/resolve1/dnssd", "org.freedesktop.resolve1.DnssdService", dnssd_vtable, dnssd_object_find, m);
-        if (r < 0)
-                return log_error_errno(r, "Failed to register dnssd objects: %m");
+                return r;
 
-        r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/resolve1/dnssd", dnssd_node_enumerator, m);
+        r = bus_log_control_api_register(m->bus);
         if (r < 0)
-                return log_error_errno(r, "Failed to register dnssd enumerator: %m");
+                return r;
 
         r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.resolve1", 0, NULL, NULL);
         if (r < 0)
index a499f76ad573ecce82d9475dba97152eb6a9fd8d..28caa64a6b5abae77bfba6d6da1a2f2423f11497 100644 (file)
@@ -1,12 +1,15 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
+#include "bus-object.h"
 #include "resolved-manager.h"
 
+extern const BusObjectImplementation manager_object;
+
 int manager_connect_bus(Manager *m);
 int _manager_send_changed(Manager *manager, const char *property, ...) _sentinel_;
 #define manager_send_changed(manager, ...) _manager_send_changed(manager, __VA_ARGS__, NULL)
-int bus_dns_server_append(sd_bus_message *reply, DnsServer *s, bool with_ifindex);
+int bus_dns_server_append(sd_bus_message *reply, DnsServer *s, bool with_ifindex, bool extended);
 int bus_property_get_resolve_support(sd_bus *bus, const char *path, const char *interface,
                                      const char *property, sd_bus_message *reply,
                                      void *userdata, sd_bus_error *error);
index ca5b8e7918391c782eb2a3fd31c675f9943b3910..6b99271245716d25f6f46ae02d43de4e92bb341a 100644 (file)
@@ -8,7 +8,10 @@
 #include "parse-util.h"
 #include "resolved-conf.h"
 #include "resolved-dnssd.h"
-#include "resolved-util.h"
+#include "resolved-manager.h"
+#include "resolved-dns-search-domain.h"
+#include "dns-domain.h"
+#include "socket-netlink.h"
 #include "specifier.h"
 #include "string-table.h"
 #include "string-util.h"
@@ -25,24 +28,33 @@ static const char* const dns_stub_listener_mode_table[_DNS_STUB_LISTENER_MODE_MA
 DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dns_stub_listener_mode, DnsStubListenerMode, DNS_STUB_LISTENER_YES);
 
 static int manager_add_dns_server_by_string(Manager *m, DnsServerType type, const char *word) {
+        _cleanup_free_ char *server_name = NULL;
         union in_addr_union address;
         int family, r, ifindex = 0;
+        uint16_t port;
         DnsServer *s;
-        _cleanup_free_ char *server_name = NULL;
 
         assert(m);
         assert(word);
 
-        r = in_addr_ifindex_name_from_string_auto(word, &family, &address, &ifindex, &server_name);
+        r = in_addr_port_ifindex_name_from_string_auto(word, &family, &address, &port, &ifindex, &server_name);
         if (r < 0)
                 return r;
 
+        if (IN_SET(port, 53, 853))
+                port = 0;
+
         /* Silently filter out 0.0.0.0 and 127.0.0.53 (our own stub DNS listener) */
         if (!dns_server_address_valid(family, &address))
                 return 0;
 
+        /* By default, the port number is determined with the transaction feature level.
+         * See dns_transaction_port() and dns_server_port(). */
+        if (IN_SET(port, 53, 853))
+                port = 0;
+
         /* Filter out duplicates */
-        s = dns_server_find(manager_get_first_dns_server(m, type), family, &address, ifindex);
+        s = dns_server_find(manager_get_first_dns_server(m, type), family, &address, port, ifindex, server_name);
         if (s) {
                 /*
                  * Drop the marker. This is used to find the servers
@@ -54,7 +66,7 @@ static int manager_add_dns_server_by_string(Manager *m, DnsServerType type, cons
                 return 0;
         }
 
-        return dns_server_new(m, NULL, type, NULL, family, &address, ifindex, server_name);
+        return dns_server_new(m, NULL, type, NULL, family, &address, port, ifindex, server_name);
 }
 
 int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, const char *string) {
@@ -218,10 +230,15 @@ int config_parse_search_domains(
 
 int config_parse_dnssd_service_name(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) {
         static const Specifier specifier_table[] = {
+                { 'm', specifier_machine_id,      NULL },
                 { 'b', specifier_boot_id,         NULL },
                 { 'H', specifier_host_name,       NULL },
-                { 'm', specifier_machine_id,      NULL },
                 { 'v', specifier_kernel_release,  NULL },
+                { 'a', specifier_architecture,    NULL },
+                { 'o', specifier_os_id,           NULL },
+                { 'w', specifier_os_version_id,   NULL },
+                { 'B', specifier_os_build_id,     NULL },
+                { 'W', specifier_os_variant_id,   NULL },
                 {}
         };
         DnssdService *s = userdata;
@@ -373,11 +390,14 @@ int manager_parse_config_file(Manager *m) {
 
         assert(m);
 
-        r = config_parse_many_nulstr(PKGSYSCONFDIR "/resolved.conf",
-                                     CONF_PATHS_NULSTR("systemd/resolved.conf.d"),
-                                     "Resolve\0",
-                                     config_item_perf_lookup, resolved_gperf_lookup,
-                                     CONFIG_PARSE_WARN, m);
+        r = config_parse_many_nulstr(
+                        PKGSYSCONFDIR "/resolved.conf",
+                        CONF_PATHS_NULSTR("systemd/resolved.conf.d"),
+                        "Resolve\0",
+                        config_item_perf_lookup, resolved_gperf_lookup,
+                        CONFIG_PARSE_WARN,
+                        m,
+                        NULL);
         if (r < 0)
                 return r;
 
index 4713df587ba0ecc8ba4163b20ffa268b7d6c4c4b..ac3937cfaeec8e6053b062af12fa19dd61923a19 100644 (file)
@@ -14,7 +14,6 @@ enum DnsStubListenerMode {
         _DNS_STUB_LISTENER_MODE_INVALID = -1
 };
 
-#include "resolved-manager.h"
 #include "resolved-dns-server.h"
 
 int manager_parse_config_file(Manager *m);
index ec9bfd7ad3b265f4cae399d6d71cf675a4a1d056..5a4f5c58b6b354e399022e84b6a7f4d3a560333b 100644 (file)
@@ -59,55 +59,6 @@ uint16_t dnssec_keytag(DnsResourceRecord *dnskey, bool mask_revoke) {
         return sum & UINT32_C(0xFFFF);
 }
 
-int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) {
-        size_t c = 0;
-        int r;
-
-        /* Converts the specified hostname into DNSSEC canonicalized
-         * form. */
-
-        if (buffer_max < 2)
-                return -ENOBUFS;
-
-        for (;;) {
-                r = dns_label_unescape(&n, buffer, buffer_max, 0);
-                if (r < 0)
-                        return r;
-                if (r == 0)
-                        break;
-
-                if (buffer_max < (size_t) r + 2)
-                        return -ENOBUFS;
-
-                /* The DNSSEC canonical form is not clear on what to
-                 * do with dots appearing in labels, the way DNS-SD
-                 * does it. Refuse it for now. */
-
-                if (memchr(buffer, '.', r))
-                        return -EINVAL;
-
-                ascii_strlower_n(buffer, (size_t) r);
-                buffer[r] = '.';
-
-                buffer += r + 1;
-                c += r + 1;
-
-                buffer_max -= r + 1;
-        }
-
-        if (c <= 0) {
-                /* Not even a single label: this is the root domain name */
-
-                assert(buffer_max > 2);
-                buffer[0] = '.';
-                buffer[1] = 0;
-
-                return 1;
-        }
-
-        return (int) c;
-}
-
 #if HAVE_GCRYPT
 
 static int rr_compare(DnsResourceRecord * const *a, DnsResourceRecord * const *b) {
index dfee7232c052bf1b220221fe6abace422d6d9c21..1f70861cd064e712431da459e0a40e462b790a81 100644 (file)
@@ -58,8 +58,6 @@ int dnssec_has_rrsig(DnsAnswer *a, const DnsResourceKey *key);
 
 uint16_t dnssec_keytag(DnsResourceRecord *dnskey, bool mask_revoke);
 
-int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max);
-
 int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret);
 
 typedef enum DnssecNsecResult {
index 05aaa0fb7f698dc0a0ff6250a11f1fb3ebfffbfe..63ede724734433921d49fe4f7cd1ccce52cce10d 100644 (file)
@@ -839,7 +839,7 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, const DnsAns
 
         rds = p->size - saved_size;
 
-        switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) {
+        switch (rr->unparsable ? _DNS_TYPE_INVALID : rr->key->type) {
 
         case DNS_TYPE_SRV:
                 r = dns_packet_append_uint16(p, rr->srv.priority, NULL);
@@ -856,14 +856,14 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, const DnsAns
 
                 /* RFC 2782 states "Unless and until permitted by future standards
                  * action, name compression is not to be used for this field." */
-                r = dns_packet_append_name(p, rr->srv.name, false, false, NULL);
+                r = dns_packet_append_name(p, rr->srv.name, false, true, NULL);
                 break;
 
         case DNS_TYPE_PTR:
         case DNS_TYPE_NS:
         case DNS_TYPE_CNAME:
         case DNS_TYPE_DNAME:
-                r = dns_packet_append_name(p, rr->ptr.name, true, false, NULL);
+                r = dns_packet_append_name(p, rr->ptr.name, true, true, NULL);
                 break;
 
         case DNS_TYPE_HINFO:
@@ -906,11 +906,11 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, const DnsAns
                 break;
 
         case DNS_TYPE_SOA:
-                r = dns_packet_append_name(p, rr->soa.mname, true, false, NULL);
+                r = dns_packet_append_name(p, rr->soa.mname, true, true, NULL);
                 if (r < 0)
                         goto fail;
 
-                r = dns_packet_append_name(p, rr->soa.rname, true, false, NULL);
+                r = dns_packet_append_name(p, rr->soa.rname, true, true, NULL);
                 if (r < 0)
                         goto fail;
 
@@ -938,7 +938,7 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, const DnsAns
                 if (r < 0)
                         goto fail;
 
-                r = dns_packet_append_name(p, rr->mx.exchange, true, false, NULL);
+                r = dns_packet_append_name(p, rr->mx.exchange, true, true, NULL);
                 break;
 
         case DNS_TYPE_LOC:
@@ -1125,7 +1125,7 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, const DnsAns
 
         case DNS_TYPE_OPT:
         case DNS_TYPE_OPENPGPKEY:
-        case _DNS_TYPE_INVALID: /* unparseable */
+        case _DNS_TYPE_INVALID: /* unparsable */
         default:
 
                 r = dns_packet_append_blob(p, rr->generic.data, rr->generic.data_size, NULL);
@@ -1815,8 +1815,8 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
                         break;
                 } else {
                         dns_packet_rewind(p, pos);
-                        rr->unparseable = true;
-                        goto unparseable;
+                        rr->unparsable = true;
+                        goto unparsable;
                 }
         }
 
@@ -2059,7 +2059,7 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
         case DNS_TYPE_OPT: /* we only care about the header of OPT for now. */
         case DNS_TYPE_OPENPGPKEY:
         default:
-        unparseable:
+        unparsable:
                 r = dns_packet_read_memdup(p, rdlength, &rr->generic.data, &rr->generic.data_size, NULL);
 
                 break;
index d6eca6dfdd75d1bf93901ac793e281bedff9a380..906158c5cedba892e6503d7c92ac0662da4e3669 100644 (file)
@@ -94,7 +94,7 @@ static int dns_query_candidate_next_search_domain(DnsQueryCandidate *c) {
 }
 
 static int dns_query_candidate_add_transaction(DnsQueryCandidate *c, DnsResourceKey *key) {
-        DnsTransaction *t;
+        _cleanup_(dns_transaction_gcp) DnsTransaction *t = NULL;
         int r;
 
         assert(c);
@@ -105,39 +105,26 @@ static int dns_query_candidate_add_transaction(DnsQueryCandidate *c, DnsResource
                 r = dns_transaction_new(&t, c->scope, key);
                 if (r < 0)
                         return r;
-        } else {
-                if (set_contains(c->transactions, t))
-                        return 0;
-        }
-
-        r = set_ensure_allocated(&c->transactions, NULL);
-        if (r < 0)
-                goto gc;
-
-        r = set_ensure_allocated(&t->notify_query_candidates, NULL);
-        if (r < 0)
-                goto gc;
+        } else if (set_contains(c->transactions, t))
+                return 0;
 
         r = set_ensure_allocated(&t->notify_query_candidates_done, NULL);
         if (r < 0)
-                goto gc;
+                return r;
 
-        r = set_put(t->notify_query_candidates, c);
+        r = set_ensure_put(&t->notify_query_candidates, NULL, c);
         if (r < 0)
-                goto gc;
+                return r;
 
-        r = set_put(c->transactions, t);
+        r = set_ensure_put(&c->transactions, NULL, t);
         if (r < 0) {
                 (void) set_remove(t->notify_query_candidates, c);
-                goto gc;
+                return r;
         }
 
         t->clamp_ttl = c->query->clamp_ttl;
+        TAKE_PTR(t);
         return 1;
-
-gc:
-        dns_transaction_gc(t);
-        return r;
 }
 
 static int dns_query_candidate_go(DnsQueryCandidate *c) {
@@ -513,7 +500,7 @@ static int on_query_timeout(sd_event_source *s, usec_t usec, void *userdata) {
 }
 
 static int dns_query_add_candidate(DnsQuery *q, DnsScope *s) {
-        DnsQueryCandidate *c;
+        _cleanup_(dns_query_candidate_freep) DnsQueryCandidate *c = NULL;
         int r;
 
         assert(q);
@@ -524,24 +511,21 @@ static int dns_query_add_candidate(DnsQuery *q, DnsScope *s) {
                 return r;
 
         /* If this a single-label domain on DNS, we might append a suitable search domain first. */
-        if ((q->flags & SD_RESOLVED_NO_SEARCH) == 0 &&
-            dns_scope_name_needs_search_domain(s, dns_question_first_name(q->question_idna))) {
-                /* OK, we need a search domain now. Let's find one for this scope */
+        if (!FLAGS_SET(q->flags, SD_RESOLVED_NO_SEARCH) &&
+            dns_scope_name_wants_search_domain(s, dns_question_first_name(q->question_idna))) {
+                /* OK, we want a search domain now. Let's find one for this scope */
 
                 r = dns_query_candidate_next_search_domain(c);
-                if (r <= 0) /* if there's no search domain, then we won't add any transaction. */
-                        goto fail;
+                if (r < 0)
+                        return r;
         }
 
         r = dns_query_candidate_setup_transactions(c);
         if (r < 0)
-                goto fail;
+                return r;
 
+        TAKE_PTR(c);
         return 0;
-
-fail:
-        dns_query_candidate_free(c);
-        return r;
 }
 
 static int dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) {
index 5ee946bc7502be2ec10146d16281369a7a3af62d..fe8a21955714a323a686eefb54837c9a48321c77 100644 (file)
@@ -10,8 +10,8 @@ typedef struct DnsQuery DnsQuery;
 
 #include "resolved-dns-answer.h"
 #include "resolved-dns-question.h"
-#include "resolved-dns-stream.h"
 #include "resolved-dns-search-domain.h"
+#include "resolved-dns-transaction.h"
 
 struct DnsQueryCandidate {
         DnsQuery *query;
@@ -102,6 +102,8 @@ enum {
 };
 
 DnsQueryCandidate* dns_query_candidate_free(DnsQueryCandidate *c);
+DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQueryCandidate*, dns_query_candidate_free);
+
 void dns_query_candidate_notify(DnsQueryCandidate *c);
 
 int dns_query_new(Manager *m, DnsQuery **q, DnsQuestion *question_utf8, DnsQuestion *question_idna, int family, uint64_t flags);
index 6ba26a24b2c3f2cc02d6f66851831e0a7844f831..fa43dd089d4019611a5b18287a3ae21c77348991 100644 (file)
@@ -474,11 +474,11 @@ static DnsResourceRecord* dns_resource_record_free(DnsResourceRecord *rr) {
 
                 case DNS_TYPE_OPENPGPKEY:
                 default:
-                        if (!rr->unparseable)
+                        if (!rr->unparsable)
                                 free(rr->generic.data);
                 }
 
-                if (rr->unparseable)
+                if (rr->unparsable)
                         free(rr->generic.data);
 
                 free(rr->wire_format);
@@ -563,10 +563,10 @@ int dns_resource_record_payload_equal(const DnsResourceRecord *a, const DnsResou
 
         /* Check if a and b are the same, but don't look at their keys */
 
-        if (a->unparseable != b->unparseable)
+        if (a->unparsable != b->unparsable)
                 return 0;
 
-        switch (a->unparseable ? _DNS_TYPE_INVALID : a->key->type) {
+        switch (a->unparsable ? _DNS_TYPE_INVALID : a->key->type) {
 
         case DNS_TYPE_SRV:
                 r = dns_name_equal(a->srv.name, b->srv.name);
@@ -828,7 +828,7 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) {
 
         dns_resource_key_to_string(rr->key, k, sizeof(k));
 
-        switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) {
+        switch (rr->unparsable ? _DNS_TYPE_INVALID : rr->key->type) {
 
         case DNS_TYPE_SRV:
                 r = asprintf(&s, "%s %u %u %u %s",
@@ -1175,7 +1175,7 @@ ssize_t dns_resource_record_payload(DnsResourceRecord *rr, void **out) {
         assert(rr);
         assert(out);
 
-        switch(rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) {
+        switch(rr->unparsable ? _DNS_TYPE_INVALID : rr->key->type) {
         case DNS_TYPE_SRV:
         case DNS_TYPE_PTR:
         case DNS_TYPE_NS:
@@ -1343,7 +1343,7 @@ void dns_resource_record_hash_func(const DnsResourceRecord *rr, struct siphash *
 
         dns_resource_key_hash_func(rr->key, state);
 
-        switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) {
+        switch (rr->unparsable ? _DNS_TYPE_INVALID : rr->key->type) {
 
         case DNS_TYPE_SRV:
                 siphash24_compress(&rr->srv.priority, sizeof(rr->srv.priority), state);
@@ -1510,9 +1510,9 @@ DnsResourceRecord *dns_resource_record_copy(DnsResourceRecord *rr) {
         copy->expiry = rr->expiry;
         copy->n_skip_labels_signer = rr->n_skip_labels_signer;
         copy->n_skip_labels_source = rr->n_skip_labels_source;
-        copy->unparseable = rr->unparseable;
+        copy->unparsable = rr->unparsable;
 
-        switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) {
+        switch (rr->unparsable ? _DNS_TYPE_INVALID : rr->key->type) {
 
         case DNS_TYPE_SRV:
                 copy->srv.priority = rr->srv.priority;
index 291447f00e7ba20f075cc4305eb7f367c7054490..6c824f7962e1e71b43650e85557f285405b14bdd 100644 (file)
@@ -102,7 +102,7 @@ struct DnsResourceRecord {
         /* How many labels to strip to determine "synthesizing source" of this RR, i.e. the wildcard's immediate parent. -1 if not signed. */
         unsigned n_skip_labels_source;
 
-        bool unparseable:1;
+        bool unparsable:1;
 
         bool wire_format_canonical:1;
         void *wire_format;
index d7e7b5a853ca3fb590fbca0b4a343048bffab806..bd4b59ea8e18ee6afd415054bd56d18aaceef324 100644 (file)
@@ -447,8 +447,8 @@ static int dns_scope_socket(
         return TAKE_FD(fd);
 }
 
-int dns_scope_socket_udp(DnsScope *s, DnsServer *server, uint16_t port) {
-        return dns_scope_socket(s, SOCK_DGRAM, AF_UNSPEC, NULL, server, port, NULL);
+int dns_scope_socket_udp(DnsScope *s, DnsServer *server) {
+        return dns_scope_socket(s, SOCK_DGRAM, AF_UNSPEC, NULL, server, dns_server_port(server), NULL);
 }
 
 int dns_scope_socket_tcp(DnsScope *s, int family, const union in_addr_union *address, DnsServer *server, uint16_t port, union sockaddr_union *ret_socket_address) {
@@ -496,9 +496,8 @@ DnsScopeMatch dns_scope_good_domain(
         assert(s);
         assert(domain);
 
-        /* Checks if the specified domain is something to look up on
-         * this scope. Note that this accepts non-qualified hostnames,
-         * i.e. those without any search path prefixed yet. */
+        /* Checks if the specified domain is something to look up on this scope. Note that this accepts
+         * non-qualified hostnames, i.e. those without any search path suffixed. */
 
         if (ifindex != 0 && (!s->link || s->link->ifindex != ifindex))
                 return DNS_SCOPE_NO;
@@ -620,7 +619,7 @@ DnsScopeMatch dns_scope_good_domain(
                      manager_is_own_hostname(s->manager, domain) <= 0))  /* never resolve the local hostname via LLMNR */
                         return DNS_SCOPE_YES_BASE + 1; /* Return +1, as we consider ourselves authoritative
                                                         * for single-label names, i.e. one label. This is
-                                                        * particular relevant as it means a "." route on some
+                                                        * particularly relevant as it means a "." route on some
                                                         * other scope won't pull all traffic away from
                                                         * us. (If people actually want to pull traffic away
                                                         * from us they should turn off LLMNR on the
@@ -652,20 +651,21 @@ bool dns_scope_good_key(DnsScope *s, const DnsResourceKey *key) {
 
         if (s->protocol == DNS_PROTOCOL_DNS) {
 
-                /* On classic DNS, looking up non-address RRs is always
-                 * fine. (Specifically, we want to permit looking up
-                 * DNSKEY and DS records on the root and top-level
-                 * domains.) */
+                /* On classic DNS, looking up non-address RRs is always fine. (Specifically, we want to
+                 * permit looking up DNSKEY and DS records on the root and top-level domains.) */
                 if (!dns_resource_key_is_address(key))
                         return true;
 
-                /* However, we refuse to look up A and AAAA RRs on the
-                 * root and single-label domains, under the assumption
-                 * that those should be resolved via LLMNR or search
-                 * path only, and should not be leaked onto the
-                 * internet. */
-                return !(dns_name_is_single_label(dns_resource_key_name(key)) ||
-                         dns_name_is_root(dns_resource_key_name(key)));
+                /* Unless explicitly overridden, we refuse to look up A and AAAA RRs on the root and
+                 * single-label domains, under the assumption that those should be resolved via LLMNR or
+                 * search path only, and should not be leaked onto the internet. */
+                const char* name = dns_resource_key_name(key);
+
+                if (!s->manager->resolve_unicast_single_label &&
+                    dns_name_is_single_label(name))
+                        return false;
+
+                return !dns_name_is_root(name);
         }
 
         /* On mDNS and LLMNR, send A and AAAA queries only on the
@@ -1170,7 +1170,7 @@ DnsSearchDomain *dns_scope_get_search_domains(DnsScope *s) {
         return s->manager->search_domains;
 }
 
-bool dns_scope_name_needs_search_domain(DnsScope *s, const char *name) {
+bool dns_scope_name_wants_search_domain(DnsScope *s, const char *name) {
         assert(s);
 
         if (s->protocol != DNS_PROTOCOL_DNS)
@@ -1254,11 +1254,7 @@ int dns_scope_announce(DnsScope *scope, bool goodbye) {
                 if (!scope->announced &&
                     dns_resource_key_is_dnssd_ptr(z->rr->key)) {
                         if (!set_contains(types, dns_resource_key_name(z->rr->key))) {
-                                r = set_ensure_allocated(&types, &dns_name_hash_ops);
-                                if (r < 0)
-                                        return log_debug_errno(r, "Failed to allocate set: %m");
-
-                                r = set_put(types, dns_resource_key_name(z->rr->key));
+                                r = set_ensure_put(&types, &dns_name_hash_ops, dns_resource_key_name(z->rr->key));
                                 if (r < 0)
                                         return log_debug_errno(r, "Failed to add item to set: %m");
                         }
index f4b45c4f20822a51e60a36fc4d26c913166bd832..8b1a958551c2f47b161797d601628d96b54be51d 100644 (file)
@@ -2,18 +2,19 @@
 #pragma once
 
 #include "list.h"
+#include "ratelimit.h"
 
+typedef struct DnsQueryCandidate DnsQueryCandidate;
 typedef struct DnsScope DnsScope;
 
 #include "resolved-dns-cache.h"
 #include "resolved-dns-dnssec.h"
 #include "resolved-dns-packet.h"
-#include "resolved-dns-query.h"
+
 #include "resolved-dns-search-domain.h"
 #include "resolved-dns-server.h"
 #include "resolved-dns-stream.h"
 #include "resolved-dns-zone.h"
-#include "resolved-link.h"
 
 typedef enum DnsScopeMatch {
         DNS_SCOPE_NO,
@@ -74,7 +75,7 @@ void dns_scope_packet_lost(DnsScope *s, usec_t usec);
 
 int dns_scope_emit_udp(DnsScope *s, int fd, DnsPacket *p);
 int dns_scope_socket_tcp(DnsScope *s, int family, const union in_addr_union *address, DnsServer *server, uint16_t port, union sockaddr_union *ret_socket_address);
-int dns_scope_socket_udp(DnsScope *s, DnsServer *server, uint16_t port);
+int dns_scope_socket_udp(DnsScope *s, DnsServer *server);
 
 DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, const char *domain);
 bool dns_scope_good_key(DnsScope *s, const DnsResourceKey *key);
@@ -98,7 +99,7 @@ void dns_scope_dump(DnsScope *s, FILE *f);
 
 DnsSearchDomain *dns_scope_get_search_domains(DnsScope *s);
 
-bool dns_scope_name_needs_search_domain(DnsScope *s, const char *name);
+bool dns_scope_name_wants_search_domain(DnsScope *s, const char *name);
 
 bool dns_scope_network_good(DnsScope *s);
 
index 21c2442c516b7698a765bf6aec6f67fcefc6826e..425a46334948c7531cc37877c69f06c52fe7135a 100644 (file)
@@ -3,6 +3,8 @@
 #include "alloc-util.h"
 #include "dns-domain.h"
 #include "resolved-dns-search-domain.h"
+#include "resolved-link.h"
+#include "resolved-manager.h"
 
 int dns_search_domain_new(
                 Manager *m,
index d0c2914d367920742a2bb80369fedd1cb01f0989..df0b2f14049928e072de232462ba295fe74a2879 100644 (file)
@@ -1,18 +1,18 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
+#include "list.h"
 #include "macro.h"
 
 typedef struct DnsSearchDomain DnsSearchDomain;
+typedef struct Link Link;
+typedef struct Manager Manager;
 
 typedef enum DnsSearchDomainType {
         DNS_SEARCH_DOMAIN_SYSTEM,
         DNS_SEARCH_DOMAIN_LINK,
 } DnsSearchDomainType;
 
-#include "resolved-link.h"
-#include "resolved-manager.h"
-
 struct DnsSearchDomain {
         Manager *manager;
 
index 6016af97d8e387f2b8203e3af969c5ad380fe7ed..9c221e1989bd19b85a5140aa32a305ceca9a59e8 100644 (file)
@@ -6,6 +6,7 @@
 #include "resolved-bus.h"
 #include "resolved-dns-server.h"
 #include "resolved-dns-stub.h"
+#include "resolved-manager.h"
 #include "resolved-resolv-conf.h"
 #include "siphash24.h"
 #include "string-table.h"
@@ -25,6 +26,7 @@ int dns_server_new(
                 Link *l,
                 int family,
                 const union in_addr_union *in_addr,
+                uint16_t port,
                 int ifindex,
                 const char *server_name) {
 
@@ -46,7 +48,7 @@ int dns_server_new(
                         return -E2BIG;
         }
 
-        if (server_name) {
+        if (!isempty(server_name)) {
                 name = strdup(server_name);
                 if (!name)
                         return -ENOMEM;
@@ -62,6 +64,7 @@ int dns_server_new(
                 .type = type,
                 .family = family,
                 .address = *in_addr,
+                .port = port,
                 .ifindex = ifindex,
                 .server_name = TAKE_PTR(name),
         };
@@ -116,6 +119,7 @@ static DnsServer* dns_server_free(DnsServer *s)  {
 #endif
 
         free(s->server_string);
+        free(s->server_string_full);
         free(s->server_name);
         return mfree(s);
 }
@@ -222,7 +226,7 @@ static void dns_server_verified(DnsServer *s, DnsServerFeatureLevel level) {
         if (s->verified_feature_level != level) {
                 log_debug("Verified we get a response at feature level %s from DNS server %s.",
                           dns_server_feature_level_to_string(level),
-                          dns_server_string(s));
+                          strna(dns_server_string_full(s)));
                 s->verified_feature_level = level;
         }
 
@@ -359,7 +363,7 @@ void dns_server_packet_rcode_downgrade(DnsServer *s, DnsServerFeatureLevel level
                 dns_server_reset_counters(s);
         }
 
-        log_debug("Downgrading transaction feature level fixed an RCODE error, downgrading server %s too.", dns_server_string(s));
+        log_debug("Downgrading transaction feature level fixed an RCODE error, downgrading server %s too.", strna(dns_server_string_full(s)));
 }
 
 static bool dns_server_grace_period_expired(DnsServer *s) {
@@ -413,7 +417,7 @@ DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s) {
 
                 log_info("Grace period over, resuming full feature set (%s) for DNS server %s.",
                          dns_server_feature_level_to_string(s->possible_feature_level),
-                         dns_server_string(s));
+                         strna(dns_server_string_full(s)));
 
                 dns_server_flush_cache(s);
 
@@ -499,7 +503,7 @@ DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s) {
 
                         log_full(log_level, "Using degraded feature set %s instead of %s for DNS server %s.",
                                  dns_server_feature_level_to_string(s->possible_feature_level),
-                                 dns_server_feature_level_to_string(p), dns_server_string(s));
+                                 dns_server_feature_level_to_string(p), strna(dns_server_string_full(s)));
                 }
         }
 
@@ -547,13 +551,37 @@ int dns_server_ifindex(const DnsServer *s) {
         return 0;
 }
 
+uint16_t dns_server_port(const DnsServer *s) {
+        assert(s);
+
+        if (s->port > 0)
+                return s->port;
+
+        return 53;
+}
+
 const char *dns_server_string(DnsServer *server) {
         assert(server);
 
         if (!server->server_string)
                 (void) in_addr_ifindex_to_string(server->family, &server->address, dns_server_ifindex(server), &server->server_string);
 
-        return strna(server->server_string);
+        return server->server_string;
+}
+
+const char *dns_server_string_full(DnsServer *server) {
+        assert(server);
+
+        if (!server->server_string_full)
+                (void) in_addr_port_ifindex_name_to_string(
+                                server->family,
+                                &server->address,
+                                server->port,
+                                dns_server_ifindex(server),
+                                server->server_name,
+                                &server->server_string_full);
+
+        return server->server_string_full;
 }
 
 bool dns_server_dnssec_supported(DnsServer *server) {
@@ -585,8 +613,8 @@ void dns_server_warn_downgrade(DnsServer *server) {
 
         log_struct(LOG_NOTICE,
                    "MESSAGE_ID=" SD_MESSAGE_DNSSEC_DOWNGRADE_STR,
-                   LOG_MESSAGE("Server %s does not support DNSSEC, downgrading to non-DNSSEC mode.", dns_server_string(server)),
-                   "DNS_SERVER=%s", dns_server_string(server),
+                   LOG_MESSAGE("Server %s does not support DNSSEC, downgrading to non-DNSSEC mode.", strna(dns_server_string_full(server))),
+                   "DNS_SERVER=%s", strna(dns_server_string_full(server)),
                    "DNS_SERVER_FEATURE_LEVEL=%s", dns_server_feature_level_to_string(server->possible_feature_level));
 
         server->warned_downgrade = true;
@@ -597,7 +625,9 @@ static void dns_server_hash_func(const DnsServer *s, struct siphash *state) {
 
         siphash24_compress(&s->family, sizeof(s->family), state);
         siphash24_compress(&s->address, FAMILY_ADDRESS_SIZE(s->family), state);
+        siphash24_compress(&s->port, sizeof(s->port), state);
         siphash24_compress(&s->ifindex, sizeof(s->ifindex), state);
+        siphash24_compress_string(s->server_name, state);
 }
 
 static int dns_server_compare_func(const DnsServer *x, const DnsServer *y) {
@@ -611,11 +641,15 @@ static int dns_server_compare_func(const DnsServer *x, const DnsServer *y) {
         if (r != 0)
                 return r;
 
+        r = CMP(x->port, y->port);
+        if (r != 0)
+                return r;
+
         r = CMP(x->ifindex, y->ifindex);
         if (r != 0)
                 return r;
 
-        return 0;
+        return streq_ptr(x->server_name, y->server_name);
 }
 
 DEFINE_HASH_OPS(dns_server_hash_ops, DnsServer, dns_server_hash_func, dns_server_compare_func);
@@ -654,11 +688,15 @@ void dns_server_mark_all(DnsServer *first) {
         dns_server_mark_all(first->servers_next);
 }
 
-DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr, int ifindex) {
+DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr, uint16_t port, int ifindex, const char *name) {
         DnsServer *s;
 
         LIST_FOREACH(servers, s, first)
-                if (s->family == family && in_addr_equal(family, &s->address, in_addr) > 0 && s->ifindex == ifindex)
+                if (s->family == family &&
+                    in_addr_equal(family, &s->address, in_addr) > 0 &&
+                    s->port == port &&
+                    s->ifindex == ifindex &&
+                    streq_ptr(s->server_name, name))
                         return s;
 
         return NULL;
@@ -689,7 +727,7 @@ DnsServer *manager_set_dns_server(Manager *m, DnsServer *s) {
         if (s)
                 log_debug("Switching to %s DNS server %s.",
                           dns_server_type_to_string(s->type),
-                          dns_server_string(s));
+                          strna(dns_server_string_full(s)));
 
         dns_server_unref(m->current_dns_server);
         m->current_dns_server = dns_server_ref(s);
@@ -829,7 +867,7 @@ void dns_server_dump(DnsServer *s, FILE *f) {
                 f = stdout;
 
         fputs("[Server ", f);
-        fputs(dns_server_string(s), f);
+        fputs(strna(dns_server_string_full(s)), f);
         fputs(" type=", f);
         fputs(dns_server_type_to_string(s->type), f);
 
index 889c80a2054bdaf488eb0632c8af0f6014796e46..464e8dc2515d51ad733e1db6493a9dd561eec2f8 100644 (file)
@@ -2,8 +2,18 @@
 #pragma once
 
 #include "in-addr-util.h"
+#include "list.h"
+#include "resolve-util.h"
+#include "time-util.h"
 
+typedef struct DnsScope DnsScope;
 typedef struct DnsServer DnsServer;
+typedef struct DnsStream DnsStream;
+typedef struct DnsPacket DnsPacket;
+typedef struct Link Link;
+typedef struct Manager Manager;
+
+#include "resolved-dnstls.h"
 
 typedef enum DnsServerType {
         DNS_SERVER_SYSTEM,
@@ -35,10 +45,6 @@ typedef enum DnsServerFeatureLevel {
 const char* dns_server_feature_level_to_string(int i) _const_;
 int dns_server_feature_level_from_string(const char *s) _pure_;
 
-#include "resolved-dnstls.h"
-#include "resolved-link.h"
-#include "resolved-manager.h"
-
 struct DnsServer {
         Manager *manager;
 
@@ -50,10 +56,11 @@ struct DnsServer {
         int family;
         union in_addr_union address;
         int ifindex; /* for IPv6 link-local DNS servers */
+        uint16_t port;
+        char *server_name;
 
         char *server_string;
-
-        char *server_name;
+        char *server_string_full;
 
         /* The long-lived stream towards this server. */
         DnsStream *stream;
@@ -96,6 +103,7 @@ int dns_server_new(
                 Link *link,
                 int family,
                 const union in_addr_union *address,
+                uint16_t port,
                 int ifindex,
                 const char *server_string);
 
@@ -117,13 +125,15 @@ DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s);
 int dns_server_adjust_opt(DnsServer *server, DnsPacket *packet, DnsServerFeatureLevel level);
 
 const char *dns_server_string(DnsServer *server);
+const char *dns_server_string_full(DnsServer *server);
 int dns_server_ifindex(const DnsServer *s);
+uint16_t dns_server_port(const DnsServer *s);
 
 bool dns_server_dnssec_supported(DnsServer *server);
 
 void dns_server_warn_downgrade(DnsServer *server);
 
-DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr, int ifindex);
+DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr, uint16_t port, int ifindex, const char *name);
 
 void dns_server_unlink_all(DnsServer *first);
 void dns_server_unlink_marked(DnsServer *first);
index 2a106949976ea307ff34eb935eceaeb193e32375..d4c49e673ef8a65dbca4df91e3968b7870a24fec 100644 (file)
@@ -8,6 +8,7 @@
 #include "io-util.h"
 #include "missing_network.h"
 #include "resolved-dns-stream.h"
+#include "resolved-manager.h"
 
 #define DNS_STREAM_TIMEOUT_USEC (10 * USEC_PER_SEC)
 #define DNS_STREAMS_MAX 128
@@ -86,11 +87,9 @@ static int dns_stream_complete(DnsStream *s, int error) {
 }
 
 static int dns_stream_identify(DnsStream *s) {
-        union {
-                struct cmsghdr header; /* For alignment */
-                uint8_t buffer[CMSG_SPACE(MAXSIZE(struct in_pktinfo, struct in6_pktinfo))
-                               + EXTRA_CMSG_SPACE /* kernel appears to require extra space */];
-        } control;
+        CMSG_BUFFER_TYPE(CMSG_SPACE(MAXSIZE(struct in_pktinfo, struct in6_pktinfo))
+                         + CMSG_SPACE(int) + /* for the TTL */
+                         + EXTRA_CMSG_SPACE /* kernel appears to require extra space */) control;
         struct msghdr mh = {};
         struct cmsghdr *cmsg;
         socklen_t sl;
@@ -191,7 +190,7 @@ static int dns_stream_identify(DnsStream *s) {
                 s->ifindex = manager_find_ifindex(s->manager, s->local.sa.sa_family, s->local.sa.sa_family == AF_INET ? (union in_addr_union*) &s->local.in.sin_addr : (union in_addr_union*)  &s->local.in6.sin6_addr);
 
         if (s->protocol == DNS_PROTOCOL_LLMNR && s->ifindex > 0) {
-                uint32_t ifindex = htobe32(s->ifindex);
+                be32_t ifindex = htobe32(s->ifindex);
 
                 /* Make sure all packets for this connection are sent on the same interface */
                 if (s->local.sa.sa_family == AF_INET) {
index 1013f6e45e6a0dc0cdff39ceae9ef6e70462024f..9fd8f5a342f2e16108cecc6a77154b99121743de 100644 (file)
@@ -1,9 +1,18 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
+#include "sd-event.h"
+
+#include "ordered-set.h"
 #include "socket-util.h"
 
+typedef struct DnsServer DnsServer;
 typedef struct DnsStream DnsStream;
+typedef struct DnsTransaction DnsTransaction;
+typedef struct Manager Manager;
+
+#include "resolved-dns-packet.h"
+#include "resolved-dnstls.h"
 
 typedef enum DnsStreamType {
         DNS_STREAM_LOOKUP,        /* Outgoing connection to a classic DNS server */
@@ -14,11 +23,6 @@ typedef enum DnsStreamType {
         _DNS_STREAM_TYPE_INVALID = -1,
 } DnsStreamType;
 
-#include "resolved-dns-packet.h"
-#include "resolved-dns-transaction.h"
-#include "resolved-dnstls.h"
-#include "resolved-manager.h"
-
 #define DNS_STREAM_WRITE_TLS_DATA 1
 
 /* Streams are used by three subsystems:
index ce994a7ee0b213c0fa525d1547cc141c85e7e69a..03edbe26dc0d245ae7011288be8126c7a946c5b9 100644 (file)
@@ -278,7 +278,7 @@ static int dns_stub_stream_complete(DnsStream *s, int error) {
 }
 
 static void dns_stub_process_query(Manager *m, DnsStream *s, DnsPacket *p) {
-        DnsQuery *q = NULL;
+        _cleanup_(dns_query_freep) DnsQuery *q = NULL;
         int r;
 
         assert(m);
@@ -289,52 +289,52 @@ static void dns_stub_process_query(Manager *m, DnsStream *s, DnsPacket *p) {
             in_addr_is_localhost(p->family, &p->destination) <= 0) {
                 log_error("Got packet on unexpected IP range, refusing.");
                 dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL, false);
-                goto fail;
+                return;
         }
 
         r = dns_packet_extract(p);
         if (r < 0) {
                 log_debug_errno(r, "Failed to extract resources from incoming packet, ignoring packet: %m");
                 dns_stub_send_failure(m, s, p, DNS_RCODE_FORMERR, false);
-                goto fail;
+                return;
         }
 
         if (!DNS_PACKET_VERSION_SUPPORTED(p)) {
                 log_debug("Got EDNS OPT field with unsupported version number.");
                 dns_stub_send_failure(m, s, p, DNS_RCODE_BADVERS, false);
-                goto fail;
+                return;
         }
 
         if (dns_type_is_obsolete(p->question->keys[0]->type)) {
                 log_debug("Got message with obsolete key type, refusing.");
                 dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP, false);
-                goto fail;
+                return;
         }
 
         if (dns_type_is_zone_transer(p->question->keys[0]->type)) {
                 log_debug("Got request for zone transfer, refusing.");
                 dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP, false);
-                goto fail;
+                return;
         }
 
         if (!DNS_PACKET_RD(p))  {
                 /* If the "rd" bit is off (i.e. recursion was not requested), then refuse operation */
                 log_debug("Got request with recursion disabled, refusing.");
                 dns_stub_send_failure(m, s, p, DNS_RCODE_REFUSED, false);
-                goto fail;
+                return;
         }
 
         if (DNS_PACKET_DO(p) && DNS_PACKET_CD(p)) {
                 log_debug("Got request with DNSSEC CD bit set, refusing.");
                 dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP, false);
-                goto fail;
+                return;
         }
 
         r = dns_query_new(m, &q, p->question, p->question, 0, SD_RESOLVED_PROTOCOLS_ALL|SD_RESOLVED_NO_SEARCH);
         if (r < 0) {
                 log_error_errno(r, "Failed to generate query object: %m");
                 dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL, false);
-                goto fail;
+                return;
         }
 
         /* Request that the TTL is corrected by the cached time for this lookup, so that we return vaguely useful TTLs */
@@ -348,30 +348,23 @@ static void dns_stub_process_query(Manager *m, DnsStream *s, DnsPacket *p) {
                 /* Remember which queries belong to this stream, so that we can cancel them when the stream
                  * is disconnected early */
 
-                r = set_ensure_allocated(&s->queries, &trivial_hash_ops);
+                r = set_ensure_put(&s->queries, NULL, q);
                 if (r < 0) {
                         log_oom();
-                        goto fail;
-                }
-
-                if (set_put(s->queries, q) < 0) {
-                        log_oom();
-                        goto fail;
+                        return;
                 }
+                assert(r > 0);
         }
 
         r = dns_query_go(q);
         if (r < 0) {
                 log_error_errno(r, "Failed to start query: %m");
                 dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL, false);
-                goto fail;
+                return;
         }
 
         log_debug("Processing query...");
-        return;
-
-fail:
-        dns_query_free(q);
+        TAKE_PTR(q);
 }
 
 static int on_dns_stub_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
index 5898308d5fa46f6060d0c83c4743028ce49dee86..e23ea273e7979835ea557548bb26c7714719e46d 100644 (file)
@@ -314,7 +314,7 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) {
                            "DNS_TRANSACTION=%" PRIu16, t->id,
                            "DNS_QUESTION=%s", key_str,
                            "DNSSEC_RESULT=%s", dnssec_result_to_string(t->answer_dnssec_result),
-                           "DNS_SERVER=%s", dns_server_string(t->server),
+                           "DNS_SERVER=%s", strna(dns_server_string_full(t->server)),
                            "DNS_SERVER_FEATURE_LEVEL=%s", dns_server_feature_level_to_string(t->server->possible_feature_level));
         }
 
@@ -398,7 +398,7 @@ static int dns_transaction_pick_server(DnsTransaction *t) {
 
         t->n_picked_servers ++;
 
-        log_debug("Using DNS server %s for transaction %u.", dns_server_string(t->server), t->id);
+        log_debug("Using DNS server %s for transaction %u.", strna(dns_server_string_full(t->server)), t->id);
 
         return 1;
 }
@@ -544,8 +544,10 @@ static int on_stream_packet(DnsStream *s) {
         return 0;
 }
 
-static uint16_t dns_port_for_feature_level(DnsServerFeatureLevel level) {
-        return DNS_SERVER_FEATURE_LEVEL_IS_TLS(level) ? 853 : 53;
+static uint16_t dns_transaction_port(DnsTransaction *t) {
+        if (t->server->port > 0)
+                return t->server->port;
+        return DNS_SERVER_FEATURE_LEVEL_IS_TLS(t->current_feature_level) ? 853 : 53;
 }
 
 static int dns_transaction_emit_tcp(DnsTransaction *t) {
@@ -576,7 +578,7 @@ static int dns_transaction_emit_tcp(DnsTransaction *t) {
                 if (t->server->stream && (DNS_SERVER_FEATURE_LEVEL_IS_TLS(t->current_feature_level) == t->server->stream->encrypted))
                         s = dns_stream_ref(t->server->stream);
                 else
-                        fd = dns_scope_socket_tcp(t->scope, AF_UNSPEC, NULL, t->server, dns_port_for_feature_level(t->current_feature_level), &sa);
+                        fd = dns_scope_socket_tcp(t->scope, AF_UNSPEC, NULL, t->server, dns_transaction_port(t), &sa);
 
                 type = DNS_STREAM_LOOKUP;
                 break;
@@ -1243,7 +1245,7 @@ static int dns_transaction_emit_udp(DnsTransaction *t) {
 
                         dns_transaction_close_connection(t);
 
-                        fd = dns_scope_socket_udp(t->scope, t->server, 53);
+                        fd = dns_scope_socket_udp(t->scope, t->server);
                         if (fd < 0)
                                 return fd;
 
@@ -1501,11 +1503,7 @@ static int dns_transaction_make_packet_mdns(DnsTransaction *t) {
                 add_known_answers = true;
 
         if (t->key->type == DNS_TYPE_ANY) {
-                r = set_ensure_allocated(&keys, &dns_resource_key_hash_ops);
-                if (r < 0)
-                        return r;
-
-                r = set_put(keys, t->key);
+                r = set_ensure_put(&keys, &dns_resource_key_hash_ops, t->key);
                 if (r < 0)
                         return r;
         }
@@ -1571,11 +1569,7 @@ static int dns_transaction_make_packet_mdns(DnsTransaction *t) {
                         add_known_answers = true;
 
                 if (other->key->type == DNS_TYPE_ANY) {
-                        r = set_ensure_allocated(&keys, &dns_resource_key_hash_ops);
-                        if (r < 0)
-                                return r;
-
-                        r = set_put(keys, other->key);
+                        r = set_ensure_put(&keys, &dns_resource_key_hash_ops, other->key);
                         if (r < 0)
                                 return r;
                 }
@@ -1800,7 +1794,7 @@ static int dns_transaction_find_cyclic(DnsTransaction *t, DnsTransaction *aux) {
 }
 
 static int dns_transaction_add_dnssec_transaction(DnsTransaction *t, DnsResourceKey *key, DnsTransaction **ret) {
-        DnsTransaction *aux;
+        _cleanup_(dns_transaction_gcp) DnsTransaction *aux = NULL;
         int r;
 
         assert(t);
@@ -1833,34 +1827,22 @@ static int dns_transaction_add_dnssec_transaction(DnsTransaction *t, DnsResource
                 }
         }
 
-        r = set_ensure_allocated(&t->dnssec_transactions, NULL);
-        if (r < 0)
-                goto gc;
-
-        r = set_ensure_allocated(&aux->notify_transactions, NULL);
-        if (r < 0)
-                goto gc;
-
         r = set_ensure_allocated(&aux->notify_transactions_done, NULL);
         if (r < 0)
-                goto gc;
+                return r;
 
-        r = set_put(t->dnssec_transactions, aux);
+        r = set_ensure_put(&t->dnssec_transactions, NULL, aux);
         if (r < 0)
-                goto gc;
+                return r;;
 
-        r = set_put(aux->notify_transactions, t);
+        r = set_ensure_put(&aux->notify_transactions, NULL, t);
         if (r < 0) {
                 (void) set_remove(t->dnssec_transactions, aux);
-                goto gc;
+                return r;
         }
 
-        *ret = aux;
+        *ret = TAKE_PTR(aux);
         return 1;
-
-gc:
-        dns_transaction_gc(aux);
-        return r;
 }
 
 static int dns_transaction_request_dnssec_rr(DnsTransaction *t, DnsResourceKey *key) {
index bdfcbc1acc3a791cdca98a1a156ee423789851d5..167541806a28eaa5f0deac78cc7664bf271ba1e8 100644 (file)
@@ -1,10 +1,18 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
+#include "sd-event.h"
+
 typedef struct DnsTransaction DnsTransaction;
 typedef enum DnsTransactionState DnsTransactionState;
 typedef enum DnsTransactionSource DnsTransactionSource;
 
+#include "resolved-dns-answer.h"
+#include "resolved-dns-dnssec.h"
+#include "resolved-dns-packet.h"
+#include "resolved-dns-question.h"
+#include "resolved-dns-server.h"
+
 enum DnsTransactionState {
         DNS_TRANSACTION_NULL,
         DNS_TRANSACTION_PENDING,
@@ -37,13 +45,6 @@ enum DnsTransactionSource {
         _DNS_TRANSACTION_SOURCE_INVALID = -1
 };
 
-#include "resolved-dns-answer.h"
-#include "resolved-dns-packet.h"
-#include "resolved-dns-question.h"
-#include "resolved-dns-scope.h"
-#include "resolved-dns-server.h"
-#include "resolved-dns-stream.h"
-
 struct DnsTransaction {
         DnsScope *scope;
 
@@ -137,6 +138,8 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key);
 DnsTransaction* dns_transaction_free(DnsTransaction *t);
 
 bool dns_transaction_gc(DnsTransaction *t);
+DEFINE_TRIVIAL_CLEANUP_FUNC(DnsTransaction*, dns_transaction_gc);
+
 int dns_transaction_go(DnsTransaction *t);
 
 void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p);
index 92842bcf89d43f8646b768f1b3333a8b88db4780..d68d0c3ba16cb47609ae5be7cfe92249ca2ad40e 100644 (file)
@@ -184,11 +184,10 @@ static int dns_trust_anchor_add_builtin_negative(DnsTrustAnchor *d) {
          * unsigned. */
 
         NULSTR_FOREACH(name, private_domains) {
-
                 if (dns_trust_anchor_knows_domain_positive(d, name))
                         continue;
 
-                r = set_put_strdup(d->negative_by_name, name);
+                r = set_put_strdup(&d->negative_by_name, name);
                 if (r < 0)
                         return r;
         }
@@ -394,16 +393,10 @@ static int dns_trust_anchor_load_negative(DnsTrustAnchor *d, const char *path, u
                 return -EINVAL;
         }
 
-        r = set_ensure_allocated(&d->negative_by_name, &dns_name_hash_ops);
+        r = set_ensure_consume(&d->negative_by_name, &dns_name_hash_ops, TAKE_PTR(domain));
         if (r < 0)
                 return log_oom();
 
-        r = set_put(d->negative_by_name, domain);
-        if (r < 0)
-                return log_oom();
-        if (r > 0)
-                domain = NULL;
-
         return 0;
 }
 
@@ -593,11 +586,7 @@ static int dns_trust_anchor_revoked_put(DnsTrustAnchor *d, DnsResourceRecord *rr
 
         assert(d);
 
-        r = set_ensure_allocated(&d->revoked_by_rr, &dns_resource_record_hash_ops);
-        if (r < 0)
-                return r;
-
-        r = set_put(d->revoked_by_rr, rr);
+        r = set_ensure_put(&d->revoked_by_rr, &dns_resource_record_hash_ops, rr);
         if (r < 0)
                 return r;
         if (r > 0)
index d5cc2767d7d7905154bacd902ee793e57076a983..33879d6142ef2dbea9c7aeb5e00f70d0eb1a5f93 100644 (file)
@@ -6,6 +6,7 @@
 #include "resolved-dns-packet.h"
 #include "resolved-dns-zone.h"
 #include "resolved-dnssd.h"
+#include "resolved-manager.h"
 #include "string-util.h"
 
 /* Never allow more than 1K entries */
@@ -161,7 +162,7 @@ static int dns_zone_link_item(DnsZone *z, DnsZoneItem *i) {
 }
 
 static int dns_zone_item_probe_start(DnsZoneItem *i)  {
-        DnsTransaction *t;
+        _cleanup_(dns_transaction_gcp) DnsTransaction *t = NULL;
         int r;
 
         assert(i);
@@ -182,25 +183,20 @@ static int dns_zone_item_probe_start(DnsZoneItem *i)  {
                         return r;
         }
 
-        r = set_ensure_allocated(&t->notify_zone_items, NULL);
-        if (r < 0)
-                goto gc;
-
         r = set_ensure_allocated(&t->notify_zone_items_done, NULL);
         if (r < 0)
-                goto gc;
+                return r;
 
-        r = set_put(t->notify_zone_items, i);
+        r = set_ensure_put(&t->notify_zone_items, NULL, i);
         if (r < 0)
-                goto gc;
+                return r;
 
-        i->probe_transaction = t;
         t->probing = true;
+        i->probe_transaction = TAKE_PTR(t);
 
-        if (t->state == DNS_TRANSACTION_NULL) {
-
+        if (i->probe_transaction->state == DNS_TRANSACTION_NULL) {
                 i->block_ready++;
-                r = dns_transaction_go(t);
+                r = dns_transaction_go(i->probe_transaction);
                 i->block_ready--;
 
                 if (r < 0) {
@@ -211,10 +207,6 @@ static int dns_zone_item_probe_start(DnsZoneItem *i)  {
 
         dns_zone_item_notify(i);
         return 0;
-
-gc:
-        dns_transaction_gc(t);
-        return r;
 }
 
 int dns_zone_put(DnsZone *z, DnsScope *s, DnsResourceRecord *rr, bool probe) {
index f7dcb3bfa56f8fb4afc035e9ab165d3211129b21..33efb8ef06c6cc5833d2801d35dc10a04edf0dba 100644 (file)
@@ -6,6 +6,7 @@
 #include "resolved-dnssd-bus.h"
 #include "resolved-dnssd.h"
 #include "resolved-link.h"
+#include "resolved-manager.h"
 #include "strv.h"
 #include "user-util.h"
 
@@ -62,16 +63,7 @@ int bus_dnssd_method_unregister(sd_bus_message *message, void *userdata, sd_bus_
         return sd_bus_reply_method_return(message, NULL);
 }
 
-const sd_bus_vtable dnssd_vtable[] = {
-        SD_BUS_VTABLE_START(0),
-
-        SD_BUS_METHOD("Unregister", NULL, NULL, bus_dnssd_method_unregister, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_SIGNAL("Conflicted", NULL, 0),
-
-        SD_BUS_VTABLE_END
-};
-
-int dnssd_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
+static int dnssd_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
         _cleanup_free_ char *name = NULL;
         Manager *m = userdata;
         DnssdService *service;
@@ -95,7 +87,7 @@ int dnssd_object_find(sd_bus *bus, const char *path, const char *interface, void
         return 1;
 }
 
-int dnssd_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
+static int dnssd_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
         _cleanup_strv_free_ char **l = NULL;
         Manager *m = userdata;
         DnssdService *service;
@@ -127,3 +119,19 @@ int dnssd_node_enumerator(sd_bus *bus, const char *path, void *userdata, char **
 
         return 1;
 }
+
+static const sd_bus_vtable dnssd_vtable[] = {
+        SD_BUS_VTABLE_START(0),
+
+        SD_BUS_METHOD("Unregister", NULL, NULL, bus_dnssd_method_unregister, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_SIGNAL("Conflicted", NULL, 0),
+
+        SD_BUS_VTABLE_END
+};
+
+const BusObjectImplementation dnssd_object = {
+        "/org/freedesktop/resolve1/dnssd",
+        "org.freedesktop.resolve1.DnssdService",
+        .fallback_vtables = BUS_FALLBACK_VTABLES({dnssd_vtable, dnssd_object_find}),
+        .node_enumerator = dnssd_node_enumerator,
+};
index 9ee2ce17ec8c1eedf0d3671ffab46aaf24205230..403455e89f4439d81cbe999eea2215ee7c9c9ecd 100644 (file)
@@ -2,9 +2,8 @@
 
 #include "sd-bus.h"
 
-extern const sd_bus_vtable dnssd_vtable[];
+#include "bus-object.h"
 
-int dnssd_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error);
-int dnssd_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error);
+extern const BusObjectImplementation dnssd_object;
 
 int bus_dnssd_method_unregister(sd_bus_message *message, void *userdata, sd_bus_error *error);
index 78dbed283e43016addfd57605816a602f7282c8c..4458ad1d2d47554c95d37e8082bb4096b8aa497b 100644 (file)
@@ -86,10 +86,13 @@ static int dnssd_service_load(Manager *manager, const char *filename) {
 
         dropin_dirname = strjoina(service->name, ".dnssd.d");
 
-        r = config_parse_many(filename, DNSSD_SERVICE_DIRS, dropin_dirname,
-                              "Service\0",
-                              config_item_perf_lookup, resolved_dnssd_gperf_lookup,
-                              false, service, NULL);
+        r = config_parse_many(
+                        filename, DNSSD_SERVICE_DIRS, dropin_dirname,
+                        "Service\0",
+                        config_item_perf_lookup, resolved_dnssd_gperf_lookup,
+                        CONFIG_PARSE_WARN,
+                        service,
+                        NULL);
         if (r < 0)
                 return r;
 
@@ -153,10 +156,15 @@ static int specifier_dnssd_host_name(char specifier, const void *data, const voi
 
 int dnssd_render_instance_name(DnssdService *s, char **ret_name) {
         static const Specifier specifier_table[] = {
+                { 'm', specifier_machine_id,      NULL },
                 { 'b', specifier_boot_id,         NULL },
                 { 'H', specifier_dnssd_host_name, NULL },
-                { 'm', specifier_machine_id,      NULL },
                 { 'v', specifier_kernel_release,  NULL },
+                { 'a', specifier_architecture,    NULL },
+                { 'o', specifier_os_id,           NULL },
+                { 'w', specifier_os_version_id,   NULL },
+                { 'B', specifier_os_build_id,     NULL },
+                { 'W', specifier_os_variant_id,   NULL },
                 {}
         };
         _cleanup_free_ char *name = NULL;
index 2a4a95989c98b52ad6952ecf6fceeeadd48490eb..6b7db7ef8c4a1f99a2114f5d24ba3d839008b244 100644 (file)
@@ -8,6 +8,7 @@
 
 #include "resolved-dns-stream.h"
 #include "resolved-dnstls.h"
+#include "resolved-manager.h"
 
 #define TLS_PROTOCOL_PRIORITY "NORMAL:-VERS-ALL:+VERS-TLS1.3:+VERS-TLS1.2"
 DEFINE_TRIVIAL_CLEANUP_FUNC(gnutls_session_t, gnutls_deinit);
@@ -56,15 +57,19 @@ int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server) {
         }
 
         if (server->manager->dns_over_tls_mode == DNS_OVER_TLS_YES) {
-                stream->dnstls_data.validation.type = GNUTLS_DT_IP_ADDRESS;
-                if (server->family == AF_INET) {
-                        stream->dnstls_data.validation.data = (unsigned char*) &server->address.in.s_addr;
-                        stream->dnstls_data.validation.size = 4;
-                } else {
-                        stream->dnstls_data.validation.data = server->address.in6.s6_addr;
-                        stream->dnstls_data.validation.size = 16;
+                if (server->server_name)
+                        gnutls_session_set_verify_cert(gs, server->server_name, 0);
+                else {
+                        stream->dnstls_data.validation.type = GNUTLS_DT_IP_ADDRESS;
+                        if (server->family == AF_INET) {
+                                stream->dnstls_data.validation.data = (unsigned char*) &server->address.in.s_addr;
+                                stream->dnstls_data.validation.size = 4;
+                        } else {
+                                stream->dnstls_data.validation.data = server->address.in6.s6_addr;
+                                stream->dnstls_data.validation.size = 16;
+                        }
+                        gnutls_session_set_verify_cert2(gs, &stream->dnstls_data.validation, 1, 0);
                 }
-                gnutls_session_set_verify_cert2(gs, &stream->dnstls_data.validation, 1, 0);
         }
 
         if (server->server_name) {
index 8f58efacbdf16cf08960a01f9487ffb06cb41f3c..f95738649df7115ce02af3af612335fc602db05e 100644 (file)
@@ -6,10 +6,12 @@
 
 #include <openssl/bio.h>
 #include <openssl/err.h>
+#include <openssl/x509v3.h>
 
 #include "io-util.h"
 #include "resolved-dns-stream.h"
 #include "resolved-dnstls.h"
+#include "resolved-manager.h"
 
 DEFINE_TRIVIAL_CLEANUP_FUNC(SSL*, SSL_free);
 DEFINE_TRIVIAL_CLEANUP_FUNC(BIO*, BIO_free);
@@ -80,13 +82,19 @@ int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server) {
 
         if (server->manager->dns_over_tls_mode == DNS_OVER_TLS_YES) {
                 X509_VERIFY_PARAM *v;
-                const unsigned char *ip;
 
                 SSL_set_verify(s, SSL_VERIFY_PEER, NULL);
                 v = SSL_get0_param(s);
-                ip = server->family == AF_INET ? (const unsigned char*) &server->address.in.s_addr : server->address.in6.s6_addr;
-                if (X509_VERIFY_PARAM_set1_ip(v, ip, FAMILY_ADDRESS_SIZE(server->family)) == 0)
-                        return -ECONNREFUSED;
+                if (server->server_name) {
+                        X509_VERIFY_PARAM_set_hostflags(v, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
+                        if (X509_VERIFY_PARAM_set1_host(v, server->server_name, 0) == 0)
+                                return -ECONNREFUSED;
+                } else {
+                        const unsigned char *ip;
+                        ip = server->family == AF_INET ? (const unsigned char*) &server->address.in.s_addr : server->address.in6.s6_addr;
+                        if (X509_VERIFY_PARAM_set1_ip(v, ip, FAMILY_ADDRESS_SIZE(server->family)) == 0)
+                                return -ECONNREFUSED;
+                }
         }
 
         if (server->server_name) {
index 1b9121171e98d55f3fa6c04786170857943e5097..6199335b7091682f3b5f8cb8bd5c580ff03fb4c3 100644 (file)
@@ -3,9 +3,14 @@
 
 #if ENABLE_DNS_OVER_TLS
 
+#include <stdint.h>
+
+typedef struct DnsServer DnsServer;
+typedef struct DnsStream DnsStream;
 typedef struct DnsTlsManagerData DnsTlsManagerData;
 typedef struct DnsTlsServerData DnsTlsServerData;
 typedef struct DnsTlsStreamData DnsTlsStreamData;
+typedef struct Manager Manager;
 
 #if DNS_OVER_TLS_USE_GNUTLS
 #include "resolved-dnstls-gnutls.h"
@@ -15,10 +20,6 @@ typedef struct DnsTlsStreamData DnsTlsStreamData;
 #error Unknown dependency for supporting DNS-over-TLS
 #endif
 
-#include "resolved-dns-stream.h"
-#include "resolved-dns-transaction.h"
-#include "resolved-manager.h"
-
 #define DNSTLS_STREAM_CLOSED 1
 
 int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server);
index c2839d425ad256e6eac44fbeb8e7b16a74e2fe6b..6a7f7499572cfc9592e64ab7233468960e9654ee 100644 (file)
@@ -100,7 +100,7 @@ static int parse_line(EtcHosts *hosts, unsigned nr, const char *line) {
 
                 r = extract_first_word(&line, &name, NULL, EXTRACT_RELAX);
                 if (r < 0)
-                        return log_error_errno(r, "/etc/hosts:%u: couldn't extract host name: %m", nr);
+                        return log_error_errno(r, "/etc/hosts:%u: couldn't extract hostname: %m", nr);
                 if (r == 0)
                         break;
 
@@ -120,15 +120,10 @@ static int parse_line(EtcHosts *hosts, unsigned nr, const char *line) {
                         /* Optimize the case where we don't need to store any addresses, by storing
                          * only the name in a dedicated Set instead of the hashmap */
 
-                        r = set_ensure_allocated(&hosts->no_address, &dns_name_hash_ops);
-                        if (r < 0)
-                                return log_oom();
-
-                        r = set_put(hosts->no_address, name);
+                        r = set_ensure_consume(&hosts->no_address, &dns_name_hash_ops, TAKE_PTR(name));
                         if (r < 0)
                                 return r;
 
-                        TAKE_PTR(name);
                         continue;
                 }
 
@@ -162,7 +157,7 @@ static int parse_line(EtcHosts *hosts, unsigned nr, const char *line) {
         }
 
         if (!found)
-                log_warning("/etc/hosts:%u: line is missing any host names", nr);
+                log_warning("/etc/hosts:%u: line is missing any hostnames", nr);
 
         return 0;
 }
index 049fe9ebddafaa3aee42db68fe35ba7b50ddd758..553da8d251826a01f234324617454de67a578d55 100644 (file)
@@ -5,6 +5,7 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"")
 #include <stddef.h>
 #include "conf-parser.h"
 #include "resolved-conf.h"
+#include "resolved-manager.h"
 %}
 struct ConfigPerfItem;
 %null_strings
@@ -17,13 +18,14 @@ struct ConfigPerfItem;
 %struct-type
 %includes
 %%
-Resolve.DNS,             config_parse_dns_servers,            DNS_SERVER_SYSTEM,   0
-Resolve.FallbackDNS,     config_parse_dns_servers,            DNS_SERVER_FALLBACK, 0
-Resolve.Domains,         config_parse_search_domains,         0,                   0
-Resolve.LLMNR,           config_parse_resolve_support,        0,                   offsetof(Manager, llmnr_support)
-Resolve.MulticastDNS,    config_parse_resolve_support,        0,                   offsetof(Manager, mdns_support)
-Resolve.DNSSEC,          config_parse_dnssec_mode,            0,                   offsetof(Manager, dnssec_mode)
-Resolve.DNSOverTLS,      config_parse_dns_over_tls_mode,      0,                   offsetof(Manager, dns_over_tls_mode)
-Resolve.Cache,           config_parse_dns_cache_mode,         DNS_CACHE_MODE_YES,  offsetof(Manager, enable_cache)
-Resolve.DNSStubListener, config_parse_dns_stub_listener_mode, 0,                   offsetof(Manager, dns_stub_listener_mode)
-Resolve.ReadEtcHosts,    config_parse_bool,                   0,                   offsetof(Manager, read_etc_hosts)
\ No newline at end of file
+Resolve.DNS,                       config_parse_dns_servers,            DNS_SERVER_SYSTEM,   0
+Resolve.FallbackDNS,               config_parse_dns_servers,            DNS_SERVER_FALLBACK, 0
+Resolve.Domains,                   config_parse_search_domains,         0,                   0
+Resolve.LLMNR,                     config_parse_resolve_support,        0,                   offsetof(Manager, llmnr_support)
+Resolve.MulticastDNS,              config_parse_resolve_support,        0,                   offsetof(Manager, mdns_support)
+Resolve.DNSSEC,                    config_parse_dnssec_mode,            0,                   offsetof(Manager, dnssec_mode)
+Resolve.DNSOverTLS,                config_parse_dns_over_tls_mode,      0,                   offsetof(Manager, dns_over_tls_mode)
+Resolve.Cache,                     config_parse_dns_cache_mode,         DNS_CACHE_MODE_YES,  offsetof(Manager, enable_cache)
+Resolve.DNSStubListener,           config_parse_dns_stub_listener_mode, 0,                   offsetof(Manager, dns_stub_listener_mode)
+Resolve.ReadEtcHosts,              config_parse_bool,                   0,                   offsetof(Manager, read_etc_hosts)
+Resolve.ResolveUnicastSingleLabel, config_parse_bool,                   0,                   offsetof(Manager, resolve_unicast_single_label)
index 2a166c11b04b65102b0a96bb5a97c4efb0870de5..42d4ae7aaf863ee9f3278d893e4ba943cff566e3 100644 (file)
@@ -6,13 +6,15 @@
 
 #include "alloc-util.h"
 #include "bus-common-errors.h"
+#include "bus-get-properties.h"
+#include "bus-message-util.h"
 #include "bus-polkit.h"
-#include "bus-util.h"
 #include "parse-util.h"
 #include "resolve-util.h"
 #include "resolved-bus.h"
 #include "resolved-link-bus.h"
 #include "resolved-resolv-conf.h"
+#include "socket-netlink.h"
 #include "stdio-util.h"
 #include "strv.h"
 #include "user-util.h"
@@ -37,14 +39,15 @@ static int property_get_dns_over_tls_mode(
         return sd_bus_message_append(reply, "s", dns_over_tls_mode_to_string(link_get_dns_over_tls_mode(l)));
 }
 
-static int property_get_dns(
+static int property_get_dns_internal(
                 sd_bus *bus,
                 const char *path,
                 const char *interface,
                 const char *property,
                 sd_bus_message *reply,
                 void *userdata,
-                sd_bus_error *error) {
+                sd_bus_error *error,
+                bool extended) {
 
         Link *l = userdata;
         DnsServer *s;
@@ -53,12 +56,12 @@ static int property_get_dns(
         assert(reply);
         assert(l);
 
-        r = sd_bus_message_open_container(reply, 'a', "(iay)");
+        r = sd_bus_message_open_container(reply, 'a', extended ? "(iayqs)" : "(iay)");
         if (r < 0)
                 return r;
 
         LIST_FOREACH(servers, s, l->dns_servers) {
-                r = bus_dns_server_append(reply, s, false);
+                r = bus_dns_server_append(reply, s, false, extended);
                 if (r < 0)
                         return r;
         }
@@ -66,7 +69,7 @@ static int property_get_dns(
         return sd_bus_message_close_container(reply);
 }
 
-static int property_get_current_dns_server(
+static int property_get_dns(
                 sd_bus *bus,
                 const char *path,
                 const char *interface,
@@ -74,6 +77,29 @@ static int property_get_current_dns_server(
                 sd_bus_message *reply,
                 void *userdata,
                 sd_bus_error *error) {
+        return property_get_dns_internal(bus, path, interface, property, reply, userdata, error, false);
+}
+
+static int property_get_dns_ex(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+        return property_get_dns_internal(bus, path, interface, property, reply, userdata, error, true);
+}
+
+static int property_get_current_dns_server_internal(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error,
+                bool extended) {
 
         DnsServer *s;
 
@@ -82,7 +108,29 @@ static int property_get_current_dns_server(
 
         s = *(DnsServer **) userdata;
 
-        return bus_dns_server_append(reply, s, false);
+        return bus_dns_server_append(reply, s, false, extended);
+}
+
+static int property_get_current_dns_server(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+        return property_get_current_dns_server_internal(bus, path, interface, property, reply, userdata, error, false);
+}
+
+static int property_get_current_dns_server_ex(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+        return property_get_current_dns_server_internal(bus, path, interface, property, reply, userdata, error, true);
 }
 
 static int property_get_domains(
@@ -204,11 +252,10 @@ static int verify_unmanaged_link(Link *l, sd_bus_error *error) {
         return 0;
 }
 
-int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error) {
-        _cleanup_free_ struct in_addr_data *dns = NULL;
-        size_t allocated = 0, n = 0;
+static int bus_link_method_set_dns_servers_internal(sd_bus_message *message, void *userdata, sd_bus_error *error, bool extended) {
+        struct in_addr_full **dns;
         Link *l = userdata;
-        unsigned i;
+        size_t n;
         int r;
 
         assert(message);
@@ -218,52 +265,7 @@ int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_
         if (r < 0)
                 return r;
 
-        r = sd_bus_message_enter_container(message, 'a', "(iay)");
-        if (r < 0)
-                return r;
-
-        for (;;) {
-                int family;
-                size_t sz;
-                const void *d;
-
-                assert_cc(sizeof(int) == sizeof(int32_t));
-
-                r = sd_bus_message_enter_container(message, 'r', "iay");
-                if (r < 0)
-                        return r;
-                if (r == 0)
-                        break;
-
-                r = sd_bus_message_read(message, "i", &family);
-                if (r < 0)
-                        return r;
-
-                if (!IN_SET(family, AF_INET, AF_INET6))
-                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
-
-                r = sd_bus_message_read_array(message, 'y', &d, &sz);
-                if (r < 0)
-                        return r;
-                if (sz != FAMILY_ADDRESS_SIZE(family))
-                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size");
-
-                if (!dns_server_address_valid(family, d))
-                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNS server address");
-
-                r = sd_bus_message_exit_container(message);
-                if (r < 0)
-                        return r;
-
-                if (!GREEDY_REALLOC(dns, allocated, n+1))
-                        return -ENOMEM;
-
-                dns[n].family = family;
-                memcpy(&dns[n].address, d, sz);
-                n++;
-        }
-
-        r = sd_bus_message_exit_container(message);
+        r = bus_message_read_dns_servers(message, error, extended, &dns, &n);
         if (r < 0)
                 return r;
 
@@ -272,22 +274,26 @@ int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_
                                     NULL, true, UID_INVALID,
                                     &l->manager->polkit_registry, error);
         if (r < 0)
-                return r;
-        if (r == 0)
-                return 1; /* Polkit will call us back */
+                goto finalize;
+        if (r == 0) {
+                r = 1; /* Polkit will call us back */
+                goto finalize;
+        }
 
         dns_server_mark_all(l->dns_servers);
 
-        for (i = 0; i < n; i++) {
+        for (size_t i = 0; i < n; i++) {
                 DnsServer *s;
 
-                s = dns_server_find(l->dns_servers, dns[i].family, &dns[i].address, 0);
+                s = dns_server_find(l->dns_servers, dns[i]->family, &dns[i]->address, dns[i]->port, 0, dns[i]->server_name);
                 if (s)
                         dns_server_move_back_and_unmark(s);
                 else {
-                        r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, dns[i].family, &dns[i].address, 0, NULL);
-                        if (r < 0)
-                                goto clear;
+                        r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, dns[i]->family, &dns[i]->address, dns[i]->port, 0, dns[i]->server_name);
+                        if (r < 0) {
+                                dns_server_unlink_all(l->dns_servers);
+                                goto finalize;
+                        }
                 }
 
         }
@@ -299,13 +305,24 @@ int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_
         (void) manager_write_resolv_conf(l->manager);
         (void) manager_send_changed(l->manager, "DNS");
 
-        return sd_bus_reply_method_return(message, NULL);
+        r = sd_bus_reply_method_return(message, NULL);
+
+finalize:
+        for (size_t i = 0; i < n; i++)
+                in_addr_full_free(dns[i]);
+        free(dns);
 
-clear:
-        dns_server_unlink_all(l->dns_servers);
         return r;
 }
 
+int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        return bus_link_method_set_dns_servers_internal(message, userdata, error, false);
+}
+
+int bus_link_method_set_dns_servers_ex(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        return bus_link_method_set_dns_servers_internal(message, userdata, error, true);
+}
+
 int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         Link *l = userdata;
         int r;
@@ -629,7 +646,7 @@ int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, v
                         return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
                                                  "Invalid negative trust anchor domain: %s", *i);
 
-                r = set_put_strdup(ns, *i);
+                r = set_put_strdup(&ns, *i);
                 if (r < 0)
                         return r;
         }
@@ -682,35 +699,7 @@ int bus_link_method_revert(sd_bus_message *message, void *userdata, sd_bus_error
         return sd_bus_reply_method_return(message, NULL);
 }
 
-const sd_bus_vtable link_vtable[] = {
-        SD_BUS_VTABLE_START(0),
-
-        SD_BUS_PROPERTY("ScopesMask", "t", property_get_scopes_mask, 0, 0),
-        SD_BUS_PROPERTY("DNS", "a(iay)", property_get_dns, 0, 0),
-        SD_BUS_PROPERTY("CurrentDNSServer", "(iay)", property_get_current_dns_server, offsetof(Link, current_dns_server), 0),
-        SD_BUS_PROPERTY("Domains", "a(sb)", property_get_domains, 0, 0),
-        SD_BUS_PROPERTY("DefaultRoute", "b", property_get_default_route, 0, 0),
-        SD_BUS_PROPERTY("LLMNR", "s", bus_property_get_resolve_support, offsetof(Link, llmnr_support), 0),
-        SD_BUS_PROPERTY("MulticastDNS", "s", bus_property_get_resolve_support, offsetof(Link, mdns_support), 0),
-        SD_BUS_PROPERTY("DNSOverTLS", "s", property_get_dns_over_tls_mode, 0, 0),
-        SD_BUS_PROPERTY("DNSSEC", "s", property_get_dnssec_mode, 0, 0),
-        SD_BUS_PROPERTY("DNSSECNegativeTrustAnchors", "as", property_get_ntas, 0, 0),
-        SD_BUS_PROPERTY("DNSSECSupported", "b", property_get_dnssec_supported, 0, 0),
-
-        SD_BUS_METHOD("SetDNS", "a(iay)", NULL, bus_link_method_set_dns_servers, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetDomains", "a(sb)", NULL, bus_link_method_set_domains, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetDefaultRoute", "b", NULL, bus_link_method_set_default_route, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetLLMNR", "s", NULL, bus_link_method_set_llmnr, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetMulticastDNS", "s", NULL, bus_link_method_set_mdns, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetDNSOverTLS", "s", NULL, bus_link_method_set_dns_over_tls, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetDNSSEC", "s", NULL, bus_link_method_set_dnssec, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetDNSSECNegativeTrustAnchors", "as", NULL, bus_link_method_set_dnssec_negative_trust_anchors, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("Revert", NULL, NULL, bus_link_method_revert, SD_BUS_VTABLE_UNPRIVILEGED),
-
-        SD_BUS_VTABLE_END
-};
-
-int link_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
+static int link_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
         _cleanup_free_ char *e = NULL;
         Manager *m = userdata;
         Link *link;
@@ -753,7 +742,7 @@ char *link_bus_path(const Link *link) {
         return p;
 }
 
-int link_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
+static int link_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
         _cleanup_strv_free_ char **l = NULL;
         Manager *m = userdata;
         Link *link;
@@ -784,3 +773,81 @@ int link_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***
 
         return 1;
 }
+
+static const sd_bus_vtable link_vtable[] = {
+        SD_BUS_VTABLE_START(0),
+
+        SD_BUS_PROPERTY("ScopesMask", "t", property_get_scopes_mask, 0, 0),
+        SD_BUS_PROPERTY("DNS", "a(iay)", property_get_dns, 0, 0),
+        SD_BUS_PROPERTY("DNSEx", "a(iayqs)", property_get_dns_ex, 0, 0),
+        SD_BUS_PROPERTY("CurrentDNSServer", "(iay)", property_get_current_dns_server, offsetof(Link, current_dns_server), 0),
+        SD_BUS_PROPERTY("CurrentDNSServerEx", "(iayqs)", property_get_current_dns_server_ex, offsetof(Link, current_dns_server), 0),
+        SD_BUS_PROPERTY("Domains", "a(sb)", property_get_domains, 0, 0),
+        SD_BUS_PROPERTY("DefaultRoute", "b", property_get_default_route, 0, 0),
+        SD_BUS_PROPERTY("LLMNR", "s", bus_property_get_resolve_support, offsetof(Link, llmnr_support), 0),
+        SD_BUS_PROPERTY("MulticastDNS", "s", bus_property_get_resolve_support, offsetof(Link, mdns_support), 0),
+        SD_BUS_PROPERTY("DNSOverTLS", "s", property_get_dns_over_tls_mode, 0, 0),
+        SD_BUS_PROPERTY("DNSSEC", "s", property_get_dnssec_mode, 0, 0),
+        SD_BUS_PROPERTY("DNSSECNegativeTrustAnchors", "as", property_get_ntas, 0, 0),
+        SD_BUS_PROPERTY("DNSSECSupported", "b", property_get_dnssec_supported, 0, 0),
+
+        SD_BUS_METHOD_WITH_ARGS("SetDNS",
+                                SD_BUS_ARGS("a(iay)", addresses),
+                                SD_BUS_NO_RESULT,
+                                bus_link_method_set_dns_servers,
+                                SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_ARGS("SetDNSEx",
+                                SD_BUS_ARGS("a(iayqs)", addresses),
+                                SD_BUS_NO_RESULT,
+                                bus_link_method_set_dns_servers_ex,
+                                SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_ARGS("SetDomains",
+                                SD_BUS_ARGS("a(sb)", domains),
+                                SD_BUS_NO_RESULT,
+                                bus_link_method_set_domains,
+                                SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_ARGS("SetDefaultRoute",
+                                SD_BUS_ARGS("b", enable),
+                                SD_BUS_NO_RESULT,
+                                bus_link_method_set_default_route,
+                                SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_ARGS("SetLLMNR",
+                                SD_BUS_ARGS("s", mode),
+                                SD_BUS_NO_RESULT,
+                                bus_link_method_set_llmnr,
+                                SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_ARGS("SetMulticastDNS",
+                                SD_BUS_ARGS("s", mode),
+                                SD_BUS_NO_RESULT,
+                                bus_link_method_set_mdns,
+                                SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_ARGS("SetDNSOverTLS",
+                                SD_BUS_ARGS("s", mode),
+                                SD_BUS_NO_RESULT,
+                                bus_link_method_set_dns_over_tls,
+                                SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_ARGS("SetDNSSEC",
+                                SD_BUS_ARGS("s", mode),
+                                SD_BUS_NO_RESULT,
+                                bus_link_method_set_dnssec,
+                                SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_ARGS("SetDNSSECNegativeTrustAnchors",
+                                SD_BUS_ARGS("as", names),
+                                SD_BUS_NO_RESULT,
+                                bus_link_method_set_dnssec_negative_trust_anchors,
+                                SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_ARGS("Revert",
+                                SD_BUS_NO_ARGS,
+                                SD_BUS_NO_RESULT,
+                                bus_link_method_revert,
+                                SD_BUS_VTABLE_UNPRIVILEGED),
+
+        SD_BUS_VTABLE_END
+};
+
+const BusObjectImplementation link_object = {
+        "/org/freedesktop/resolve1/link",
+        "org.freedesktop.resolve1.Link",
+        .fallback_vtables = BUS_FALLBACK_VTABLES({link_vtable, link_object_find}),
+        .node_enumerator = link_node_enumerator,
+};
index 74068a4777110b11de319ce349a09a28718a7ccf..fc85ff855c16f83e403906d0aaa05c2cab6f9658 100644 (file)
@@ -3,15 +3,15 @@
 
 #include "sd-bus.h"
 
+#include "bus-util.h"
 #include "resolved-link.h"
 
-extern const sd_bus_vtable link_vtable[];
+extern const BusObjectImplementation link_object;
 
-int link_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error);
 char *link_bus_path(const Link *link);
-int link_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error);
 
 int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_link_method_set_dns_servers_ex(sd_bus_message *message, void *userdata, sd_bus_error *error);
 int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_error *error);
 int bus_link_method_set_default_route(sd_bus_message *message, void *userdata, sd_bus_error *error);
 int bus_link_method_set_llmnr(sd_bus_message *message, void *userdata, sd_bus_error *error);
index f19fc2f3aa12d480188faedea3048c5ff8b7b2b0..f52c556bd13b72c011bc83b8d5fc75b278703eda 100644 (file)
@@ -15,6 +15,7 @@
 #include "resolved-link.h"
 #include "resolved-llmnr.h"
 #include "resolved-mdns.h"
+#include "socket-netlink.h"
 #include "string-util.h"
 #include "strv.h"
 #include "tmpfile-util.h"
@@ -251,25 +252,35 @@ int link_process_rtnl(Link *l, sd_netlink_message *m) {
         return 0;
 }
 
-static int link_update_dns_server_one(Link *l, const char *name) {
+static int link_update_dns_server_one(Link *l, const char *str) {
+        _cleanup_free_ char *name = NULL;
+        int family, ifindex, r;
         union in_addr_union a;
         DnsServer *s;
-        int family, r;
+        uint16_t port;
 
         assert(l);
-        assert(name);
+        assert(str);
 
-        r = in_addr_from_string_auto(name, &family, &a);
+        r = in_addr_port_ifindex_name_from_string_auto(str, &family, &a, &port, &ifindex, &name);
         if (r < 0)
                 return r;
 
-        s = dns_server_find(l->dns_servers, family, &a, 0);
+        if (ifindex != 0 && ifindex != l->ifindex)
+                return -EINVAL;
+
+        /* By default, the port number is determined with the transaction feature level.
+         * See dns_transaction_port() and dns_server_port(). */
+        if (IN_SET(port, 53, 853))
+                port = 0;
+
+        s = dns_server_find(l->dns_servers, family, &a, port, 0, name);
         if (s) {
                 dns_server_move_back_and_unmark(s);
                 return 0;
         }
 
-        return dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, family, &a, 0, NULL);
+        return dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, family, &a, port, 0, name);
 }
 
 static int link_update_dns_servers(Link *l) {
@@ -493,7 +504,7 @@ static int link_update_dnssec_negative_trust_anchors(Link *l) {
         if (!ns)
                 return -ENOMEM;
 
-        r = set_put_strdupv(ns, ntas);
+        r = set_put_strdupv(&ns, ntas);
         if (r < 0)
                 return r;
 
@@ -652,7 +663,9 @@ int link_update(Link *l) {
         assert(l);
 
         link_read_settings(l);
-        link_load_user(l);
+        r = link_load_user(l);
+        if (r < 0)
+                return r;
 
         if (l->llmnr_support != RESOLVE_SUPPORT_NO) {
                 r = manager_llmnr_start(l->manager);
@@ -730,7 +743,7 @@ DnsServer* link_set_dns_server(Link *l, DnsServer *s) {
                 return s;
 
         if (s)
-                log_debug("Switching to DNS server %s for interface %s.", dns_server_string(s), l->ifname);
+                log_debug("Switching to DNS server %s for interface %s.", strna(dns_server_string_full(s)), l->ifname);
 
         dns_server_unref(l->current_dns_server);
         l->current_dns_server = dns_server_ref(s);
@@ -1207,7 +1220,7 @@ int link_save_user(Link *l) {
                         if (server != l->dns_servers)
                                 fputc(' ', f);
 
-                        v = dns_server_string(server);
+                        v = dns_server_string_full(server);
                         if (!v) {
                                 r = -ENOMEM;
                                 goto fail;
index 4b545a553688160503d3423b1bd55e00646c8236..44d489ce4792435fbe61f2e862ca82ffeb21b041 100644 (file)
@@ -1,6 +1,8 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
+#include "sd-netlink.h"
+
 #include "in-addr-util.h"
 #include "ratelimit.h"
 #include "resolve-util.h"
@@ -12,7 +14,6 @@ typedef struct LinkAddress LinkAddress;
 #include "resolved-dns-scope.h"
 #include "resolved-dns-search-domain.h"
 #include "resolved-dns-server.h"
-#include "resolved-manager.h"
 
 #define LINK_SEARCH_DOMAINS_MAX 256
 #define LINK_DNS_SERVERS_MAX 256
index 0d9d1c48e2761e63ca24b390b2eb6524ebd50b2c..1da590b68acc418056c3fdc229e273b14b785f50 100644 (file)
@@ -341,7 +341,7 @@ static int determine_hostname(char **full_hostname, char **llmnr_hostname, char
         p = h;
         r = dns_label_unescape(&p, label, sizeof label, 0);
         if (r < 0)
-                return log_error_errno(r, "Failed to unescape host name: %m");
+                return log_error_errno(r, "Failed to unescape hostname: %m");
         if (r == 0)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                        "Couldn't find a single label in hostname.");
@@ -371,7 +371,7 @@ static int determine_hostname(char **full_hostname, char **llmnr_hostname, char
 
         r = dns_label_escape_new(decoded, r, &n);
         if (r < 0)
-                return log_error_errno(r, "Failed to escape host name: %m");
+                return log_error_errno(r, "Failed to escape hostname: %m");
 
         if (is_localhost(n))
                 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
@@ -411,7 +411,7 @@ static int make_fallback_hostnames(char **full_hostname, char **llmnr_hostname,
         p = fallback_hostname();
         r = dns_label_unescape(&p, label, sizeof label, 0);
         if (r < 0)
-                return log_error_errno(r, "Failed to unescape fallback host name: %m");
+                return log_error_errno(r, "Failed to unescape fallback hostname: %m");
 
         assert(r > 0); /* The fallback hostname must have at least one label */
 
@@ -581,8 +581,8 @@ int manager_new(Manager **ret) {
                 .dns_stub_tcp_fd = -1,
                 .hostname_fd = -1,
 
-                .llmnr_support = RESOLVE_SUPPORT_YES,
-                .mdns_support = RESOLVE_SUPPORT_YES,
+                .llmnr_support = DEFAULT_LLMNR_MODE,
+                .mdns_support = DEFAULT_MDNS_MODE,
                 .dnssec_mode = DEFAULT_DNSSEC_MODE,
                 .dns_over_tls_mode = DEFAULT_DNS_OVER_TLS_MODE,
                 .enable_cache = DNS_CACHE_MODE_YES,
@@ -741,12 +741,9 @@ Manager *manager_free(Manager *m) {
 
 int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret) {
         _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
-        union {
-                struct cmsghdr header; /* For alignment */
-                uint8_t buffer[CMSG_SPACE(MAXSIZE(struct in_pktinfo, struct in6_pktinfo))
-                               + CMSG_SPACE(int) /* ttl/hoplimit */
-                               + EXTRA_CMSG_SPACE /* kernel appears to require extra buffer space */];
-        } control;
+        CMSG_BUFFER_TYPE(CMSG_SPACE(MAXSIZE(struct in_pktinfo, struct in6_pktinfo))
+                         + CMSG_SPACE(int) /* ttl/hoplimit */
+                         + EXTRA_CMSG_SPACE /* kernel appears to require extra buffer space */) control;
         union sockaddr_union sa;
         struct iovec iov;
         struct msghdr mh = {
@@ -930,10 +927,8 @@ static int manager_ipv4_send(
                 uint16_t port,
                 const struct in_addr *source,
                 DnsPacket *p) {
-        union {
-                struct cmsghdr header; /* For alignment */
-                uint8_t buffer[CMSG_SPACE(sizeof(struct in_pktinfo))];
-        } control = {};
+
+        CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct in_pktinfo))) control = {};
         union sockaddr_union sa;
         struct iovec iov;
         struct msghdr mh = {
@@ -988,10 +983,7 @@ static int manager_ipv6_send(
                 const struct in6_addr *source,
                 DnsPacket *p) {
 
-        union {
-                struct cmsghdr header; /* For alignment */
-                uint8_t buffer[CMSG_SPACE(sizeof(struct in6_pktinfo))];
-        } control = {};
+        CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct in6_pktinfo))) control = {};
         union sockaddr_union sa;
         struct iovec iov;
         struct msghdr mh = {
index 446f258b49ba63dd99de01944e6caf31dedde2fa..59944df7469081639d100a6f4268f18a2492b171 100644 (file)
@@ -1,6 +1,8 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
+#include <sys/stat.h>
+
 #include "sd-event.h"
 #include "sd-netlink.h"
 #include "sd-network.h"
@@ -15,10 +17,8 @@ typedef struct Manager Manager;
 #include "resolved-conf.h"
 #include "resolved-dns-query.h"
 #include "resolved-dns-search-domain.h"
-#include "resolved-dns-server.h"
 #include "resolved-dns-stream.h"
 #include "resolved-dns-trust-anchor.h"
-#include "resolved-dnstls.h"
 #include "resolved-link.h"
 
 #define MANAGER_SEARCH_DOMAINS_MAX 256
@@ -70,10 +70,11 @@ struct Manager {
         LIST_HEAD(DnsSearchDomain, search_domains);
         unsigned n_search_domains;
 
-        bool need_builtin_fallbacks:1;
+        bool need_builtin_fallbacks;
+        bool read_resolv_conf;
+        bool resolve_unicast_single_label;
 
-        bool read_resolv_conf:1;
-        usec_t resolv_conf_mtime;
+        struct stat resolv_conf_stat;
 
         DnsTrustAnchor trust_anchor;
 
@@ -167,6 +168,7 @@ void manager_verify_all(Manager *m);
 
 DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
 
+/* For some reason we need some extra cmsg space on some kernels/archs. One of those days we need to figure out why */
 #define EXTRA_CMSG_SPACE 1024
 
 int manager_is_own_hostname(Manager *m, const char *name);
index 1896ce3379c9e45d964f50d66e9ac4c17141f34e..c6f48d6d8856ed7b5333fa0667c01602ad69b881 100644 (file)
@@ -9,59 +9,41 @@
 #include "dns-domain.h"
 #include "fd-util.h"
 #include "fileio.h"
+#include "fs-util.h"
 #include "ordered-set.h"
 #include "resolved-conf.h"
 #include "resolved-dns-server.h"
 #include "resolved-resolv-conf.h"
+#include "stat-util.h"
 #include "string-util.h"
 #include "strv.h"
 #include "tmpfile-util-label.h"
 
-/* A resolv.conf file containing the DNS server and domain data we learnt from uplink, i.e. the full uplink data */
-#define PRIVATE_UPLINK_RESOLV_CONF "/run/systemd/resolve/resolv.conf"
-
-/* A resolv.conf file containing the domain data we learnt from uplink, but our own DNS server address. */
-#define PRIVATE_STUB_RESOLV_CONF "/run/systemd/resolve/stub-resolv.conf"
-
-/* A static resolv.conf file containing no domains, but only our own DNS server address */
-#define PRIVATE_STATIC_RESOLV_CONF ROOTLIBEXECDIR "/resolv.conf"
-
 int manager_check_resolv_conf(const Manager *m) {
-        const char *path;
-        struct stat st;
-        int r;
+        struct stat st, own;
 
         assert(m);
 
         /* This warns only when our stub listener is disabled and /etc/resolv.conf is a symlink to
-         * PRIVATE_STATIC_RESOLV_CONF or PRIVATE_STUB_RESOLV_CONF. */
+         * PRIVATE_STATIC_RESOLV_CONF. */
 
         if (m->dns_stub_listener_mode != DNS_STUB_LISTENER_NO)
                 return 0;
 
-        r = stat("/etc/resolv.conf", &st);
-        if (r < 0) {
+        if (stat("/etc/resolv.conf", &st) < 0) {
                 if (errno == ENOENT)
                         return 0;
 
                 return log_warning_errno(errno, "Failed to stat /etc/resolv.conf: %m");
         }
 
-        FOREACH_STRING(path,
-                       PRIVATE_STUB_RESOLV_CONF,
-                       PRIVATE_STATIC_RESOLV_CONF) {
-
-                struct stat own;
-
-                /* Is it symlinked to our own uplink file? */
-                if (stat(path, &own) >= 0 &&
-                    st.st_dev == own.st_dev &&
-                    st.st_ino == own.st_ino) {
-                        log_warning("DNSStubListener= is disabled, but /etc/resolv.conf is a symlink to %s "
-                                    "which expects DNSStubListener= to be enabled.", path);
-                        return -EOPNOTSUPP;
-                }
-        }
+        /* Is it symlinked to our own uplink file? */
+        if (stat(PRIVATE_STATIC_RESOLV_CONF, &own) >= 0 &&
+            st.st_dev == own.st_dev &&
+            st.st_ino == own.st_ino)
+                return log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+                                         "DNSStubListener= is disabled, but /etc/resolv.conf is a symlink to "
+                                         PRIVATE_STATIC_RESOLV_CONF " which expects DNSStubListener= to be enabled.");
 
         return 0;
 }
@@ -112,7 +94,7 @@ int manager_read_resolv_conf(Manager *m) {
         }
 
         /* Have we already seen the file? */
-        if (timespec_load(&st.st_mtim) == m->resolv_conf_mtime)
+        if (stat_inode_unmodified(&st, &m->resolv_conf_stat))
                 return 0;
 
         if (file_is_our_own(&st))
@@ -178,7 +160,7 @@ int manager_read_resolv_conf(Manager *m) {
                 log_syntax(NULL, LOG_DEBUG, "/etc/resolv.conf", n, 0, "Ignoring resolv.conf line: %s", l);
         }
 
-        m->resolv_conf_mtime = timespec_load(&st.st_mtim);
+        m->resolv_conf_stat = st;
 
         /* Flush out all servers and search domains that are still
          * marked. Those are then ones that didn't appear in the new
@@ -355,45 +337,49 @@ int manager_write_resolv_conf(Manager *m) {
 
         r = fopen_temporary_label(PRIVATE_UPLINK_RESOLV_CONF, PRIVATE_UPLINK_RESOLV_CONF, &f_uplink, &temp_path_uplink);
         if (r < 0)
-                return log_warning_errno(r, "Failed to open private resolv.conf file for writing: %m");
+                return log_warning_errno(r, "Failed to open new %s for writing: %m", PRIVATE_UPLINK_RESOLV_CONF);
 
         (void) fchmod(fileno(f_uplink), 0644);
 
-        r = fopen_temporary_label(PRIVATE_STUB_RESOLV_CONF, PRIVATE_STUB_RESOLV_CONF, &f_stub, &temp_path_stub);
-        if (r < 0)
-                return log_warning_errno(r, "Failed to open private stub-resolv.conf file for writing: %m");
-
-        (void) fchmod(fileno(f_stub), 0644);
-
         r = write_uplink_resolv_conf_contents(f_uplink, dns, domains);
         if (r < 0) {
-                log_error_errno(r, "Failed to write private resolv.conf contents: %m");
+                log_error_errno(r, "Failed to write new %s: %m", PRIVATE_UPLINK_RESOLV_CONF);
                 goto fail;
         }
 
-        if (rename(temp_path_uplink, PRIVATE_UPLINK_RESOLV_CONF) < 0) {
-                r = log_error_errno(errno, "Failed to move private resolv.conf file into place: %m");
-                goto fail;
-        }
+        if (m->dns_stub_listener_mode != DNS_STUB_LISTENER_NO) {
+                r = fopen_temporary_label(PRIVATE_STUB_RESOLV_CONF, PRIVATE_STUB_RESOLV_CONF, &f_stub, &temp_path_stub);
+                if (r < 0) {
+                        log_warning_errno(r, "Failed to open new %s for writing: %m", PRIVATE_STUB_RESOLV_CONF);
+                        goto fail;
+                }
 
-        r = write_stub_resolv_conf_contents(f_stub, dns, domains);
-        if (r < 0) {
-                log_error_errno(r, "Failed to write private stub-resolv.conf contents: %m");
-                goto fail;
-        }
+                (void) fchmod(fileno(f_stub), 0644);
 
-        if (rename(temp_path_stub, PRIVATE_STUB_RESOLV_CONF) < 0) {
-                r = log_error_errno(errno, "Failed to move private stub-resolv.conf file into place: %m");
-                goto fail;
+                r = write_stub_resolv_conf_contents(f_stub, dns, domains);
+                if (r < 0) {
+                        log_error_errno(r, "Failed to write new %s: %m", PRIVATE_STUB_RESOLV_CONF);
+                        goto fail;
+                }
+
+                if (rename(temp_path_stub, PRIVATE_STUB_RESOLV_CONF) < 0)
+                        r = log_error_errno(errno, "Failed to move new %s into place: %m", PRIVATE_STUB_RESOLV_CONF);
+
+        } else {
+                r = symlink_atomic(basename(PRIVATE_UPLINK_RESOLV_CONF), PRIVATE_STUB_RESOLV_CONF);
+                if (r < 0)
+                        log_error_errno(r, "Failed to symlink %s: %m", PRIVATE_STUB_RESOLV_CONF);
         }
 
-        return 0;
+        if (rename(temp_path_uplink, PRIVATE_UPLINK_RESOLV_CONF) < 0)
+                r = log_error_errno(errno, "Failed to move new %s into place: %m", PRIVATE_UPLINK_RESOLV_CONF);
 
-fail:
-        (void) unlink(PRIVATE_UPLINK_RESOLV_CONF);
-        (void) unlink(temp_path_uplink);
-        (void) unlink(PRIVATE_STUB_RESOLV_CONF);
-        (void) unlink(temp_path_stub);
+ fail:
+        if (r < 0) {
+                /* Something went wrong, perform cleanup... */
+                (void) unlink(temp_path_uplink);
+                (void) unlink(temp_path_stub);
+        }
 
         return r;
 }
diff --git a/src/resolve/resolved-util.c b/src/resolve/resolved-util.c
deleted file mode 100644 (file)
index ac8df63..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
-
-#include "alloc-util.h"
-#include "macro.h"
-#include "resolved-util.h"
-#include "socket-netlink.h"
-
-int in_addr_ifindex_name_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex, char **server_name) {
-        _cleanup_free_ char *buf = NULL, *name = NULL;
-        const char *m;
-        int r;
-
-        assert(s);
-
-        m = strchr(s, '#');
-        if (m) {
-                name = strdup(m+1);
-                if (!name)
-                        return -ENOMEM;
-
-                buf = strndup(s, m - s);
-                if (!buf)
-                        return -ENOMEM;
-
-                s = buf;
-        }
-
-        r = in_addr_ifindex_from_string_auto(s, family, ret, ifindex);
-        if (r < 0)
-                return r;
-
-        if (server_name)
-                *server_name = TAKE_PTR(name);
-
-        return r;
-}
diff --git a/src/resolve/resolved-util.h b/src/resolve/resolved-util.h
deleted file mode 100644 (file)
index 10ebbc0..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
-#pragma once
-
-#include "in-addr-util.h"
-
-int in_addr_ifindex_name_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex, char **server_name);
index 27848cccae7bc3e854489301a92cdd3c069e79e3..16477f28d69e73d30638689a81a76f254ab6c639 100644 (file)
@@ -7,32 +7,40 @@
 #include "sd-daemon.h"
 #include "sd-event.h"
 
+#include "bus-log-control-api.h"
 #include "capability-util.h"
 #include "daemon-util.h"
 #include "main-func.h"
 #include "mkdir.h"
+#include "resolved-bus.h"
 #include "resolved-conf.h"
 #include "resolved-manager.h"
 #include "resolved-resolv-conf.h"
 #include "selinux-util.h"
+#include "service-util.h"
 #include "signal-util.h"
 #include "user-util.h"
 
 static int run(int argc, char *argv[]) {
-        _cleanup_(notify_on_cleanup) const char *notify_stop = NULL;
         _cleanup_(manager_freep) Manager *m = NULL;
+        _cleanup_(notify_on_cleanup) const char *notify_stop = NULL;
         int r;
 
         log_setup_service();
 
-        if (argc != 1)
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments.");
+        r = service_parse_argv("systemd-resolved.service",
+                               "Provide name resolution with caching using DNS, mDNS, LLMNR.",
+                               BUS_IMPLEMENTATIONS(&manager_object,
+                                                   &log_control_object),
+                               argc, argv);
+        if (r <= 0)
+                return r;
 
         umask(0022);
 
         r = mac_selinux_init();
         if (r < 0)
-                return log_error_errno(r, "SELinux setup failed: %m");
+                return r;
 
         /* Drop privileges, but only if we have been started as root. If we are not running as root we assume most
          * privileges are already dropped and we can't create our directory. */
index 6898c7848be92c1181824e04a4a9cc7c09e4e1de..082ad7162610993c8098fcefa01d8c464ec00f07 100644 (file)
 #DNS=
 #FallbackDNS=@DNS_SERVERS@
 #Domains=
-#LLMNR=yes
-#MulticastDNS=yes
 #DNSSEC=@DEFAULT_DNSSEC_MODE@
 #DNSOverTLS=@DEFAULT_DNS_OVER_TLS_MODE@
+#MulticastDNS=@DEFAULT_MDNS_MODE@
+#LLMNR=@DEFAULT_LLMNR_MODE@
 #Cache=yes
 #DNSStubListener=yes
 #ReadEtcHosts=yes
+#ResolveUnicastSingleLabel=no
index 7c6346cb66a5066ee776f4aa221942504601b8b8..bdd96aa9a20241055f6fd0515714a8d10ddc9c39 100644 (file)
@@ -92,7 +92,6 @@ static void test_packet_from_file(const char* filename, bool canonical) {
 
 int main(int argc, char **argv) {
         int i, N;
-        _cleanup_free_ char *pkts_glob = NULL;
         _cleanup_globfree_ glob_t g = {};
         char **fnames;
 
@@ -102,7 +101,8 @@ int main(int argc, char **argv) {
                 N = argc - 1;
                 fnames = argv + 1;
         } else {
-                pkts_glob = path_join(get_testdata_dir(), "test-resolve/*.pkts");
+                _cleanup_free_ char *pkts_glob = NULL;
+                assert_se(get_testdata_dir("test-resolve/*.pkts", &pkts_glob) >= 0);
                 assert_se(glob(pkts_glob, GLOB_NOSORT, NULL, &g) == 0);
                 N = g.gl_pathc;
                 fnames = g.gl_pathv;
index 840c4fa1db6e2c2149541e42ce24c82e2e4c1f5a..213177d4da646bcd9f9236ece374009e537835ef 100644 (file)
 #include "string-util.h"
 #include "hexdecoct.h"
 
-static void test_dnssec_canonicalize_one(const char *original, const char *canonical, int r) {
-        char canonicalized[DNSSEC_CANONICAL_HOSTNAME_MAX];
-
-        assert_se(dnssec_canonicalize(original, canonicalized, sizeof(canonicalized)) == r);
-        if (r < 0)
-                return;
-
-        assert_se(streq(canonicalized, canonical));
-}
-
-static void test_dnssec_canonicalize(void) {
-        test_dnssec_canonicalize_one("", ".", 1);
-        test_dnssec_canonicalize_one(".", ".", 1);
-        test_dnssec_canonicalize_one("foo", "foo.", 4);
-        test_dnssec_canonicalize_one("foo.", "foo.", 4);
-        test_dnssec_canonicalize_one("FOO.", "foo.", 4);
-        test_dnssec_canonicalize_one("FOO.bar.", "foo.bar.", 8);
-        test_dnssec_canonicalize_one("FOO..bar.", NULL, -EINVAL);
-}
-
 #if HAVE_GCRYPT
 
 static void test_dnssec_verify_dns_key(void) {
@@ -463,6 +443,136 @@ static void test_dnssec_verify_rrset2(void) {
         assert_se(result == DNSSEC_VALIDATED);
 }
 
+static void test_dnssec_verify_rrset3(void) {
+
+        static const uint8_t signature_blob[] = {
+                0x41, 0x09, 0x08, 0x67, 0x51, 0x6d, 0x02, 0xf2, 0x17, 0x1e, 0x61, 0x03, 0xc6, 0x80, 0x7a, 0x82,
+                0x8f, 0x6c, 0x8c, 0x4c, 0x68, 0x6f, 0x1c, 0xaa, 0x4a, 0xe0, 0x9b, 0x72, 0xdf, 0x7f, 0x15, 0xfa,
+                0x2b, 0xc5, 0x63, 0x6f, 0x52, 0xa2, 0x60, 0x59, 0x24, 0xb6, 0xc3, 0x43, 0x3d, 0x47, 0x38, 0xd8,
+                0x0c, 0xcc, 0x6c, 0x10, 0x49, 0x92, 0x97, 0x6c, 0x7d, 0x32, 0xc2, 0x62, 0x83, 0x34, 0x96, 0xdf,
+                0xbd, 0xf9, 0xcc, 0xcf, 0xd9, 0x4d, 0x8b, 0x8a, 0xa9, 0x3c, 0x1f, 0x89, 0xc4, 0xad, 0xd5, 0xbb,
+                0x74, 0xf8, 0xee, 0x60, 0x54, 0x7a, 0xec, 0x36, 0x45, 0xf2, 0xec, 0xb9, 0x73, 0x66, 0xae, 0x57,
+                0x2d, 0xd4, 0x91, 0x02, 0x99, 0xcd, 0xba, 0xbd, 0x6e, 0xfb, 0xa6, 0xf6, 0x34, 0xce, 0x4c, 0x44,
+                0x0b, 0xd2, 0x66, 0xdb, 0x4e, 0x5e, 0x00, 0x72, 0x1b, 0xe5, 0x2f, 0x24, 0xd2, 0xc8, 0x72, 0x37,
+                0x97, 0x2b, 0xd0, 0xcd, 0xa9, 0x6b, 0x84, 0x32, 0x56, 0x7a, 0x89, 0x6e, 0x3d, 0x8f, 0x03, 0x9a,
+                0x9d, 0x6d, 0xf7, 0xe5, 0x13, 0xd7, 0x4b, 0xbc, 0xe2, 0x6c, 0xd1, 0x18, 0x60, 0x0e, 0x1a, 0xe3,
+                0xf9, 0xc0, 0x34, 0x4b, 0x1c, 0x82, 0x17, 0x5e, 0xdf, 0x81, 0x32, 0xd7, 0x5b, 0x30, 0x1d, 0xe0,
+                0x29, 0x80, 0x6b, 0xb1, 0x69, 0xbf, 0x3f, 0x12, 0x56, 0xb0, 0x80, 0x91, 0x22, 0x1a, 0x31, 0xd5,
+                0x5d, 0x3d, 0xdd, 0x70, 0x5e, 0xcb, 0xc7, 0x2d, 0xb8, 0x3e, 0x54, 0x34, 0xd3, 0x50, 0x89, 0x77,
+                0x08, 0xc1, 0xf7, 0x11, 0x6e, 0x57, 0xd7, 0x09, 0x94, 0x20, 0x03, 0x38, 0xc3, 0x3a, 0xd3, 0x93,
+                0x8f, 0xd0, 0x65, 0xc5, 0xa1, 0xe0, 0x69, 0x2c, 0xf6, 0x0a, 0xce, 0x01, 0xb6, 0x0d, 0x95, 0xa0,
+                0x5d, 0x97, 0x94, 0xc3, 0xf1, 0xcd, 0x49, 0xea, 0x20, 0xd3, 0xa9, 0xa6, 0x67, 0x94, 0x64, 0x17
+        };
+
+        static const uint8_t dnskey_blob[] = {
+                0x03, 0x01, 0x00, 0x01, 0xbf, 0xdd, 0x24, 0x95, 0x21, 0x70, 0xa8, 0x5b, 0x19, 0xa6, 0x76, 0xd3,
+                0x5b, 0x37, 0xcf, 0x59, 0x0d, 0x3c, 0xdb, 0x0c, 0xcf, 0xd6, 0x19, 0x02, 0xc7, 0x8e, 0x56, 0x4d,
+                0x14, 0xb7, 0x9d, 0x71, 0xf4, 0xdd, 0x24, 0x36, 0xc8, 0x32, 0x1c, 0x63, 0xf7, 0xc0, 0xfc, 0xe3,
+                0x83, 0xa6, 0x22, 0x8b, 0x6a, 0x34, 0x41, 0x72, 0xaa, 0x95, 0x98, 0x06, 0xac, 0x03, 0xec, 0xc3,
+                0xa1, 0x6d, 0x8b, 0x1b, 0xfd, 0xa4, 0x05, 0x72, 0xe6, 0xe0, 0xb9, 0x98, 0x07, 0x54, 0x7a, 0xb2,
+                0x55, 0x30, 0x96, 0xa3, 0x22, 0x3b, 0xe0, 0x9d, 0x61, 0xf6, 0xdc, 0x31, 0x2b, 0xc9, 0x2c, 0x12,
+                0x06, 0x7f, 0x3c, 0x5d, 0x29, 0x76, 0x01, 0x62, 0xe3, 0x41, 0x41, 0x4f, 0xa6, 0x07, 0xfa, 0x2d,
+                0x0c, 0x64, 0x88, 0xd1, 0x56, 0x18, 0x4b, 0x2b, 0xc2, 0x19, 0x7e, 0xd0, 0x1a, 0x8c, 0x2d, 0x8d,
+                0x06, 0xdf, 0x4d, 0xaf, 0xd9, 0xe3, 0x31, 0x59, 0xbc, 0xc3, 0x36, 0x22, 0xe7, 0x15, 0xf9, 0xb2,
+                0x44, 0x8a, 0x33, 0xd7, 0x6c, 0xf1, 0xcc, 0x37, 0x05, 0x69, 0x32, 0x71, 0x76, 0xd8, 0x50, 0x06,
+                0xae, 0x27, 0xed, 0x3b, 0xdb, 0x1a, 0x97, 0x9b, 0xa3, 0x3e, 0x40, 0x42, 0x29, 0xaf, 0x75, 0x1c,
+                0xff, 0x1d, 0xaf, 0x85, 0x02, 0xb3, 0x2e, 0x99, 0x67, 0x08, 0x13, 0xd5, 0xda, 0x6d, 0x65, 0xb2,
+                0x36, 0x6f, 0x2f, 0x64, 0xe0, 0xfa, 0xd3, 0x81, 0x86, 0x6b, 0x41, 0x3e, 0x91, 0xaa, 0x0a, 0xd3,
+                0xb2, 0x92, 0xd9, 0x42, 0x36, 0x8a, 0x11, 0x0b, 0x5b, 0xb0, 0xea, 0xad, 0x76, 0xd5, 0xb4, 0x81,
+                0x30, 0xca, 0x5c, 0x4f, 0xd9, 0xea, 0xe7, 0x4b, 0x10, 0x0a, 0x09, 0x4b, 0x73, 0x66, 0xed, 0x8e,
+                0x84, 0xa2, 0x4f, 0x93, 0x7e, 0x29, 0xdc, 0x6a, 0xbd, 0x12, 0xa1, 0x3d, 0xd2, 0xd6, 0x2a, 0x67,
+                0x99, 0x4d, 0xf3, 0x43
+        };
+
+        _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *mx1 = NULL, *mx2 = NULL, *mx3 = NULL, *mx4 = NULL, *rrsig = NULL, *dnskey = NULL;
+        _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
+        DnssecResult result;
+
+        mx1 = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_MX, "kodapan.se");
+        assert_se(mx1);
+
+        mx1->mx.priority = 1;
+        mx1->mx.exchange = strdup("ASPMX.L.GOOGLE.COM");
+        assert_se(mx1->mx.exchange);
+
+        log_info("MX: %s", strna(dns_resource_record_to_string(mx1)));
+
+        mx2 = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_MX, "kodapan.se");
+        assert_se(mx2);
+
+        mx2->mx.priority = 5;
+        mx2->mx.exchange = strdup("ALT2.ASPMX.L.GOOGLE.COM");
+        assert_se(mx2->mx.exchange);
+
+        log_info("MX: %s", strna(dns_resource_record_to_string(mx2)));
+
+        mx3 = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_MX, "kodapan.se");
+        assert_se(mx3);
+
+        mx3->mx.priority = 10;
+        mx3->mx.exchange = strdup("ASPMX2.GOOGLEMAIL.COM");
+        assert_se(mx3->mx.exchange);
+
+        log_info("MX: %s", strna(dns_resource_record_to_string(mx3)));
+
+        mx4 = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_MX, "kodapan.se");
+        assert_se(mx4);
+
+        mx4->mx.priority = 10;
+        mx4->mx.exchange = strdup("ASPMX3.GOOGLEMAIL.COM");
+        assert_se(mx4->mx.exchange);
+
+        log_info("MX: %s", strna(dns_resource_record_to_string(mx4)));
+
+        rrsig = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_RRSIG, "kodapan.se");
+        assert_se(rrsig);
+
+        rrsig->rrsig.type_covered = DNS_TYPE_MX;
+        rrsig->rrsig.algorithm = DNSSEC_ALGORITHM_RSASHA256;
+        rrsig->rrsig.labels = 2;
+        rrsig->rrsig.original_ttl = 900;
+        rrsig->rrsig.expiration = 0x5e608a84;
+        rrsig->rrsig.inception = 0x5e4e1584;
+        rrsig->rrsig.key_tag = 44028;
+        rrsig->rrsig.signer = strdup("kodapan.se.");
+        assert_se(rrsig->rrsig.signer);
+        rrsig->rrsig.signature_size = sizeof(signature_blob);
+        rrsig->rrsig.signature = memdup(signature_blob, rrsig->rrsig.signature_size);
+        assert_se(rrsig->rrsig.signature);
+
+        log_info("RRSIG: %s", strna(dns_resource_record_to_string(rrsig)));
+
+        dnskey = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNSKEY, "kodapan.se");
+        assert_se(dnskey);
+
+        dnskey->dnskey.flags = 256;
+        dnskey->dnskey.protocol = 3;
+        dnskey->dnskey.algorithm = DNSSEC_ALGORITHM_RSASHA256;
+        dnskey->dnskey.key_size = sizeof(dnskey_blob);
+        dnskey->dnskey.key = memdup(dnskey_blob, sizeof(dnskey_blob));
+        assert_se(dnskey->dnskey.key);
+
+        log_info("DNSKEY: %s", strna(dns_resource_record_to_string(dnskey)));
+        log_info("DNSKEY keytag: %u", dnssec_keytag(dnskey, false));
+
+        assert_se(dnssec_key_match_rrsig(mx1->key, rrsig) > 0);
+        assert_se(dnssec_key_match_rrsig(mx2->key, rrsig) > 0);
+        assert_se(dnssec_key_match_rrsig(mx3->key, rrsig) > 0);
+        assert_se(dnssec_key_match_rrsig(mx4->key, rrsig) > 0);
+        assert_se(dnssec_rrsig_match_dnskey(rrsig, dnskey, false) > 0);
+
+        answer = dns_answer_new(4);
+        assert_se(answer);
+        assert_se(dns_answer_add(answer, mx1, 0, DNS_ANSWER_AUTHENTICATED) >= 0);
+        assert_se(dns_answer_add(answer, mx2, 0, DNS_ANSWER_AUTHENTICATED) >= 0);
+        assert_se(dns_answer_add(answer, mx3, 0, DNS_ANSWER_AUTHENTICATED) >= 0);
+        assert_se(dns_answer_add(answer, mx4, 0, DNS_ANSWER_AUTHENTICATED) >= 0);
+
+        /* Validate the RR as it if was 2020-02-24 today */
+        assert_se(dnssec_verify_rrset(answer, mx1->key, rrsig, dnskey, 1582534685*USEC_PER_SEC, &result) >= 0);
+        assert_se(result == DNSSEC_VALIDATED);
+}
+
 static void test_dnssec_nsec3_hash(void) {
         static const uint8_t salt[] = { 0xB0, 0x1D, 0xFA, 0xCE };
         static const uint8_t next_hashed_name[] = { 0x84, 0x10, 0x26, 0x53, 0xc9, 0xfa, 0x4d, 0x85, 0x6c, 0x97, 0x82, 0xe2, 0x8f, 0xdf, 0x2d, 0x5e, 0x87, 0x69, 0xc4, 0x52 };
@@ -499,14 +609,13 @@ static void test_dnssec_nsec3_hash(void) {
 
 int main(int argc, char *argv[]) {
 
-        test_dnssec_canonicalize();
-
 #if HAVE_GCRYPT
         test_dnssec_verify_dns_key();
         test_dnssec_verify_rfc8080_ed25519_example1();
         test_dnssec_verify_rfc8080_ed25519_example2();
         test_dnssec_verify_rrset();
         test_dnssec_verify_rrset2();
+        test_dnssec_verify_rrset3();
         test_dnssec_nsec3_hash();
 #endif
 
index ca3590066c83e7260de5849a27d67440e7452207..721bf8732ef620461eb94a9525752af2df2027c2 100644 (file)
@@ -65,7 +65,7 @@ static void test_parse_etc_hosts(void) {
               "1::2::3 multi.colon\n"
 
               "::0 some.where some.other\n"
-              "0.0.0.0 black.listed\n"
+              "0.0.0.0 deny.listed\n"
               "::5\t\t\t \tsome.where\tsome.other foobar.foo.foo\t\t\t\n"
               "        \n", f);
         assert_se(fflush_and_check(f) >= 0);
@@ -123,7 +123,7 @@ static void test_parse_etc_hosts(void) {
 
         assert_se( set_contains(hosts.no_address, "some.where"));
         assert_se( set_contains(hosts.no_address, "some.other"));
-        assert_se( set_contains(hosts.no_address, "black.listed"));
+        assert_se( set_contains(hosts.no_address, "deny.listed"));
         assert_se(!set_contains(hosts.no_address, "foobar.foo.foo"));
 }
 
diff --git a/src/resolve/test-resolved-util.c b/src/resolve/test-resolved-util.c
deleted file mode 100644 (file)
index 35bd73c..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
-
-#include "log.h"
-#include "resolved-util.h"
-#include "string-util.h"
-#include "tests.h"
-
-
-static void test_in_addr_ifindex_name_from_string_auto_one(const char *a, const char *expected) {
-        int family, ifindex;
-        union in_addr_union ua;
-        _cleanup_free_ char *server_name = NULL;
-
-        assert_se(in_addr_ifindex_name_from_string_auto(a, &family, &ua, &ifindex, &server_name) >= 0);
-        assert_se(streq_ptr(server_name, expected));
-}
-
-static void test_in_addr_ifindex_name_from_string_auto(void) {
-        log_info("/* %s */", __func__);
-
-        test_in_addr_ifindex_name_from_string_auto_one("192.168.0.1", NULL);
-        test_in_addr_ifindex_name_from_string_auto_one("192.168.0.1#test.com", "test.com");
-        test_in_addr_ifindex_name_from_string_auto_one("fe80::18%19", NULL);
-        test_in_addr_ifindex_name_from_string_auto_one("fe80::18%19#another.test.com", "another.test.com");
-}
-
-int main(int argc, char **argv) {
-        test_setup_logging(LOG_DEBUG);
-
-        test_in_addr_ifindex_name_from_string_auto();
-        return 0;
-}
index 3b21101297c76468c9c5413b6f99c38cef73188b..d4ce3966e7649ca041d95ecad60f4d24468e8c77 100644 (file)
@@ -11,8 +11,9 @@
 
 #include "alloc-util.h"
 #include "bus-error.h"
+#include "bus-locator.h"
+#include "bus-map-properties.h"
 #include "bus-unit-util.h"
-#include "bus-util.h"
 #include "bus-wait-for-jobs.h"
 #include "calendarspec.h"
 #include "env-util.h"
@@ -41,6 +42,7 @@ static bool arg_wait = false;
 static const char *arg_unit = NULL;
 static const char *arg_description = NULL;
 static const char *arg_slice = NULL;
+static bool arg_slice_inherit = false;
 static bool arg_send_sighup = false;
 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
 static const char *arg_host = NULL;
@@ -97,6 +99,7 @@ static int help(void) {
                "  -p --property=NAME=VALUE        Set service or scope unit property\n"
                "     --description=TEXT           Description for unit\n"
                "     --slice=SLICE                Run in the specified slice\n"
+               "     --slice-inherit              Inherit the slice\n"
                "     --no-block                   Do not wait until operation finished\n"
                "  -r --remain-after-exit          Leave service around until explicitly stopped\n"
                "     --wait                       Wait until service stopped again\n"
@@ -162,6 +165,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_SCOPE,
                 ARG_DESCRIPTION,
                 ARG_SLICE,
+                ARG_SLICE_INHERIT,
                 ARG_SEND_SIGHUP,
                 ARG_SERVICE_TYPE,
                 ARG_EXEC_USER,
@@ -194,6 +198,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "unit",              required_argument, NULL, 'u'                   },
                 { "description",       required_argument, NULL, ARG_DESCRIPTION       },
                 { "slice",             required_argument, NULL, ARG_SLICE             },
+                { "slice-inherit",     no_argument,       NULL, ARG_SLICE_INHERIT     },
                 { "remain-after-exit", no_argument,       NULL, 'r'                   },
                 { "send-sighup",       no_argument,       NULL, ARG_SEND_SIGHUP       },
                 { "host",              required_argument, NULL, 'H'                   },
@@ -273,6 +278,10 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_slice = optarg;
                         break;
 
+                case ARG_SLICE_INHERIT:
+                        arg_slice_inherit = true;
+                        break;
+
                 case ARG_SEND_SIGHUP:
                         arg_send_sighup = true;
                         break;
@@ -637,23 +646,50 @@ static int transient_unit_set_properties(sd_bus_message *m, UnitType t, char **p
 }
 
 static int transient_cgroup_set_properties(sd_bus_message *m) {
+        _cleanup_free_ char *name = NULL;
+        _cleanup_free_ char *slice = NULL;
         int r;
         assert(m);
 
-        if (!isempty(arg_slice)) {
-                _cleanup_free_ char *slice = NULL;
+        if (arg_slice_inherit) {
+                char *end;
 
-                r = unit_name_mangle_with_suffix(arg_slice, "as slice",
-                                                 arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN,
-                                                 ".slice", &slice);
+                if (arg_user)
+                        r = cg_pid_get_user_slice(0, &name);
+                else
+                        r = cg_pid_get_slice(0, &name);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to mangle name '%s': %m", arg_slice);
+                        return log_error_errno(r, "Failed to get PID slice: %m");
 
-                r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
-                if (r < 0)
-                        return bus_log_create_error(r);
+                end = endswith(name, ".slice");
+                if (!end)
+                        return -ENXIO;
+                *end = 0;
         }
 
+        if (!isempty(arg_slice)) {
+                if (name) {
+                        char *j = strjoin(name, "-", arg_slice);
+                        free_and_replace(name, j);
+                } else
+                        name = strdup(arg_slice);
+                if (!name)
+                        return log_oom();
+        }
+
+        if (!name)
+                return 0;
+
+        r = unit_name_mangle_with_suffix(name, "as slice",
+                                         arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN,
+                                         ".slice", &slice);
+        if (r < 0)
+                return log_error_errno(r, "Failed to mangle name '%s': %m", arg_slice);
+
+        r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
+        if (r < 0)
+                return bus_log_create_error(r);
+
         return 0;
 }
 
@@ -943,8 +979,11 @@ typedef struct RunContext {
         PTYForward *forward;
         sd_bus_slot *match;
 
-        /* The exit data of the unit */
+        /* Current state of the unit */
         char *active_state;
+        bool has_job;
+
+        /* The exit data of the unit */
         uint64_t inactive_exit_usec;
         uint64_t inactive_enter_usec;
         char *result;
@@ -975,7 +1014,7 @@ static void run_context_check_done(RunContext *c) {
         assert(c);
 
         if (c->match)
-                done = STRPTR_IN_SET(c->active_state, "inactive", "failed");
+                done = STRPTR_IN_SET(c->active_state, "inactive", "failed") && !c->has_job;
         else
                 done = true;
 
@@ -986,20 +1025,35 @@ static void run_context_check_done(RunContext *c) {
                 sd_event_exit(c->event, EXIT_SUCCESS);
 }
 
+static int map_job(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
+        bool *b = userdata;
+        const char *job;
+        uint32_t id;
+        int r;
+
+        r = sd_bus_message_read(m, "(uo)", &id, &job);
+        if (r < 0)
+                return r;
+
+        *b = id != 0 || !streq(job, "/");
+        return 0;
+}
+
 static int run_context_update(RunContext *c, const char *path) {
 
         static const struct bus_properties_map map[] = {
-                { "ActiveState",                      "s", NULL, offsetof(RunContext, active_state)        },
-                { "InactiveExitTimestampMonotonic",   "t", NULL, offsetof(RunContext, inactive_exit_usec)  },
-                { "InactiveEnterTimestampMonotonic",  "t", NULL, offsetof(RunContext, inactive_enter_usec) },
-                { "Result",                           "s", NULL, offsetof(RunContext, result)              },
-                { "ExecMainCode",                     "i", NULL, offsetof(RunContext, exit_code)           },
-                { "ExecMainStatus",                   "i", NULL, offsetof(RunContext, exit_status)         },
-                { "CPUUsageNSec",                     "t", NULL, offsetof(RunContext, cpu_usage_nsec)      },
-                { "IPIngressBytes",                   "t", NULL, offsetof(RunContext, ip_ingress_bytes)    },
-                { "IPEgressBytes",                    "t", NULL, offsetof(RunContext, ip_egress_bytes)     },
-                { "IOReadBytes",                      "t", NULL, offsetof(RunContext, io_read_bytes)       },
-                { "IOWriteBytes",                     "t", NULL, offsetof(RunContext, io_write_bytes)      },
+                { "ActiveState",                     "s",    NULL,    offsetof(RunContext, active_state)        },
+                { "InactiveExitTimestampMonotonic",  "t",    NULL,    offsetof(RunContext, inactive_exit_usec)  },
+                { "InactiveEnterTimestampMonotonic", "t",    NULL,    offsetof(RunContext, inactive_enter_usec) },
+                { "Result",                          "s",    NULL,    offsetof(RunContext, result)              },
+                { "ExecMainCode",                    "i",    NULL,    offsetof(RunContext, exit_code)           },
+                { "ExecMainStatus",                  "i",    NULL,    offsetof(RunContext, exit_status)         },
+                { "CPUUsageNSec",                    "t",    NULL,    offsetof(RunContext, cpu_usage_nsec)      },
+                { "IPIngressBytes",                  "t",    NULL,    offsetof(RunContext, ip_ingress_bytes)    },
+                { "IPEgressBytes",                   "t",    NULL,    offsetof(RunContext, ip_egress_bytes)     },
+                { "IOReadBytes",                     "t",    NULL,    offsetof(RunContext, io_read_bytes)       },
+                { "IOWriteBytes",                    "t",    NULL,    offsetof(RunContext, io_write_bytes)      },
+                { "Job",                             "(uo)", map_job, offsetof(RunContext, has_job)             },
                 {}
         };
 
@@ -1131,13 +1185,7 @@ static int start_transient_service(
                         return r;
         }
 
-        r = sd_bus_message_new_method_call(
-                        bus,
-                        &m,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "StartTransientUnit");
+        r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "StartTransientUnit");
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -1385,13 +1433,7 @@ static int start_transient_scope(sd_bus *bus) {
                         return r;
         }
 
-        r = sd_bus_message_new_method_call(
-                        bus,
-                        &m,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "StartTransientUnit");
+        r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "StartTransientUnit");
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -1575,13 +1617,7 @@ static int start_transient_trigger(
                         return log_error_errno(r, "Failed to change unit suffix: %m");
         }
 
-        r = sd_bus_message_new_method_call(
-                        bus,
-                        &m,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "StartTransientUnit");
+        r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "StartTransientUnit");
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -1717,7 +1753,7 @@ static int run(int argc, char* argv[]) {
         else
                 r = bus_connect_transport_systemd(arg_transport, arg_host, arg_user, &bus);
         if (r < 0)
-                return log_error_errno(r, "Failed to create bus connection: %m");
+                return bus_log_connect_error(r);
 
         if (arg_scope)
                 r = start_transient_scope(bus);
index 1ccb4f8295f06a013060881c5defc290f9cd0de6..dd2b1efb112901fd80a99ff11156c82bb6ebf9c4 100644 (file)
@@ -378,10 +378,13 @@ int acls_for_file(const char *path, acl_type_t type, acl_t new, acl_t *acl) {
 
 int add_acls_for_user(int fd, uid_t uid) {
         _cleanup_(acl_freep) acl_t acl = NULL;
-        acl_entry_t entry;
         acl_permset_t permset;
+        acl_entry_t entry;
         int r;
 
+        assert(fd >= 0);
+        assert(uid_is_valid(uid));
+
         acl = acl_get_fd(fd);
         if (!acl)
                 return -errno;
@@ -394,8 +397,8 @@ int add_acls_for_user(int fd, uid_t uid) {
                         return -errno;
         }
 
-        /* We do not recalculate the mask unconditionally here,
-         * so that the fchmod() mask above stays intact. */
+        /* We do not recalculate the mask unconditionally here, so that the fchmod() mask above stays
+         * intact. */
         if (acl_get_permset(entry, &permset) < 0 ||
             acl_add_perm(permset, ACL_READ) < 0)
                 return -errno;
@@ -404,5 +407,8 @@ int add_acls_for_user(int fd, uid_t uid) {
         if (r < 0)
                 return r;
 
-        return acl_set_fd(fd, acl);
+        if (acl_set_fd(fd, acl) < 0)
+                return -errno;
+
+        return 0;
 }
index d565ebd43e655108b03930c4ac6b4a31a4cd993c..38c464c912a53e4f2c76ed7b37ce169d4f95930b 100644 (file)
@@ -23,7 +23,7 @@ struct acpi_table_header {
         uint32_t oem_revision;
         char asl_compiler_id[4];
         uint32_t asl_compiler_revision;
-};
+} _packed_;
 
 enum {
         ACPI_FPDT_TYPE_BOOT =   0,
@@ -36,12 +36,12 @@ struct acpi_fpdt_header {
         uint8_t revision;
         uint8_t reserved[4];
         uint64_t ptr;
-};
+} _packed_;
 
 struct acpi_fpdt_boot_header {
         char signature[4];
         uint32_t length;
-};
+} _packed_;
 
 enum {
         ACPI_FPDT_S3PERF_RESUME_REC =   0,
@@ -59,7 +59,7 @@ struct acpi_fpdt_boot {
         uint64_t startup_start;
         uint64_t exit_services_entry;
         uint64_t exit_services_exit;
-};
+} _packed;
 
 int acpi_get_boot_usec(usec_t *loader_start, usec_t *loader_exit) {
         _cleanup_free_ char *buf = NULL;
index 1175f2336c60bdac795d56261347625a4efbd281..3d0b9391069507049c9fcec873a5a19eafaf2e92 100644 (file)
@@ -27,6 +27,7 @@
 #include "format-util.h"
 #include "fs-util.h"
 #include "io-util.h"
+#include "locale-util.h"
 #include "log.h"
 #include "macro.h"
 #include "memory-util.h"
@@ -134,12 +135,12 @@ static int add_to_keyring(const char *keyname, AskPasswordFlags flags, char **pa
         if (keyctl(KEYCTL_SET_TIMEOUT,
                    (unsigned long) serial,
                    (unsigned long) DIV_ROUND_UP(KEYRING_TIMEOUT_USEC, USEC_PER_SEC), 0, 0) < 0)
-                log_debug_errno(errno, "Failed to adjust timeout: %m");
+                log_debug_errno(errno, "Failed to adjust kernel keyring key timeout: %m");
 
         /* Tell everyone to check the keyring */
         (void) touch("/run/systemd/ask-password");
 
-        log_debug("Added key to keyring as %" PRIi32 ".", serial);
+        log_debug("Added key to kernel keyring as %" PRIi32 ".", serial);
 
         return 1;
 }
@@ -151,7 +152,7 @@ static int add_to_keyring_and_log(const char *keyname, AskPasswordFlags flags, c
 
         r = add_to_keyring(keyname, flags, passwords);
         if (r < 0)
-                return log_debug_errno(r, "Failed to add password to keyring: %m");
+                return log_debug_errno(r, "Failed to add password to kernel keyring: %m");
 
         return 0;
 }
@@ -304,6 +305,12 @@ int ask_password_plymouth(
                         goto finish;
                 }
 
+                if (pollfd[POLL_SOCKET].revents & POLLNVAL ||
+                    (notify >= 0 && pollfd[POLL_INOTIFY].revents & POLLNVAL)) {
+                        r = -EBADF;
+                        goto finish;
+                }
+
                 if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0)
                         (void) flush_fd(notify);
 
@@ -430,6 +437,9 @@ int ask_password_tty(
         if (!message)
                 message = "Password:";
 
+        if (emoji_enabled())
+                message = strjoina(special_glyph(SPECIAL_GLYPH_LOCK_AND_KEY), " ", message);
+
         if (flag_file || ((flags & ASK_PASSWORD_ACCEPT_CACHED) && keyname)) {
                 notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
                 if (notify < 0)
@@ -537,6 +547,12 @@ int ask_password_tty(
                         goto finish;
                 }
 
+                if ((pollfd[POLL_TTY].revents & POLLNVAL) ||
+                    (notify >= 0 && (pollfd[POLL_INOTIFY].revents & POLLNVAL))) {
+                        r = -EBADF;
+                        goto finish;
+                }
+
                 if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0 && keyname) {
                         (void) flush_fd(notify);
 
@@ -791,14 +807,12 @@ int ask_password_agent(
 
         (void) fchmod(fd, 0644);
 
-        f = fdopen(fd, "w");
+        f = take_fdopen(&fd, "w");
         if (!f) {
                 r = -errno;
                 goto finish;
         }
 
-        fd = -1;
-
         signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
         if (signal_fd < 0) {
                 r = -errno;
@@ -857,14 +871,10 @@ int ask_password_agent(
         pollfd[FD_INOTIFY].events = POLLIN;
 
         for (;;) {
+                CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control;
                 char passphrase[LINE_MAX+1];
-                struct msghdr msghdr;
                 struct iovec iovec;
                 struct ucred *ucred;
-                union {
-                        struct cmsghdr cmsghdr;
-                        uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
-                } control;
                 ssize_t n;
                 int k;
                 usec_t t;
@@ -890,6 +900,13 @@ int ask_password_agent(
                         goto finish;
                 }
 
+                if (pollfd[FD_SOCKET].revents & POLLNVAL ||
+                    pollfd[FD_SIGNAL].revents & POLLNVAL ||
+                    (notify >= 0 && pollfd[FD_INOTIFY].revents & POLLNVAL)) {
+                        r = -EBADF;
+                        goto finish;
+                }
+
                 if (pollfd[FD_SIGNAL].revents & POLLIN) {
                         r = -EINTR;
                         goto finish;
@@ -916,12 +933,12 @@ int ask_password_agent(
 
                 iovec = IOVEC_MAKE(passphrase, sizeof(passphrase));
 
-                zero(control);
-                zero(msghdr);
-                msghdr.msg_iov = &iovec;
-                msghdr.msg_iovlen = 1;
-                msghdr.msg_control = &control;
-                msghdr.msg_controllen = sizeof(control);
+                struct msghdr msghdr = {
+                        .msg_iov = &iovec,
+                        .msg_iovlen = 1,
+                        .msg_control = &control,
+                        .msg_controllen = sizeof(control),
+                };
 
                 n = recvmsg_safe(socket_fd, &msghdr, 0);
                 if (IN_SET(n, -EAGAIN, -EINTR))
@@ -938,15 +955,12 @@ int ask_password_agent(
                         continue;
                 }
 
-                if (msghdr.msg_controllen < CMSG_LEN(sizeof(struct ucred)) ||
-                    control.cmsghdr.cmsg_level != SOL_SOCKET ||
-                    control.cmsghdr.cmsg_type != SCM_CREDENTIALS ||
-                    control.cmsghdr.cmsg_len != CMSG_LEN(sizeof(struct ucred))) {
+                ucred = CMSG_FIND_DATA(&msghdr, SOL_SOCKET, SCM_CREDENTIALS, struct ucred);
+                if (!ucred) {
                         log_debug("Received message without credentials. Ignoring.");
                         continue;
                 }
 
-                ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr);
                 if (ucred->uid != 0) {
                         log_debug("Got request from unprivileged user. Ignoring.");
                         continue;
index bb5869dad405166f0adc5af8f7069d0a69e8b024..80b597b5cadda9b94bea9daa463d7d04244d25ca 100644 (file)
@@ -218,10 +218,14 @@ static bool barrier_read(Barrier *b, int64_t comp) {
                 uint64_t buf;
                 int r;
 
-                r = poll(pfd, 2, -1);
-                if (r < 0 && IN_SET(errno, EAGAIN, EINTR))
-                        continue;
-                else if (r < 0)
+                r = poll(pfd, ELEMENTSOF(pfd), -1);
+                if (r < 0) {
+                        if (IN_SET(errno, EAGAIN, EINTR))
+                                continue;
+                        goto error;
+                }
+                if (pfd[0].revents & POLLNVAL ||
+                    pfd[1].revents & POLLNVAL)
                         goto error;
 
                 if (pfd[1].revents) {
diff --git a/src/shared/bond-util.c b/src/shared/bond-util.c
new file mode 100644 (file)
index 0000000..2296ecd
--- /dev/null
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "bond-util.h"
+#include "string-table.h"
+
+static const char* const bond_mode_table[_NETDEV_BOND_MODE_MAX] = {
+        [NETDEV_BOND_MODE_BALANCE_RR] = "balance-rr",
+        [NETDEV_BOND_MODE_ACTIVE_BACKUP] = "active-backup",
+        [NETDEV_BOND_MODE_BALANCE_XOR] = "balance-xor",
+        [NETDEV_BOND_MODE_BROADCAST] = "broadcast",
+        [NETDEV_BOND_MODE_802_3AD] = "802.3ad",
+        [NETDEV_BOND_MODE_BALANCE_TLB] = "balance-tlb",
+        [NETDEV_BOND_MODE_BALANCE_ALB] = "balance-alb",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(bond_mode, BondMode);
+
+static const char* const bond_xmit_hash_policy_table[_NETDEV_BOND_XMIT_HASH_POLICY_MAX] = {
+        [NETDEV_BOND_XMIT_HASH_POLICY_LAYER2] = "layer2",
+        [NETDEV_BOND_XMIT_HASH_POLICY_LAYER34] = "layer3+4",
+        [NETDEV_BOND_XMIT_HASH_POLICY_LAYER23] = "layer2+3",
+        [NETDEV_BOND_XMIT_HASH_POLICY_ENCAP23] = "encap2+3",
+        [NETDEV_BOND_XMIT_HASH_POLICY_ENCAP34] = "encap3+4",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(bond_xmit_hash_policy, BondXmitHashPolicy);
+
+static const char* const bond_lacp_rate_table[_NETDEV_BOND_LACP_RATE_MAX] = {
+        [NETDEV_BOND_LACP_RATE_SLOW] = "slow",
+        [NETDEV_BOND_LACP_RATE_FAST] = "fast",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(bond_lacp_rate, BondLacpRate);
+
+static const char* const bond_ad_select_table[_NETDEV_BOND_AD_SELECT_MAX] = {
+        [NETDEV_BOND_AD_SELECT_STABLE] = "stable",
+        [NETDEV_BOND_AD_SELECT_BANDWIDTH] = "bandwidth",
+        [NETDEV_BOND_AD_SELECT_COUNT] = "count",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(bond_ad_select, BondAdSelect);
+
+static const char* const bond_fail_over_mac_table[_NETDEV_BOND_FAIL_OVER_MAC_MAX] = {
+        [NETDEV_BOND_FAIL_OVER_MAC_NONE] = "none",
+        [NETDEV_BOND_FAIL_OVER_MAC_ACTIVE] = "active",
+        [NETDEV_BOND_FAIL_OVER_MAC_FOLLOW] = "follow",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(bond_fail_over_mac, BondFailOverMac);
+
+static const char *const bond_arp_validate_table[_NETDEV_BOND_ARP_VALIDATE_MAX] = {
+        [NETDEV_BOND_ARP_VALIDATE_NONE] = "none",
+        [NETDEV_BOND_ARP_VALIDATE_ACTIVE]= "active",
+        [NETDEV_BOND_ARP_VALIDATE_BACKUP]= "backup",
+        [NETDEV_BOND_ARP_VALIDATE_ALL]= "all",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(bond_arp_validate, BondArpValidate);
+
+static const char *const bond_arp_all_targets_table[_NETDEV_BOND_ARP_ALL_TARGETS_MAX] = {
+        [NETDEV_BOND_ARP_ALL_TARGETS_ANY] = "any",
+        [NETDEV_BOND_ARP_ALL_TARGETS_ALL] = "all",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(bond_arp_all_targets, BondArpAllTargets);
+
+static const char *const bond_primary_reselect_table[_NETDEV_BOND_PRIMARY_RESELECT_MAX] = {
+        [NETDEV_BOND_PRIMARY_RESELECT_ALWAYS] = "always",
+        [NETDEV_BOND_PRIMARY_RESELECT_BETTER]= "better",
+        [NETDEV_BOND_PRIMARY_RESELECT_FAILURE]= "failure",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(bond_primary_reselect, BondPrimaryReselect);
diff --git a/src/shared/bond-util.h b/src/shared/bond-util.h
new file mode 100644 (file)
index 0000000..66f86e7
--- /dev/null
@@ -0,0 +1,106 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <netinet/in.h>
+#include <linux/if_bonding.h>
+
+#include "macro.h"
+
+/*
+ * Maximum number of targets supported by the kernel for a single
+ * bond netdev.
+ */
+#define NETDEV_BOND_ARP_TARGETS_MAX 16
+
+typedef enum BondMode {
+        NETDEV_BOND_MODE_BALANCE_RR    = BOND_MODE_ROUNDROBIN,
+        NETDEV_BOND_MODE_ACTIVE_BACKUP = BOND_MODE_ACTIVEBACKUP,
+        NETDEV_BOND_MODE_BALANCE_XOR   = BOND_MODE_XOR,
+        NETDEV_BOND_MODE_BROADCAST     = BOND_MODE_BROADCAST,
+        NETDEV_BOND_MODE_802_3AD       = BOND_MODE_8023AD,
+        NETDEV_BOND_MODE_BALANCE_TLB   = BOND_MODE_TLB,
+        NETDEV_BOND_MODE_BALANCE_ALB   = BOND_MODE_ALB,
+        _NETDEV_BOND_MODE_MAX,
+        _NETDEV_BOND_MODE_INVALID      = -1
+} BondMode;
+
+typedef enum BondXmitHashPolicy {
+        NETDEV_BOND_XMIT_HASH_POLICY_LAYER2   = BOND_XMIT_POLICY_LAYER2,
+        NETDEV_BOND_XMIT_HASH_POLICY_LAYER34  = BOND_XMIT_POLICY_LAYER34,
+        NETDEV_BOND_XMIT_HASH_POLICY_LAYER23  = BOND_XMIT_POLICY_LAYER23,
+        NETDEV_BOND_XMIT_HASH_POLICY_ENCAP23  = BOND_XMIT_POLICY_ENCAP23,
+        NETDEV_BOND_XMIT_HASH_POLICY_ENCAP34  = BOND_XMIT_POLICY_ENCAP34,
+        _NETDEV_BOND_XMIT_HASH_POLICY_MAX,
+        _NETDEV_BOND_XMIT_HASH_POLICY_INVALID = -1
+} BondXmitHashPolicy;
+
+typedef enum BondLacpRate {
+        NETDEV_BOND_LACP_RATE_SLOW,
+        NETDEV_BOND_LACP_RATE_FAST,
+        _NETDEV_BOND_LACP_RATE_MAX,
+        _NETDEV_BOND_LACP_RATE_INVALID = -1,
+} BondLacpRate;
+
+typedef enum BondAdSelect {
+        NETDEV_BOND_AD_SELECT_STABLE,
+        NETDEV_BOND_AD_SELECT_BANDWIDTH,
+        NETDEV_BOND_AD_SELECT_COUNT,
+        _NETDEV_BOND_AD_SELECT_MAX,
+        _NETDEV_BOND_AD_SELECT_INVALID = -1,
+} BondAdSelect;
+
+typedef enum BondFailOverMac {
+        NETDEV_BOND_FAIL_OVER_MAC_NONE,
+        NETDEV_BOND_FAIL_OVER_MAC_ACTIVE,
+        NETDEV_BOND_FAIL_OVER_MAC_FOLLOW,
+        _NETDEV_BOND_FAIL_OVER_MAC_MAX,
+        _NETDEV_BOND_FAIL_OVER_MAC_INVALID = -1,
+} BondFailOverMac;
+
+typedef enum BondArpValidate {
+        NETDEV_BOND_ARP_VALIDATE_NONE,
+        NETDEV_BOND_ARP_VALIDATE_ACTIVE,
+        NETDEV_BOND_ARP_VALIDATE_BACKUP,
+        NETDEV_BOND_ARP_VALIDATE_ALL,
+        _NETDEV_BOND_ARP_VALIDATE_MAX,
+        _NETDEV_BOND_ARP_VALIDATE_INVALID = -1,
+} BondArpValidate;
+
+typedef enum BondArpAllTargets {
+        NETDEV_BOND_ARP_ALL_TARGETS_ANY,
+        NETDEV_BOND_ARP_ALL_TARGETS_ALL,
+        _NETDEV_BOND_ARP_ALL_TARGETS_MAX,
+        _NETDEV_BOND_ARP_ALL_TARGETS_INVALID = -1,
+} BondArpAllTargets;
+
+typedef enum BondPrimaryReselect {
+        NETDEV_BOND_PRIMARY_RESELECT_ALWAYS,
+        NETDEV_BOND_PRIMARY_RESELECT_BETTER,
+        NETDEV_BOND_PRIMARY_RESELECT_FAILURE,
+        _NETDEV_BOND_PRIMARY_RESELECT_MAX,
+        _NETDEV_BOND_PRIMARY_RESELECT_INVALID = -1,
+} BondPrimaryReselect;
+
+const char *bond_mode_to_string(BondMode d) _const_;
+BondMode bond_mode_from_string(const char *d) _pure_;
+
+const char *bond_xmit_hash_policy_to_string(BondXmitHashPolicy d) _const_;
+BondXmitHashPolicy bond_xmit_hash_policy_from_string(const char *d) _pure_;
+
+const char *bond_lacp_rate_to_string(BondLacpRate d) _const_;
+BondLacpRate bond_lacp_rate_from_string(const char *d) _pure_;
+
+const char *bond_fail_over_mac_to_string(BondFailOverMac d) _const_;
+BondFailOverMac bond_fail_over_mac_from_string(const char *d) _pure_;
+
+const char *bond_ad_select_to_string(BondAdSelect d) _const_;
+BondAdSelect bond_ad_select_from_string(const char *d) _pure_;
+
+const char *bond_arp_validate_to_string(BondArpValidate d) _const_;
+BondArpValidate bond_arp_validate_from_string(const char *d) _pure_;
+
+const char *bond_arp_all_targets_to_string(BondArpAllTargets d) _const_;
+BondArpAllTargets bond_arp_all_targets_from_string(const char *d) _pure_;
+
+const char *bond_primary_reselect_to_string(BondPrimaryReselect d) _const_;
+BondPrimaryReselect bond_primary_reselect_from_string(const char *d) _pure_;
index 13d7b2f16075ae37cfea2d8e82d39d3e04d473eb..11bba2c7edfadd737d7be627fcc26432f10f4f9b 100644 (file)
@@ -735,9 +735,12 @@ int boot_entries_load_config_auto(
         return boot_entries_load_config(esp_where, xbootldr_where, config);
 }
 
-#if ENABLE_EFI
-int boot_entries_augment_from_loader(BootConfig *config, bool only_auto) {
-        static const char * const title_table[] = {
+int boot_entries_augment_from_loader(
+                BootConfig *config,
+                char **found_by_loader,
+                bool only_auto) {
+
+        static const char *const title_table[] = {
                 /* Pretty names for a few well-known automatically discovered entries. */
                 "auto-osx",                      "macOS",
                 "auto-windows",                  "Windows Boot Manager",
@@ -746,22 +749,14 @@ int boot_entries_augment_from_loader(BootConfig *config, bool only_auto) {
                 "auto-reboot-to-firmware-setup", "Reboot Into Firmware Interface",
         };
 
-        _cleanup_strv_free_ char **found_by_loader = NULL;
         size_t n_allocated;
         char **i;
-        int r;
 
         assert(config);
 
         /* Let's add the entries discovered by the boot loader to the end of our list, unless they are
          * already included there. */
 
-        r = efi_loader_get_entries(&found_by_loader);
-        if (IN_SET(r, -ENOENT, -EOPNOTSUPP))
-                return log_debug_errno(r, "Boot loader reported no entries.");
-        if (r < 0)
-                return log_error_errno(r, "Failed to determine entries reported by boot loader: %m");
-
         n_allocated = config->n_entries;
 
         STRV_FOREACH(i, found_by_loader) {
@@ -803,7 +798,6 @@ int boot_entries_augment_from_loader(BootConfig *config, bool only_auto) {
 
         return 0;
 }
-#endif
 
 /********************************************************************************/
 
index b40680b643b3b3ff39419ec763e9cbc016f09f44..1075a41d54151d70a237a67eca524020fd796ed3 100644 (file)
@@ -76,13 +76,7 @@ static inline BootEntry* boot_config_default_entry(BootConfig *config) {
 void boot_config_free(BootConfig *config);
 int boot_entries_load_config(const char *esp_path, const char *xbootldr_path, BootConfig *config);
 int boot_entries_load_config_auto(const char *override_esp_path, const char *override_xbootldr_path, BootConfig *config);
-#if ENABLE_EFI
-int boot_entries_augment_from_loader(BootConfig *config, bool only_auto);
-#else
-static inline int boot_entries_augment_from_loader(BootConfig *config, bool only_auto) {
-        return -EOPNOTSUPP;
-}
-#endif
+int boot_entries_augment_from_loader(BootConfig *config, char **list, bool only_auto);
 
 static inline const char* boot_entry_title(const BootEntry *entry) {
         return entry->show_title ?: entry->title ?: entry->id;
diff --git a/src/shared/bridge-util.c b/src/shared/bridge-util.c
new file mode 100644 (file)
index 0000000..83a94ef
--- /dev/null
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "bridge-util.h"
+#include "string-table.h"
+
+static const char* const bridge_state_table[_NETDEV_BRIDGE_STATE_MAX] = {
+        [NETDEV_BRIDGE_STATE_DISABLED]   = "disabled",
+        [NETDEV_BRIDGE_STATE_LISTENING]  = "listening",
+        [NETDEV_BRIDGE_STATE_LEARNING]   = "learning",
+        [NETDEV_BRIDGE_STATE_FORWARDING] = "forwarding",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(bridge_state, BridgeState);
diff --git a/src/shared/bridge-util.h b/src/shared/bridge-util.h
new file mode 100644 (file)
index 0000000..5b1c3e9
--- /dev/null
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <netinet/in.h>
+#include <linux/if_bridge.h>
+
+#include "conf-parser.h"
+
+typedef enum BridgeState {
+        NETDEV_BRIDGE_STATE_DISABLED   = BR_STATE_DISABLED,
+        NETDEV_BRIDGE_STATE_LISTENING  = BR_STATE_LISTENING,
+        NETDEV_BRIDGE_STATE_LEARNING   = BR_STATE_LEARNING,
+        NETDEV_BRIDGE_STATE_FORWARDING = BR_STATE_FORWARDING,
+        NETDEV_BRIDGE_STATE_BLOCKING   = BR_STATE_BLOCKING,
+        _NETDEV_BRIDGE_STATE_MAX,
+        _NETDEV_BRIDGE_STATE_INVALID      = -1,
+} BridgeState;
+
+const char *bridge_state_to_string(BridgeState d) _const_;
+BridgeState bridge_state_from_string(const char *d) _pure_;
diff --git a/src/shared/bus-get-properties.c b/src/shared/bus-get-properties.c
new file mode 100644 (file)
index 0000000..8ad4694
--- /dev/null
@@ -0,0 +1,149 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "bus-get-properties.h"
+#include "rlimit-util.h"
+#include "string-util.h"
+
+int bus_property_get_bool(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        int b = *(bool*) userdata;
+
+        return sd_bus_message_append_basic(reply, 'b', &b);
+}
+
+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,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        sd_id128_t *id = userdata;
+
+        if (sd_id128_is_null(*id)) /* Add an empty array if the ID is zero */
+                return sd_bus_message_append(reply, "ay", 0);
+        else
+                return sd_bus_message_append_array(reply, 'y', id->bytes, 16);
+}
+
+#if __SIZEOF_SIZE_T__ != 8
+int bus_property_get_size(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        uint64_t sz = *(size_t*) userdata;
+
+        return sd_bus_message_append_basic(reply, 't', &sz);
+}
+#endif
+
+#if __SIZEOF_LONG__ != 8
+int bus_property_get_long(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        int64_t l = *(long*) userdata;
+
+        return sd_bus_message_append_basic(reply, 'x', &l);
+}
+
+int bus_property_get_ulong(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        uint64_t ul = *(unsigned long*) userdata;
+
+        return sd_bus_message_append_basic(reply, 't', &ul);
+}
+#endif
+
+int bus_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) {
+
+        const char *is_soft;
+        struct rlimit *rl;
+        uint64_t u;
+        rlim_t x;
+
+        assert(bus);
+        assert(reply);
+        assert(userdata);
+
+        is_soft = endswith(property, "Soft");
+
+        rl = *(struct rlimit**) userdata;
+        if (rl)
+                x = is_soft ? rl->rlim_cur : rl->rlim_max;
+        else {
+                struct rlimit buf = {};
+                const char *s, *p;
+                int z;
+
+                /* Chop off "Soft" suffix */
+                s = is_soft ? strndupa(property, is_soft - property) : property;
+
+                /* Skip over any prefix, such as "Default" */
+                assert_se(p = strstr(s, "Limit"));
+
+                z = rlimit_from_string(p + 5);
+                assert(z >= 0);
+
+                (void) getrlimit(z, &buf);
+                x = is_soft ? buf.rlim_cur : buf.rlim_max;
+        }
+
+        /* rlim_t might have different sizes, let's map RLIMIT_INFINITY to (uint64_t) -1, so that it is the same on all
+         * archs */
+        u = x == RLIM_INFINITY ? (uint64_t) -1 : (uint64_t) x;
+
+        return sd_bus_message_append(reply, "t", u);
+}
diff --git a/src/shared/bus-get-properties.h b/src/shared/bus-get-properties.h
new file mode 100644 (file)
index 0000000..81af743
--- /dev/null
@@ -0,0 +1,101 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "sd-bus.h"
+
+#include "macro.h"
+
+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)
+#define bus_property_set_usec ((sd_bus_property_set_t) NULL)
+
+assert_cc(sizeof(int) == sizeof(int32_t));
+#define bus_property_get_int ((sd_bus_property_get_t) NULL)
+
+assert_cc(sizeof(unsigned) == sizeof(uint32_t));
+#define bus_property_get_unsigned ((sd_bus_property_get_t) NULL)
+
+/* On 64bit machines we can use the default serializer for size_t and
+ * friends, otherwise we need to cast this manually */
+#if __SIZEOF_SIZE_T__ == 8
+#define bus_property_get_size ((sd_bus_property_get_t) NULL)
+#else
+int bus_property_get_size(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
+#endif
+
+#if __SIZEOF_LONG__ == 8
+#define bus_property_get_long ((sd_bus_property_get_t) NULL)
+#define bus_property_get_ulong ((sd_bus_property_get_t) NULL)
+#else
+int bus_property_get_long(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
+int bus_property_get_ulong(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
+#endif
+
+/* uid_t and friends on Linux 32 bit. This means we can just use the
+ * default serializer for 32bit unsigned, for serializing it, and map
+ * it to NULL here */
+assert_cc(sizeof(uid_t) == sizeof(uint32_t));
+#define bus_property_get_uid ((sd_bus_property_get_t) NULL)
+
+assert_cc(sizeof(gid_t) == sizeof(uint32_t));
+#define bus_property_get_gid ((sd_bus_property_get_t) NULL)
+
+assert_cc(sizeof(pid_t) == sizeof(uint32_t));
+#define bus_property_get_pid ((sd_bus_property_get_t) NULL)
+
+assert_cc(sizeof(mode_t) == sizeof(uint32_t));
+#define bus_property_get_mode ((sd_bus_property_get_t) NULL)
+
+int bus_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);
+
+#define BUS_DEFINE_PROPERTY_GET_GLOBAL(function, bus_type, val)         \
+        int function(sd_bus *bus,                                       \
+                     const char *path,                                  \
+                     const char *interface,                             \
+                     const char *property,                              \
+                     sd_bus_message *reply,                             \
+                     void *userdata,                                    \
+                     sd_bus_error *error) {                             \
+                                                                        \
+                assert(bus);                                            \
+                assert(reply);                                          \
+                                                                        \
+                return sd_bus_message_append(reply, bus_type, val);     \
+        }
+
+#define BUS_DEFINE_PROPERTY_GET2(function, bus_type, data_type, get1, get2) \
+        int function(sd_bus *bus,                                       \
+                     const char *path,                                  \
+                     const char *interface,                             \
+                     const char *property,                              \
+                     sd_bus_message *reply,                             \
+                     void *userdata,                                    \
+                     sd_bus_error *error) {                             \
+                                                                        \
+                data_type *data = userdata;                             \
+                                                                        \
+                assert(bus);                                            \
+                assert(reply);                                          \
+                assert(data);                                           \
+                                                                        \
+                return sd_bus_message_append(reply, bus_type,           \
+                                             get2(get1(data)));         \
+        }
+
+#define ident(x) (x)
+#define BUS_DEFINE_PROPERTY_GET(function, bus_type, data_type, get1) \
+        BUS_DEFINE_PROPERTY_GET2(function, bus_type, data_type, get1, ident)
+
+#define ref(x) (*(x))
+#define BUS_DEFINE_PROPERTY_GET_REF(function, bus_type, data_type, get) \
+        BUS_DEFINE_PROPERTY_GET2(function, bus_type, data_type, ref, get)
+
+#define BUS_DEFINE_PROPERTY_GET_ENUM(function, name, type)              \
+        BUS_DEFINE_PROPERTY_GET_REF(function, "s", type, name##_to_string)
+
+#define BUS_PROPERTY_DUAL_TIMESTAMP(name, offset, flags) \
+        SD_BUS_PROPERTY(name, "t", bus_property_get_usec, (offset) + offsetof(struct dual_timestamp, realtime), (flags)), \
+        SD_BUS_PROPERTY(name "Monotonic", "t", bus_property_get_usec, (offset) + offsetof(struct dual_timestamp, monotonic), (flags))
diff --git a/src/shared/bus-locator.c b/src/shared/bus-locator.c
new file mode 100644 (file)
index 0000000..2a5aa74
--- /dev/null
@@ -0,0 +1,213 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "bus-locator.h"
+#include "macro.h"
+
+const BusLocator* const bus_home_mgr = &(BusLocator){
+        .destination = "org.freedesktop.home1",
+        .path = "/org/freedesktop/home1",
+        .interface = "org.freedesktop.home1.Manager",
+};
+
+const BusLocator* const bus_import_mgr = &(BusLocator){
+        .destination ="org.freedesktop.import1",
+        .path = "/org/freedesktop/import1",
+        .interface = "org.freedesktop.import1.Manager"
+};
+
+const BusLocator* const bus_locale = &(BusLocator){
+        .destination = "org.freedesktop.locale1",
+        .path = "/org/freedesktop/locale1",
+        .interface = "org.freedesktop.locale1"
+};
+
+const BusLocator* const bus_login_mgr = &(BusLocator){
+        .destination = "org.freedesktop.login1",
+        .path = "/org/freedesktop/login1",
+        .interface = "org.freedesktop.login1.Manager"
+};
+
+const BusLocator* const bus_machine_mgr = &(BusLocator){
+        .destination ="org.freedesktop.machine1",
+        .path = "/org/freedesktop/machine1",
+        .interface = "org.freedesktop.machine1.Manager"
+};
+
+const BusLocator* const bus_network_mgr = &(BusLocator){
+        .destination = "org.freedesktop.network1",
+        .path = "/org/freedesktop/network1",
+        .interface = "org.freedesktop.network1.Manager"
+};
+
+const BusLocator* const bus_portable_mgr = &(BusLocator){
+        .destination = "org.freedesktop.portable1",
+        .path = "/org/freedesktop/portable1",
+        .interface = "org.freedesktop.portable1.Manager"
+};
+
+const BusLocator* const bus_resolve_mgr = &(BusLocator){
+        .destination = "org.freedesktop.resolve1",
+        .path = "/org/freedesktop/resolve1",
+        .interface = "org.freedesktop.resolve1.Manager"
+};
+
+const BusLocator* const bus_systemd_mgr = &(BusLocator){
+        .destination = "org.freedesktop.systemd1",
+        .path = "/org/freedesktop/systemd1",
+        .interface = "org.freedesktop.systemd1.Manager"
+};
+
+const BusLocator* const bus_timedate = &(BusLocator){
+        .destination = "org.freedesktop.timedate1",
+        .path = "/org/freedesktop/timedate1",
+        .interface = "org.freedesktop.timedate1"
+};
+
+/* Shorthand flavors of the sd-bus convenience helpers with destination,path,interface strings encapsulated
+ * within a single struct. */
+int bus_call_method_async(
+                sd_bus *bus,
+                sd_bus_slot **slot,
+                const BusLocator *locator,
+                const char *member,
+                sd_bus_message_handler_t callback,
+                void *userdata,
+                const char *types, ...) {
+
+        va_list ap;
+        int r;
+
+        assert(locator);
+
+        va_start(ap, types);
+        r = sd_bus_call_method_asyncv(bus, slot, locator->destination, locator->path, locator->interface, member, callback, userdata, types, ap);
+        va_end(ap);
+
+        return r;
+}
+
+int bus_call_method(
+                sd_bus *bus,
+                const BusLocator *locator,
+                const char *member,
+                sd_bus_error *error,
+                sd_bus_message **reply,
+                const char *types, ...) {
+
+        va_list ap;
+        int r;
+
+        assert(locator);
+
+        va_start(ap, types);
+        r = sd_bus_call_methodv(bus, locator->destination, locator->path, locator->interface, member, error, reply, types, ap);
+        va_end(ap);
+
+        return r;
+}
+
+int bus_get_property(
+                sd_bus *bus,
+                const BusLocator *locator,
+                const char *member,
+                sd_bus_error *error,
+                sd_bus_message **reply,
+                const char *type) {
+
+        assert(locator);
+
+        return sd_bus_get_property(bus, locator->destination, locator->path, locator->interface, member, error, reply, type);
+}
+
+int bus_get_property_trivial(
+                sd_bus *bus,
+                const BusLocator *locator,
+                const char *member,
+                sd_bus_error *error,
+                char type, void *ptr) {
+
+        assert(locator);
+
+        return sd_bus_get_property_trivial(bus, locator->destination, locator->path, locator->interface, member, error, type, ptr);
+}
+
+int bus_get_property_string(
+                sd_bus *bus,
+                const BusLocator *locator,
+                const char *member,
+                sd_bus_error *error,
+                char **ret) {
+
+        assert(locator);
+
+        return sd_bus_get_property_string(bus, locator->destination, locator->path, locator->interface, member, error, ret);
+}
+
+int bus_get_property_strv(
+                sd_bus *bus,
+                const BusLocator *locator,
+                const char *member,
+                sd_bus_error *error,
+                char ***ret) {
+
+        assert(locator);
+
+        return sd_bus_get_property_strv(bus, locator->destination, locator->path, locator->interface, member, error, ret);
+}
+
+int bus_set_property(
+                sd_bus *bus,
+                const BusLocator *locator,
+                const char *member,
+                sd_bus_error *error,
+                const char *type, ...) {
+
+        va_list ap;
+        int r;
+
+        assert(locator);
+
+        va_start(ap, type);
+        r = sd_bus_set_propertyv(bus, locator->destination, locator->path, locator->interface, member, error, type, ap);
+        va_end(ap);
+
+        return r;
+}
+
+int bus_match_signal(
+                sd_bus *bus,
+                sd_bus_slot **ret,
+                const BusLocator *locator,
+                const char *member,
+                sd_bus_message_handler_t callback,
+                void *userdata) {
+
+        assert(locator);
+
+        return sd_bus_match_signal(bus, ret, locator->destination, locator->path, locator->interface, member, callback, userdata);
+}
+
+int bus_match_signal_async(
+                sd_bus *bus,
+                sd_bus_slot **ret,
+                const BusLocator *locator,
+                const char *member,
+                sd_bus_message_handler_t callback,
+                sd_bus_message_handler_t install_callback,
+                void *userdata) {
+
+        assert(locator);
+
+        return sd_bus_match_signal_async(bus, ret, locator->destination, locator->path, locator->interface, member, callback, install_callback, userdata);
+}
+
+int bus_message_new_method_call(
+                sd_bus *bus,
+                sd_bus_message **m,
+                const BusLocator *locator,
+                const char *member) {
+
+        assert(locator);
+
+        return sd_bus_message_new_method_call(bus, m, locator->destination, locator->path, locator->interface, member);
+}
diff --git a/src/shared/bus-locator.h b/src/shared/bus-locator.h
new file mode 100644 (file)
index 0000000..2b89236
--- /dev/null
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "sd-bus.h"
+
+typedef struct BusLocator {
+        const char *destination;
+        const char *path;
+        const char *interface;
+} BusLocator;
+
+extern const BusLocator* const bus_home_mgr;
+extern const BusLocator* const bus_import_mgr;
+extern const BusLocator* const bus_locale;
+extern const BusLocator* const bus_login_mgr;
+extern const BusLocator* const bus_machine_mgr;
+extern const BusLocator* const bus_network_mgr;
+extern const BusLocator* const bus_portable_mgr;
+extern const BusLocator* const bus_resolve_mgr;
+extern const BusLocator* const bus_systemd_mgr;
+extern const BusLocator* const bus_timedate;
+
+/* Shorthand flavors of the sd-bus convenience helpers with destination,path,interface strings encapsulated
+ * within a single struct. */
+int bus_call_method_async(sd_bus *bus, sd_bus_slot **slot, const BusLocator *locator, const char *member, sd_bus_message_handler_t callback, void *userdata, const char *types, ...);
+int bus_call_method(sd_bus *bus, const BusLocator *locator, const char *member, sd_bus_error *error, sd_bus_message **reply, const char *types, ...);
+int bus_get_property(sd_bus *bus, const BusLocator *locator, const char *member, sd_bus_error *error, sd_bus_message **reply, const char *type);
+int bus_get_property_trivial(sd_bus *bus, const BusLocator *locator, const char *member, sd_bus_error *error, char type, void *ptr);
+int bus_get_property_string(sd_bus *bus, const BusLocator *locator, const char *member, sd_bus_error *error, char **ret);
+int bus_get_property_strv(sd_bus *bus, const BusLocator *locator, const char *member, sd_bus_error *error, char ***ret);
+int bus_set_property(sd_bus *bus, const BusLocator *locator, const char *member, sd_bus_error *error, const char *type, ...);
+int bus_match_signal(sd_bus *bus, sd_bus_slot **ret, const BusLocator *locator, const char *member, sd_bus_message_handler_t callback, void *userdata);
+int bus_match_signal_async(sd_bus *bus, sd_bus_slot **ret, const BusLocator *locator, const char *member, sd_bus_message_handler_t callback, sd_bus_message_handler_t install_callback, void *userdata);
+int bus_message_new_method_call(sd_bus *bus, sd_bus_message **m, const BusLocator *locator, const char *member);
diff --git a/src/shared/bus-log-control-api.c b/src/shared/bus-log-control-api.c
new file mode 100644 (file)
index 0000000..7c487ad
--- /dev/null
@@ -0,0 +1,115 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "alloc-util.h"
+#include "bus-get-properties.h"
+#include "bus-log-control-api.h"
+#include "bus-util.h"
+#include "log.h"
+#include "sd-bus.h"
+#include "syslog-util.h"
+
+int bus_property_get_log_level(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        _cleanup_free_ char *t = NULL;
+        int r;
+
+        assert(bus);
+        assert(reply);
+
+        r = log_level_to_string_alloc(log_get_max_level(), &t);
+        if (r < 0)
+                return r;
+
+        return sd_bus_message_append(reply, "s", t);
+}
+
+int bus_property_set_log_level(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *value,
+                void *userdata,
+                sd_bus_error *error) {
+
+        const char *t;
+        int r;
+
+        assert(bus);
+        assert(value);
+
+        r = sd_bus_message_read(value, "s", &t);
+        if (r < 0)
+                return r;
+
+        r = log_level_from_string(t);
+        if (r < 0)
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid log level '%s'", t);
+
+        log_info("Setting log level to %s.", t);
+        log_set_max_level(r);
+
+        return 0;
+}
+
+BUS_DEFINE_PROPERTY_GET_GLOBAL(bus_property_get_log_target, "s", log_target_to_string(log_get_target()));
+
+int bus_property_set_log_target(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *value,
+                void *userdata,
+                sd_bus_error *error) {
+
+        LogTarget target;
+        const char *t;
+        int r;
+
+        assert(bus);
+        assert(value);
+
+        r = sd_bus_message_read(value, "s", &t);
+        if (r < 0)
+                return r;
+
+        target = log_target_from_string(t);
+        if (target < 0)
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid log target '%s'", t);
+
+        log_info("Setting log target to %s.", log_target_to_string(target));
+        log_set_target(target);
+        log_open();
+
+        return 0;
+}
+
+BUS_DEFINE_PROPERTY_GET_GLOBAL(bus_property_get_syslog_identifier, "s", program_invocation_short_name);
+
+static const sd_bus_vtable log_control_vtable[] = {
+        SD_BUS_VTABLE_START(0),
+
+        SD_BUS_WRITABLE_PROPERTY("LogLevel", "s", bus_property_get_log_level, bus_property_set_log_level, 0, 0),
+        SD_BUS_WRITABLE_PROPERTY("LogTarget", "s", bus_property_get_log_target, bus_property_set_log_target, 0, 0),
+        SD_BUS_PROPERTY("SyslogIdentifier", "s", bus_property_get_syslog_identifier, 0, 0),
+
+        /* One of those days we might want to add a similar, second interface to cover common service
+         * operations such as Reload(), Reexecute(), Exit() …  and maybe some properties exposing version
+         * number and other meta-data of the service. */
+
+        SD_BUS_VTABLE_END,
+};
+
+const BusObjectImplementation log_control_object = {
+        "/org/freedesktop/LogControl1",
+        "org.freedesktop.LogControl1",
+        .vtables = BUS_VTABLES(log_control_vtable),
+};
diff --git a/src/shared/bus-log-control-api.h b/src/shared/bus-log-control-api.h
new file mode 100644 (file)
index 0000000..64eaa54
--- /dev/null
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "sd-bus.h"
+
+#include "bus-object.h"
+
+extern const BusObjectImplementation log_control_object;
+static inline int bus_log_control_api_register(sd_bus *bus) {
+        return bus_add_implementation(bus, &log_control_object, NULL);
+}
+
+int bus_property_get_log_level(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_log_level(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_log_target(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_log_target(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
+
+int bus_property_get_syslog_identifier(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
diff --git a/src/shared/bus-map-properties.c b/src/shared/bus-map-properties.c
new file mode 100644 (file)
index 0000000..ab393c3
--- /dev/null
@@ -0,0 +1,229 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "bus-map-properties.h"
+#include "alloc-util.h"
+#include "strv.h"
+#include "bus-message.h"
+
+int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
+        sd_id128_t *p = userdata;
+        const void *v;
+        size_t n;
+        int r;
+
+        r = sd_bus_message_read_array(m, SD_BUS_TYPE_BYTE, &v, &n);
+        if (r < 0)
+                return r;
+
+        if (n == 0)
+                *p = SD_ID128_NULL;
+        else if (n == 16)
+                memcpy((*p).bytes, v, n);
+        else
+                return -EINVAL;
+
+        return 0;
+}
+
+static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, unsigned flags, sd_bus_error *error, void *userdata) {
+        char type;
+        int r;
+
+        r = sd_bus_message_peek_type(m, &type, NULL);
+        if (r < 0)
+                return r;
+
+        switch (type) {
+
+        case SD_BUS_TYPE_STRING:
+        case SD_BUS_TYPE_OBJECT_PATH: {
+                const char **p = userdata;
+                const char *s;
+
+                r = sd_bus_message_read_basic(m, type, &s);
+                if (r < 0)
+                        return r;
+
+                if (isempty(s))
+                        s = NULL;
+
+                if (flags & BUS_MAP_STRDUP)
+                        return free_and_strdup((char **) userdata, s);
+
+                *p = s;
+                return 0;
+        }
+
+        case SD_BUS_TYPE_ARRAY: {
+                _cleanup_strv_free_ char **l = NULL;
+                char ***p = userdata;
+
+                r = bus_message_read_strv_extend(m, &l);
+                if (r < 0)
+                        return r;
+
+                return strv_extend_strv(p, l, false);
+        }
+
+        case SD_BUS_TYPE_BOOLEAN: {
+                int b;
+
+                r = sd_bus_message_read_basic(m, type, &b);
+                if (r < 0)
+                        return r;
+
+                if (flags & BUS_MAP_BOOLEAN_AS_BOOL)
+                        *(bool*) userdata = b;
+                else
+                        *(int*) userdata = b;
+
+                return 0;
+        }
+
+        case SD_BUS_TYPE_INT32:
+        case SD_BUS_TYPE_UINT32: {
+                uint32_t u, *p = userdata;
+
+                r = sd_bus_message_read_basic(m, type, &u);
+                if (r < 0)
+                        return r;
+
+                *p = u;
+                return 0;
+        }
+
+        case SD_BUS_TYPE_INT64:
+        case SD_BUS_TYPE_UINT64: {
+                uint64_t t, *p = userdata;
+
+                r = sd_bus_message_read_basic(m, type, &t);
+                if (r < 0)
+                        return r;
+
+                *p = t;
+                return 0;
+        }
+
+        case SD_BUS_TYPE_DOUBLE: {
+                double d, *p = userdata;
+
+                r = sd_bus_message_read_basic(m, type, &d);
+                if (r < 0)
+                        return r;
+
+                *p = d;
+                return 0;
+        }}
+
+        return -EOPNOTSUPP;
+}
+
+int bus_message_map_all_properties(
+                sd_bus_message *m,
+                const struct bus_properties_map *map,
+                unsigned flags,
+                sd_bus_error *error,
+                void *userdata) {
+
+        int r;
+
+        assert(m);
+        assert(map);
+
+        r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
+        if (r < 0)
+                return r;
+
+        while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
+                const struct bus_properties_map *prop;
+                const char *member;
+                const char *contents;
+                void *v;
+                unsigned i;
+
+                r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &member);
+                if (r < 0)
+                        return r;
+
+                for (i = 0, prop = NULL; map[i].member; i++)
+                        if (streq(map[i].member, member)) {
+                                prop = &map[i];
+                                break;
+                        }
+
+                if (prop) {
+                        r = sd_bus_message_peek_type(m, NULL, &contents);
+                        if (r < 0)
+                                return r;
+
+                        r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents);
+                        if (r < 0)
+                                return r;
+
+                        v = (uint8_t *)userdata + prop->offset;
+                        if (map[i].set)
+                                r = prop->set(sd_bus_message_get_bus(m), member, m, error, v);
+                        else
+                                r = map_basic(sd_bus_message_get_bus(m), member, m, flags, error, v);
+                        if (r < 0)
+                                return r;
+
+                        r = sd_bus_message_exit_container(m);
+                        if (r < 0)
+                                return r;
+                } else {
+                        r = sd_bus_message_skip(m, "v");
+                        if (r < 0)
+                                return r;
+                }
+
+                r = sd_bus_message_exit_container(m);
+                if (r < 0)
+                        return r;
+        }
+        if (r < 0)
+                return r;
+
+        return sd_bus_message_exit_container(m);
+}
+
+int bus_map_all_properties(
+                sd_bus *bus,
+                const char *destination,
+                const char *path,
+                const struct bus_properties_map *map,
+                unsigned flags,
+                sd_bus_error *error,
+                sd_bus_message **reply,
+                void *userdata) {
+
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+        int r;
+
+        assert(bus);
+        assert(destination);
+        assert(path);
+        assert(map);
+        assert(reply || (flags & BUS_MAP_STRDUP));
+
+        r = sd_bus_call_method(
+                        bus,
+                        destination,
+                        path,
+                        "org.freedesktop.DBus.Properties",
+                        "GetAll",
+                        error,
+                        &m,
+                        "s", "");
+        if (r < 0)
+                return r;
+
+        r = bus_message_map_all_properties(m, map, flags, error, userdata);
+        if (r < 0)
+                return r;
+
+        if (reply)
+                *reply = sd_bus_message_ref(m);
+
+        return r;
+}
diff --git a/src/shared/bus-map-properties.h b/src/shared/bus-map-properties.h
new file mode 100644 (file)
index 0000000..11b8992
--- /dev/null
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "sd-bus.h"
+
+typedef int (*bus_property_set_t) (sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata);
+
+struct bus_properties_map {
+        const char *member;
+        const char *signature;
+        bus_property_set_t set;
+        size_t offset;
+};
+
+enum {
+        BUS_MAP_STRDUP          = 1 << 0, /* If set, each "s" message is duplicated. Thus, each pointer needs to be freed. */
+        BUS_MAP_BOOLEAN_AS_BOOL = 1 << 1, /* If set, each "b" message is written to a bool pointer. If not set, "b" is written to a int pointer. */
+};
+
+int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata);
+
+int bus_message_map_all_properties(sd_bus_message *m, const struct bus_properties_map *map, unsigned flags, sd_bus_error *error, void *userdata);
+int bus_map_all_properties(sd_bus *bus, const char *destination, const char *path, const struct bus_properties_map *map,
+                           unsigned flags, sd_bus_error *error, sd_bus_message **reply, void *userdata);
diff --git a/src/shared/bus-message-util.c b/src/shared/bus-message-util.c
new file mode 100644 (file)
index 0000000..85e1e98
--- /dev/null
@@ -0,0 +1,182 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "bus-message-util.h"
+
+#include "resolve-util.h"
+
+int bus_message_read_ifindex(sd_bus_message *message, sd_bus_error *error, int *ret) {
+        int ifindex, r;
+
+        assert(message);
+        assert(ret);
+
+        assert_cc(sizeof(int) == sizeof(int32_t));
+
+        r = sd_bus_message_read(message, "i", &ifindex);
+        if (r < 0)
+                return r;
+
+        if (ifindex <= 0)
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid interface index");
+
+        *ret = ifindex;
+
+        return 0;
+}
+
+int bus_message_read_family(sd_bus_message *message, sd_bus_error *error, int *ret) {
+        int family, r;
+
+        assert(message);
+        assert(ret);
+
+        assert_cc(sizeof(int) == sizeof(int32_t));
+
+        r = sd_bus_message_read(message, "i", &family);
+        if (r < 0)
+                return r;
+
+        if (!IN_SET(family, AF_INET, AF_INET6))
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
+
+        *ret = family;
+        return 0;
+}
+
+int bus_message_read_in_addr_auto(sd_bus_message *message, sd_bus_error *error, int *ret_family, union in_addr_union *ret_addr) {
+        int family, r;
+        const void *d;
+        size_t sz;
+
+        assert(message);
+
+        r = sd_bus_message_read(message, "i", &family);
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_read_array(message, 'y', &d, &sz);
+        if (r < 0)
+                return r;
+
+        if (!IN_SET(family, AF_INET, AF_INET6))
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family);
+
+        if (sz != FAMILY_ADDRESS_SIZE(family))
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address size");
+
+        if (ret_family)
+                *ret_family = family;
+        if (ret_addr)
+                memcpy(ret_addr, d, sz);
+        return 0;
+}
+
+static int bus_message_read_dns_one(
+                        sd_bus_message *message,
+                        sd_bus_error *error,
+                        bool extended,
+                        int *ret_family,
+                        union in_addr_union *ret_address,
+                        uint16_t *ret_port,
+                        const char **ret_server_name) {
+        const char *server_name = NULL;
+        union in_addr_union a;
+        uint16_t port = 0;
+        int family, r;
+
+        assert(message);
+        assert(ret_family);
+        assert(ret_address);
+        assert(ret_port);
+        assert(ret_server_name);
+
+        r = sd_bus_message_enter_container(message, 'r', extended ? "iayqs" : "iay");
+        if (r <= 0)
+                return r;
+
+        r = bus_message_read_in_addr_auto(message, error, &family, &a);
+        if (r < 0)
+                return r;
+
+        if (!dns_server_address_valid(family, &a))
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid DNS server address");
+
+        if (extended) {
+                r = sd_bus_message_read(message, "q", &port);
+                if (r < 0)
+                        return r;
+
+                if (IN_SET(port, 53, 853))
+                        port = 0;
+
+                r = sd_bus_message_read(message, "s", &server_name);
+                if (r < 0)
+                        return r;
+        }
+
+        r = sd_bus_message_exit_container(message);
+        if (r < 0)
+                return r;
+
+        *ret_family = family;
+        *ret_address = a;
+        *ret_port = port;
+        *ret_server_name = server_name;
+
+        return 1;
+}
+
+int bus_message_read_dns_servers(
+                        sd_bus_message *message,
+                        sd_bus_error *error,
+                        bool extended,
+                        struct in_addr_full ***ret_dns,
+                        size_t *ret_n_dns) {
+
+        struct in_addr_full **dns = NULL;
+        size_t n = 0, allocated = 0;
+        int r;
+
+        assert(message);
+        assert(ret_dns);
+        assert(ret_n_dns);
+
+        r = sd_bus_message_enter_container(message, 'a', extended ? "(iayqs)" : "(iay)");
+        if (r < 0)
+                return r;
+
+        for (;;) {
+                const char *server_name;
+                union in_addr_union a;
+                uint16_t port;
+                int family;
+
+                r = bus_message_read_dns_one(message, error, extended, &family, &a, &port, &server_name);
+                if (r < 0)
+                        goto clear;
+                if (r == 0)
+                        break;
+
+                if (!GREEDY_REALLOC(dns, allocated, n+1)) {
+                        r = -ENOMEM;
+                        goto clear;
+                }
+
+                r = in_addr_full_new(family, &a, port, 0, server_name, dns + n);
+                if (r < 0)
+                        goto clear;
+
+                n++;
+        }
+
+        *ret_dns = TAKE_PTR(dns);
+        *ret_n_dns = n;
+        return 0;
+
+clear:
+        for (size_t i = 0; i < n; i++)
+                in_addr_full_free(dns[i]);
+        free(dns);
+
+        return r;
+}
diff --git a/src/shared/bus-message-util.h b/src/shared/bus-message-util.h
new file mode 100644 (file)
index 0000000..98ad035
--- /dev/null
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "sd-bus.h"
+
+#include "in-addr-util.h"
+#include "socket-netlink.h"
+
+int bus_message_read_ifindex(sd_bus_message *message, sd_bus_error *error, int *ret);
+int bus_message_read_family(sd_bus_message *message, sd_bus_error *error, int *ret);
+int bus_message_read_in_addr_auto(sd_bus_message *message, sd_bus_error *error, int *ret_family, union in_addr_union *ret_addr);
+
+int bus_message_read_dns_servers(
+                        sd_bus_message *message,
+                        sd_bus_error *error,
+                        bool extended,
+                        struct in_addr_full ***ret_dns,
+                        size_t *ret_n_dns);
diff --git a/src/shared/bus-object.c b/src/shared/bus-object.c
new file mode 100644 (file)
index 0000000..217d43d
--- /dev/null
@@ -0,0 +1,177 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "bus-introspect.h"
+#include "bus-object.h"
+#include "macro.h"
+#include "string-util.h"
+#include "strv.h"
+
+int bus_add_implementation(sd_bus *bus, const BusObjectImplementation *impl, void *userdata) {
+        int r;
+
+        log_debug("Registering bus object implementation for path=%s iface=%s", impl->path, impl->interface);
+
+        for (const sd_bus_vtable **p = impl->vtables; p && *p; p++) {
+                r = sd_bus_add_object_vtable(bus, NULL,
+                                             impl->path,
+                                             impl->interface,
+                                             *p,
+                                             userdata);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to register bus path %s with interface %s: %m",
+                                               impl->path,
+                                               impl->interface);
+        }
+
+        for (const BusObjectVtablePair *p = impl->fallback_vtables; p && p->vtable; p++) {
+                r = sd_bus_add_fallback_vtable(bus, NULL,
+                                               impl->path,
+                                               impl->interface,
+                                               p->vtable,
+                                               p->object_find,
+                                               userdata);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to register bus path %s with interface %s: %m",
+                                               impl->path,
+                                               impl->interface);
+        }
+
+        if (impl->node_enumerator) {
+                r = sd_bus_add_node_enumerator(bus, NULL,
+                                               impl->path,
+                                               impl->node_enumerator,
+                                               userdata);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to add node enumerator for %s: %m",
+                                               impl->path);
+        }
+
+        if (impl->manager) {
+                r = sd_bus_add_object_manager(bus, NULL, impl->path);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to add object manager for %s: %m", impl->path);
+        }
+
+        for (size_t i = 0; impl->children && impl->children[i]; i++) {
+                r = bus_add_implementation(bus, impl->children[i], userdata);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+static const BusObjectImplementation* find_implementation(
+                const char *pattern,
+                const BusObjectImplementation* const* bus_objects) {
+
+        for (size_t i = 0; bus_objects && bus_objects[i]; i++) {
+                const BusObjectImplementation *impl = bus_objects[i];
+
+                if (STR_IN_SET(pattern, impl->path, impl->interface))
+                        return impl;
+
+                impl = find_implementation(pattern, impl->children);
+                if (impl)
+                        return impl;
+        }
+
+        return NULL;
+}
+
+static int bus_introspect_implementation(
+                struct introspect *intro,
+                const BusObjectImplementation *impl) {
+        int r;
+
+        for (const sd_bus_vtable **p = impl->vtables; p && *p; p++) {
+                r = introspect_write_interface(intro, impl->interface, *p);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to write introspection data: %m");
+        }
+
+        for (const BusObjectVtablePair *p = impl->fallback_vtables; p && p->vtable; p++) {
+                r = introspect_write_interface(intro, impl->interface, p->vtable);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to write introspection data: %m");
+        }
+
+        return 0;
+}
+
+static void list_paths(
+                FILE *out,
+                const BusObjectImplementation* const* bus_objects) {
+
+        for (size_t i = 0; bus_objects[i]; i++) {
+                fprintf(out, "%s\t%s\n", bus_objects[i]->path, bus_objects[i]->interface);
+                if (bus_objects[i]->children)
+                        list_paths(out, bus_objects[i]->children);
+        }
+}
+
+int bus_introspect_implementations(
+                FILE *out,
+                const char *pattern,
+                const BusObjectImplementation* const* bus_objects) {
+
+        const BusObjectImplementation *impl, *main_impl = NULL;
+        _cleanup_free_ char *s = NULL;
+        int r;
+
+        if (streq(pattern, "list")) {
+                list_paths(out, bus_objects);
+                return 0;
+        }
+
+        struct introspect intro = {};
+        bool is_interface = sd_bus_interface_name_is_valid(pattern);
+
+        impl = find_implementation(pattern, bus_objects);
+        if (!impl)
+                return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
+                                       "%s %s not found",
+                                       is_interface ? "Interface" : "Object path",
+                                       pattern);
+
+        /* We use trusted=false here to get all the @org.freedesktop.systemd1.Privileged annotations. */
+        r = introspect_begin(&intro, false);
+        if (r < 0)
+                return log_error_errno(r, "Failed to write introspection data: %m");
+
+        r = introspect_write_default_interfaces(&intro, impl->manager);
+        if (r < 0)
+                return log_error_errno(r, "Failed to write introspection data: %m");
+
+        /* Check if there is a non-fallback path that applies to the given interface, also
+         * print it. This is useful in the case of units: o.fd.systemd1.Service is declared
+         * as a fallback vtable for o/fd/systemd1/unit, and we also want to print
+         * o.fd.systemd1.Unit, which is the non-fallback implementation. */
+        if (impl->fallback_vtables && is_interface)
+                main_impl = find_implementation(impl->path, bus_objects);
+
+        if (main_impl)
+                bus_introspect_implementation(&intro, main_impl);
+
+        if (impl != main_impl)
+                bus_introspect_implementation(&intro, impl);
+
+        _cleanup_set_free_ Set *nodes = NULL;
+
+        for (size_t i = 0; impl->children && impl->children[i]; i++) {
+                r = set_put_strdup(&nodes, impl->children[i]->path);
+                if (r < 0)
+                        return log_oom();
+        }
+
+        r = introspect_write_child_nodes(&intro, nodes, impl->path);
+        if (r < 0)
+                return r;
+
+        r = introspect_finish(&intro, &s);
+        if (r < 0)
+                return log_error_errno(r, "Failed to write introspection data: %m");
+
+        fputs(s, out);
+        return 0;
+}
diff --git a/src/shared/bus-object.h b/src/shared/bus-object.h
new file mode 100644 (file)
index 0000000..8add854
--- /dev/null
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <stdbool.h>
+#include <stdio.h>
+
+#include "sd-bus.h"
+
+typedef struct BusObjectImplementation BusObjectImplementation;
+
+typedef struct BusObjectVtablePair {
+        const sd_bus_vtable *vtable;
+        sd_bus_object_find_t object_find;
+} BusObjectVtablePair;
+
+struct BusObjectImplementation {
+        const char *path;
+        const char *interface;
+        const sd_bus_vtable **vtables;
+        const BusObjectVtablePair *fallback_vtables;
+        sd_bus_node_enumerator_t node_enumerator;
+        bool manager;
+        const BusObjectImplementation **children;
+};
+
+#define BUS_VTABLES(...) ((const sd_bus_vtable* []){ __VA_ARGS__, NULL })
+#define BUS_FALLBACK_VTABLES(...) ((const BusObjectVtablePair[]) { __VA_ARGS__, {} })
+#define BUS_IMPLEMENTATIONS(...) ((const BusObjectImplementation* []) { __VA_ARGS__, NULL })
+
+int bus_add_implementation(sd_bus *bus, const BusObjectImplementation *impl, void *userdata);
+int bus_introspect_implementations(
+                FILE *out,
+                const char *pattern,
+                const BusObjectImplementation* const* bus_objects);
diff --git a/src/shared/bus-print-properties.c b/src/shared/bus-print-properties.c
new file mode 100644 (file)
index 0000000..bf7d050
--- /dev/null
@@ -0,0 +1,462 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "bus-print-properties.h"
+#include "cap-list.h"
+#include "cgroup-util.h"
+#include "escape.h"
+#include "mountpoint-util.h"
+#include "nsflags.h"
+#include "parse-util.h"
+#include "stdio-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "time-util.h"
+#include "user-util.h"
+
+int bus_print_property_value(const char *name, const char *expected_value, bool only_value, const char *value) {
+        assert(name);
+
+        if (expected_value && !streq_ptr(expected_value, value))
+                return 0;
+
+        if (only_value)
+                puts(value);
+        else
+                printf("%s=%s\n", name, value);
+
+        return 0;
+}
+
+int bus_print_property_valuef(const char *name, const char *expected_value, bool only_value, const char *fmt, ...) {
+        va_list ap;
+        int r;
+
+        assert(name);
+        assert(fmt);
+
+        if (expected_value) {
+                _cleanup_free_ char *s = NULL;
+
+                va_start(ap, fmt);
+                r = vasprintf(&s, fmt, ap);
+                va_end(ap);
+                if (r < 0)
+                        return -ENOMEM;
+
+                if (streq_ptr(expected_value, s)) {
+                        if (only_value)
+                                puts(s);
+                        else
+                                printf("%s=%s\n", name, s);
+                }
+
+                return 0;
+        }
+
+        if (!only_value)
+                printf("%s=", name);
+        va_start(ap, fmt);
+        vprintf(fmt, ap);
+        va_end(ap);
+        puts("");
+
+        return 0;
+}
+
+static int bus_print_property(const char *name, const char *expected_value, sd_bus_message *m, bool value, bool all) {
+        char type;
+        const char *contents;
+        int r;
+
+        assert(name);
+        assert(m);
+
+        r = sd_bus_message_peek_type(m, &type, &contents);
+        if (r < 0)
+                return r;
+
+        switch (type) {
+
+        case SD_BUS_TYPE_STRING: {
+                const char *s;
+
+                r = sd_bus_message_read_basic(m, type, &s);
+                if (r < 0)
+                        return r;
+
+                if (all || !isempty(s)) {
+                        bool good;
+
+                        /* This property has a single value, so we need to take
+                         * care not to print a new line, everything else is OK. */
+                        good = !strchr(s, '\n');
+                        bus_print_property_value(name, expected_value, value, good ? s : "[unprintable]");
+                }
+
+                return 1;
+        }
+
+        case SD_BUS_TYPE_BOOLEAN: {
+                int b;
+
+                r = sd_bus_message_read_basic(m, type, &b);
+                if (r < 0)
+                        return r;
+
+                if (expected_value && parse_boolean(expected_value) != b)
+                        return 1;
+
+                bus_print_property_value(name, NULL, value, yes_no(b));
+                return 1;
+        }
+
+        case SD_BUS_TYPE_UINT64: {
+                uint64_t u;
+
+                r = sd_bus_message_read_basic(m, type, &u);
+                if (r < 0)
+                        return r;
+
+                /* Yes, heuristics! But we can change this check
+                 * should it turn out to not be sufficient */
+
+                if (endswith(name, "Timestamp") ||
+                    STR_IN_SET(name, "NextElapseUSecRealtime", "LastTriggerUSec", "TimeUSec", "RTCTimeUSec")) {
+                        char timestamp[FORMAT_TIMESTAMP_MAX];
+                        const char *t;
+
+                        t = format_timestamp(timestamp, sizeof(timestamp), u);
+                        if (t || all)
+                                bus_print_property_value(name, expected_value, value, strempty(t));
+
+                } else if (strstr(name, "USec")) {
+                        char timespan[FORMAT_TIMESPAN_MAX];
+
+                        (void) format_timespan(timespan, sizeof(timespan), u, 0);
+                        bus_print_property_value(name, expected_value, value, timespan);
+
+                } else if (streq(name, "CoredumpFilter")) {
+                        char buf[STRLEN("0xFFFFFFFF")];
+
+                        xsprintf(buf, "0x%"PRIx64, u);
+                        bus_print_property_value(name, expected_value, value, buf);
+
+                } else if (streq(name, "RestrictNamespaces")) {
+                        _cleanup_free_ char *s = NULL;
+                        const char *result;
+
+                        if ((u & NAMESPACE_FLAGS_ALL) == 0)
+                                result = "yes";
+                        else if (FLAGS_SET(u, NAMESPACE_FLAGS_ALL))
+                                result = "no";
+                        else {
+                                r = namespace_flags_to_string(u, &s);
+                                if (r < 0)
+                                        return r;
+
+                                result = strempty(s);
+                        }
+
+                        bus_print_property_value(name, expected_value, value, result);
+
+                } else if (streq(name, "MountFlags")) {
+                        const char *result;
+
+                        result = mount_propagation_flags_to_string(u);
+                        if (!result)
+                                return -EINVAL;
+
+                        bus_print_property_value(name, expected_value, value, result);
+
+                } else if (STR_IN_SET(name, "CapabilityBoundingSet", "AmbientCapabilities")) {
+                        _cleanup_free_ char *s = NULL;
+
+                        r = capability_set_to_string_alloc(u, &s);
+                        if (r < 0)
+                                return r;
+
+                        bus_print_property_value(name, expected_value, value, s);
+
+                } else if ((STR_IN_SET(name, "CPUWeight", "StartupCPUWeight", "IOWeight", "StartupIOWeight") && u == CGROUP_WEIGHT_INVALID) ||
+                           (STR_IN_SET(name, "CPUShares", "StartupCPUShares") && u == CGROUP_CPU_SHARES_INVALID) ||
+                           (STR_IN_SET(name, "BlockIOWeight", "StartupBlockIOWeight") && u == CGROUP_BLKIO_WEIGHT_INVALID) ||
+                           (STR_IN_SET(name, "MemoryCurrent", "TasksCurrent") && u == (uint64_t) -1) ||
+                           (endswith(name, "NSec") && u == (uint64_t) -1))
+
+                        bus_print_property_value(name, expected_value, value, "[not set]");
+
+                else if ((STR_IN_SET(name, "DefaultMemoryLow", "DefaultMemoryMin", "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit") && u == CGROUP_LIMIT_MAX) ||
+                         (STR_IN_SET(name, "TasksMax", "DefaultTasksMax") && u == (uint64_t) -1) ||
+                         (startswith(name, "Limit") && u == (uint64_t) -1) ||
+                         (startswith(name, "DefaultLimit") && u == (uint64_t) -1))
+
+                        bus_print_property_value(name, expected_value, value, "infinity");
+                else if (STR_IN_SET(name, "IPIngressBytes", "IPIngressPackets", "IPEgressBytes", "IPEgressPackets") && u == (uint64_t) -1)
+                        bus_print_property_value(name, expected_value, value, "[no data]");
+                else
+                        bus_print_property_valuef(name, expected_value, value, "%"PRIu64, u);
+
+                return 1;
+        }
+
+        case SD_BUS_TYPE_INT64: {
+                int64_t i;
+
+                r = sd_bus_message_read_basic(m, type, &i);
+                if (r < 0)
+                        return r;
+
+                bus_print_property_valuef(name, expected_value, value, "%"PRIi64, i);
+                return 1;
+        }
+
+        case SD_BUS_TYPE_UINT32: {
+                uint32_t u;
+
+                r = sd_bus_message_read_basic(m, type, &u);
+                if (r < 0)
+                        return r;
+
+                if (strstr(name, "UMask") || strstr(name, "Mode"))
+                        bus_print_property_valuef(name, expected_value, value, "%04o", u);
+
+                else if (streq(name, "UID")) {
+                        if (u == UID_INVALID)
+                                bus_print_property_value(name, expected_value, value, "[not set]");
+                        else
+                                bus_print_property_valuef(name, expected_value, value, "%"PRIu32, u);
+                } else if (streq(name, "GID")) {
+                        if (u == GID_INVALID)
+                                bus_print_property_value(name, expected_value, value, "[not set]");
+                        else
+                                bus_print_property_valuef(name, expected_value, value, "%"PRIu32, u);
+                } else
+                        bus_print_property_valuef(name, expected_value, value, "%"PRIu32, u);
+
+                return 1;
+        }
+
+        case SD_BUS_TYPE_INT32: {
+                int32_t i;
+
+                r = sd_bus_message_read_basic(m, type, &i);
+                if (r < 0)
+                        return r;
+
+                bus_print_property_valuef(name, expected_value, value, "%"PRIi32, i);
+                return 1;
+        }
+
+        case SD_BUS_TYPE_DOUBLE: {
+                double d;
+
+                r = sd_bus_message_read_basic(m, type, &d);
+                if (r < 0)
+                        return r;
+
+                bus_print_property_valuef(name, expected_value, value, "%g", d);
+                return 1;
+        }
+
+        case SD_BUS_TYPE_ARRAY:
+                if (streq(contents, "s")) {
+                        bool first = true;
+                        const char *str;
+
+                        r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, contents);
+                        if (r < 0)
+                                return r;
+
+                        while ((r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &str)) > 0) {
+                                _cleanup_free_ char *e = NULL;
+
+                                e = shell_maybe_quote(str, ESCAPE_BACKSLASH_ONELINE);
+                                if (!e)
+                                        return -ENOMEM;
+
+                                if (first) {
+                                        if (!value)
+                                                printf("%s=", name);
+                                        first = false;
+                                } else
+                                        fputs(" ", stdout);
+
+                                fputs(e, stdout);
+                        }
+                        if (r < 0)
+                                return r;
+
+                        if (first && all && !value)
+                                printf("%s=", name);
+                        if (!first || all)
+                                puts("");
+
+                        r = sd_bus_message_exit_container(m);
+                        if (r < 0)
+                                return r;
+
+                        return 1;
+
+                } else if (streq(contents, "y")) {
+                        const uint8_t *u;
+                        size_t n;
+
+                        r = sd_bus_message_read_array(m, SD_BUS_TYPE_BYTE, (const void**) &u, &n);
+                        if (r < 0)
+                                return r;
+
+                        if (all || n > 0) {
+                                unsigned i;
+
+                                if (!value)
+                                        printf("%s=", name);
+
+                                for (i = 0; i < n; i++)
+                                        printf("%02x", u[i]);
+
+                                puts("");
+                        }
+
+                        return 1;
+
+                } else if (streq(contents, "u")) {
+                        uint32_t *u;
+                        size_t n;
+
+                        r = sd_bus_message_read_array(m, SD_BUS_TYPE_UINT32, (const void**) &u, &n);
+                        if (r < 0)
+                                return r;
+
+                        if (all || n > 0) {
+                                unsigned i;
+
+                                if (!value)
+                                        printf("%s=", name);
+
+                                for (i = 0; i < n; i++)
+                                        printf("%08x", u[i]);
+
+                                puts("");
+                        }
+
+                        return 1;
+                }
+
+                break;
+        }
+
+        return 0;
+}
+
+int bus_message_print_all_properties(
+                sd_bus_message *m,
+                bus_message_print_t func,
+                char **filter,
+                bool value,
+                bool all,
+                Set **found_properties) {
+
+        int r;
+
+        assert(m);
+
+        r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
+        if (r < 0)
+                return r;
+
+        while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
+                _cleanup_free_ char *name_with_equal = NULL;
+                const char *name, *contents, *expected_value = NULL;
+
+                r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &name);
+                if (r < 0)
+                        return r;
+
+                if (found_properties) {
+                        r = set_ensure_put(found_properties, &string_hash_ops, name);
+                        if (r < 0)
+                                return log_oom();
+                }
+
+                name_with_equal = strjoin(name, "=");
+                if (!name_with_equal)
+                        return log_oom();
+
+                if (!filter || strv_find(filter, name) ||
+                    (expected_value = strv_find_startswith(filter, name_with_equal))) {
+                        r = sd_bus_message_peek_type(m, NULL, &contents);
+                        if (r < 0)
+                                return r;
+
+                        r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents);
+                        if (r < 0)
+                                return r;
+
+                        if (func)
+                                r = func(name, expected_value, m, value, all);
+                        if (!func || r == 0)
+                                r = bus_print_property(name, expected_value, m, value, all);
+                        if (r < 0)
+                                return r;
+                        if (r == 0) {
+                                if (all && !expected_value)
+                                        printf("%s=[unprintable]\n", name);
+                                /* skip what we didn't read */
+                                r = sd_bus_message_skip(m, contents);
+                                if (r < 0)
+                                        return r;
+                        }
+
+                        r = sd_bus_message_exit_container(m);
+                        if (r < 0)
+                                return r;
+                } else {
+                        r = sd_bus_message_skip(m, "v");
+                        if (r < 0)
+                                return r;
+                }
+
+                r = sd_bus_message_exit_container(m);
+                if (r < 0)
+                        return r;
+        }
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_exit_container(m);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+int bus_print_all_properties(
+                sd_bus *bus,
+                const char *dest,
+                const char *path,
+                bus_message_print_t func,
+                char **filter,
+                bool value,
+                bool all,
+                Set **found_properties) {
+
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        int r;
+
+        assert(bus);
+        assert(path);
+
+        r = sd_bus_call_method(bus,
+                        dest,
+                        path,
+                        "org.freedesktop.DBus.Properties",
+                        "GetAll",
+                        &error,
+                        &reply,
+                        "s", "");
+        if (r < 0)
+                return r;
+
+        return bus_message_print_all_properties(reply, func, filter, value, all, found_properties);
+}
diff --git a/src/shared/bus-print-properties.h b/src/shared/bus-print-properties.h
new file mode 100644 (file)
index 0000000..1c21dff
--- /dev/null
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <stdbool.h>
+
+#include "sd-bus.h"
+
+#include "macro.h"
+#include "set.h"
+
+typedef int (*bus_message_print_t) (const char *name, const char *expected_value, sd_bus_message *m, bool value, bool all);
+
+int bus_print_property_value(const char *name, const char *expected_value, bool only_value, const char *value);
+int bus_print_property_valuef(const char *name, const char *expected_value, bool only_value, const char *fmt, ...) _printf_(4,5);
+int bus_message_print_all_properties(sd_bus_message *m, bus_message_print_t func, char **filter, bool value, bool all, Set **found_properties);
+int bus_print_all_properties(sd_bus *bus, const char *dest, const char *path, bus_message_print_t func, char **filter, bool value, bool all, Set **found_properties);
index a30876c1a13f672dedc943c7923b11c2c9570e95..f2652ed9a59dafeef5a03940b338a96d066056f0 100644 (file)
@@ -8,10 +8,12 @@
 #include "cgroup-setup.h"
 #include "cgroup-util.h"
 #include "condition.h"
+#include "coredump-util.h"
 #include "cpu-set-util.h"
 #include "escape.h"
 #include "exec-util.h"
 #include "exit-status.h"
+#include "fileio.h"
 #include "hexdecoct.h"
 #include "hostname-util.h"
 #include "in-addr-util.h"
@@ -23,6 +25,7 @@
 #include "nsflags.h"
 #include "numa-util.h"
 #include "parse-util.h"
+#include "path-util.h"
 #include "process-util.h"
 #include "rlimit-util.h"
 #include "securebits-util.h"
@@ -119,6 +122,7 @@ 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);
+DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, coredump_filter_mask_from_string);
 
 static int bus_append_string(sd_bus_message *m, const char *field, const char *eq) {
         int r;
@@ -487,11 +491,24 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
                               "MemoryLimit",
                               "TasksMax")) {
 
-                if (isempty(eq) || streq(eq, "infinity")) {
+                if (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;
+                } else if (isempty(eq)) {
+                        uint64_t empty_value = STR_IN_SET(field,
+                                                          "DefaultMemoryLow",
+                                                          "DefaultMemoryMin",
+                                                          "MemoryLow",
+                                                          "MemoryMin") ?
+                                               CGROUP_LIMIT_MIN :
+                                               CGROUP_LIMIT_MAX;
+
+                        r = sd_bus_message_append(m, "(sv)", field, "t", empty_value);
+                        if (r < 0)
+                                return bus_log_create_error(r);
+                        return 1;
                 }
 
                 r = parse_permille(eq);
@@ -832,6 +849,7 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
                               "ProtectHome",
                               "SELinuxContext",
                               "RootImage",
+                              "RootVerity",
                               "RuntimeDirectoryPreserve",
                               "Personality",
                               "KeyringMode",
@@ -898,6 +916,9 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
                               "OOMScoreAdjust"))
                 return bus_append_safe_atoi(m, field, eq);
 
+        if (streq(field, "CoredumpFilter"))
+                return bus_append_coredump_filter_mask_from_string(m, field, eq);
+
         if (streq(field, "Nice"))
                 return bus_append_parse_nice(m, field, eq);
 
@@ -1152,11 +1173,11 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
 
         if (STR_IN_SET(field, "RestrictAddressFamilies",
                               "SystemCallFilter")) {
-                int whitelist = 1;
+                int allow_list = 1;
                 const char *p = eq;
 
                 if (*p == '~') {
-                        whitelist = 0;
+                        allow_list = 0;
                         p++;
                 }
 
@@ -1176,7 +1197,7 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
                 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, 'b', &allow_list);
                 if (r < 0)
                         return bus_log_create_error(r);
 
@@ -1395,6 +1416,44 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
                 return 1;
         }
 
+        if (streq(field, "RootHash")) {
+                _cleanup_free_ void *roothash_decoded = NULL;
+                size_t roothash_decoded_size = 0;
+
+                /* We have the path to a roothash to load and decode, eg: RootHash=/foo/bar.roothash */
+                if (path_is_absolute(eq))
+                        return bus_append_string(m, "RootHashPath", eq);
+
+                /* We have a roothash to decode, eg: RootHash=012345789abcdef */
+                r = unhexmem(eq, strlen(eq), &roothash_decoded, &roothash_decoded_size);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to decode RootHash= '%s': %m", eq);
+                if (roothash_decoded_size < sizeof(sd_id128_t))
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "RootHash= '%s' is too short: %m", eq);
+
+                return bus_append_byte_array(m, field, roothash_decoded, roothash_decoded_size);
+        }
+
+        if (streq(field, "RootHashSignature")) {
+                _cleanup_free_ void *roothash_sig_decoded = NULL;
+                char *value;
+                size_t roothash_sig_decoded_size = 0;
+
+                /* We have the path to a roothash signature to load and decode, eg: RootHash=/foo/bar.roothash.p7s */
+                if (path_is_absolute(eq))
+                        return bus_append_string(m, "RootHashSignaturePath", eq);
+
+                if (!(value = startswith(eq, "base64:")))
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to decode RootHashSignature= '%s', not a path but doesn't start with 'base64:': %m", eq);
+
+                /* We have a roothash signature to decode, eg: RootHashSignature=base64:012345789abcdef */
+                r = unbase64mem(value, strlen(value), &roothash_sig_decoded, &roothash_sig_decoded_size);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to decode RootHashSignature= '%s': %m", eq);
+
+                return bus_append_byte_array(m, field, roothash_sig_decoded, roothash_sig_decoded_size);
+        }
+
         return 0;
 }
 
@@ -1431,7 +1490,8 @@ static int bus_append_mount_property(sd_bus_message *m, const char *field, const
 
         if (STR_IN_SET(field, "SloppyOptions",
                               "LazyUnmount",
-                              "ForceUnmount"))
+                              "ForceUnmount",
+                              "ReadwriteOnly"))
                 return bus_append_parse_boolean(m, field, eq);
 
         return 0;
@@ -1484,7 +1544,9 @@ static int bus_append_service_property(sd_bus_message *m, const char *field, con
                               "NotifyAccess",
                               "USBFunctionDescriptors",
                               "USBFunctionStrings",
-                              "OOMPolicy"))
+                              "OOMPolicy",
+                              "TimeoutStartFailureMode",
+                              "TimeoutStopFailureMode"))
                 return bus_append_string(m, field, eq);
 
         if (STR_IN_SET(field, "PermissionsStartOnly",
@@ -1626,6 +1688,7 @@ static int bus_append_socket_property(sd_bus_message *m, const char *field, cons
                               "Broadcast",
                               "PassCredentials",
                               "PassSecurity",
+                              "PassPacketInfo",
                               "ReusePort",
                               "RemoveOnStop",
                               "SELinuxContextFromNet"))
index 8e6a6e2ce2dedfb1f1340f497bc916d486353253..77c1c6218272bbfb89b45364822d1bb90fff83b5 100644 (file)
 #include "sd-event.h"
 #include "sd-id128.h"
 
-#include "alloc-util.h"
+/* #include "alloc-util.h" */
 #include "bus-internal.h"
 #include "bus-label.h"
-#include "bus-message.h"
 #include "bus-util.h"
-#include "cap-list.h"
-#include "cgroup-util.h"
-#include "mountpoint-util.h"
-#include "nsflags.h"
-#include "parse-util.h"
 #include "path-util.h"
-#include "rlimit-util.h"
 #include "socket-util.h"
 #include "stdio-util.h"
-#include "strv.h"
-#include "user-util.h"
+/* #include "string-util.h" */
 
 static int name_owner_change_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
         sd_event *e = userdata;
@@ -115,36 +107,16 @@ int bus_event_loop_with_idle(
                         return r;
 
                 if (r == 0 && !exiting && idle) {
+                        /* Inform the service manager that we are going down, so that it will queue all
+                         * further start requests, instead of assuming we are already running. */
+                        sd_notify(false, "STOPPING=1");
 
-                        r = sd_bus_try_close(bus);
-                        if (r == -EBUSY)
-                                continue;
-
-                        /* Fallback for dbus1 connections: we
-                         * unregister the name and wait for the
-                         * response to come through for it */
-                        if (r == -EOPNOTSUPP) {
-
-                                /* Inform the service manager that we
-                                 * are going down, so that it will
-                                 * queue all further start requests,
-                                 * instead of assuming we are already
-                                 * running. */
-                                sd_notify(false, "STOPPING=1");
-
-                                r = bus_async_unregister_and_exit(e, bus, name);
-                                if (r < 0)
-                                        return r;
-
-                                exiting = true;
-                                continue;
-                        }
-
+                        r = bus_async_unregister_and_exit(e, bus, name);
                         if (r < 0)
                                 return r;
 
-                        sd_event_exit(e, 0);
-                        break;
+                        exiting = true;
+                        continue;
                 }
         }
 
@@ -271,673 +243,6 @@ int bus_connect_user_systemd(sd_bus **_bus) {
         return 0;
 }
 
-int bus_print_property_value(const char *name, const char *expected_value, bool only_value, const char *value) {
-        assert(name);
-
-        if (expected_value && !streq_ptr(expected_value, value))
-                return 0;
-
-        if (only_value)
-                puts(value);
-        else
-                printf("%s=%s\n", name, value);
-
-        return 0;
-}
-
-int bus_print_property_valuef(const char *name, const char *expected_value, bool only_value, const char *fmt, ...) {
-        va_list ap;
-        int r;
-
-        assert(name);
-        assert(fmt);
-
-        if (expected_value) {
-                _cleanup_free_ char *s = NULL;
-
-                va_start(ap, fmt);
-                r = vasprintf(&s, fmt, ap);
-                va_end(ap);
-                if (r < 0)
-                        return -ENOMEM;
-
-                if (streq_ptr(expected_value, s)) {
-                        if (only_value)
-                                puts(s);
-                        else
-                                printf("%s=%s\n", name, s);
-                }
-
-                return 0;
-        }
-
-        if (!only_value)
-                printf("%s=", name);
-        va_start(ap, fmt);
-        vprintf(fmt, ap);
-        va_end(ap);
-        puts("");
-
-        return 0;
-}
-
-static int bus_print_property(const char *name, const char *expected_value, sd_bus_message *m, bool value, bool all) {
-        char type;
-        const char *contents;
-        int r;
-
-        assert(name);
-        assert(m);
-
-        r = sd_bus_message_peek_type(m, &type, &contents);
-        if (r < 0)
-                return r;
-
-        switch (type) {
-
-        case SD_BUS_TYPE_STRING: {
-                const char *s;
-
-                r = sd_bus_message_read_basic(m, type, &s);
-                if (r < 0)
-                        return r;
-
-                if (all || !isempty(s)) {
-                        bool good;
-
-                        /* This property has a single value, so we need to take
-                         * care not to print a new line, everything else is OK. */
-                        good = !strchr(s, '\n');
-                        bus_print_property_value(name, expected_value, value, good ? s : "[unprintable]");
-                }
-
-                return 1;
-        }
-
-        case SD_BUS_TYPE_BOOLEAN: {
-                int b;
-
-                r = sd_bus_message_read_basic(m, type, &b);
-                if (r < 0)
-                        return r;
-
-                if (expected_value && parse_boolean(expected_value) != b)
-                        return 1;
-
-                bus_print_property_value(name, NULL, value, yes_no(b));
-                return 1;
-        }
-
-        case SD_BUS_TYPE_UINT64: {
-                uint64_t u;
-
-                r = sd_bus_message_read_basic(m, type, &u);
-                if (r < 0)
-                        return r;
-
-                /* Yes, heuristics! But we can change this check
-                 * should it turn out to not be sufficient */
-
-                if (endswith(name, "Timestamp") ||
-                    STR_IN_SET(name, "NextElapseUSecRealtime", "LastTriggerUSec", "TimeUSec", "RTCTimeUSec")) {
-                        char timestamp[FORMAT_TIMESTAMP_MAX];
-                        const char *t;
-
-                        t = format_timestamp(timestamp, sizeof(timestamp), u);
-                        if (t || all)
-                                bus_print_property_value(name, expected_value, value, strempty(t));
-
-                } else if (strstr(name, "USec")) {
-                        char timespan[FORMAT_TIMESPAN_MAX];
-
-                        (void) format_timespan(timespan, sizeof(timespan), u, 0);
-                        bus_print_property_value(name, expected_value, value, timespan);
-
-                } else if (streq(name, "RestrictNamespaces")) {
-                        _cleanup_free_ char *s = NULL;
-                        const char *result;
-
-                        if ((u & NAMESPACE_FLAGS_ALL) == 0)
-                                result = "yes";
-                        else if (FLAGS_SET(u, NAMESPACE_FLAGS_ALL))
-                                result = "no";
-                        else {
-                                r = namespace_flags_to_string(u, &s);
-                                if (r < 0)
-                                        return r;
-
-                                result = strempty(s);
-                        }
-
-                        bus_print_property_value(name, expected_value, value, result);
-
-                } else if (streq(name, "MountFlags")) {
-                        const char *result;
-
-                        result = mount_propagation_flags_to_string(u);
-                        if (!result)
-                                return -EINVAL;
-
-                        bus_print_property_value(name, expected_value, value, result);
-
-                } else if (STR_IN_SET(name, "CapabilityBoundingSet", "AmbientCapabilities")) {
-                        _cleanup_free_ char *s = NULL;
-
-                        r = capability_set_to_string_alloc(u, &s);
-                        if (r < 0)
-                                return r;
-
-                        bus_print_property_value(name, expected_value, value, s);
-
-                } else if ((STR_IN_SET(name, "CPUWeight", "StartupCPUWeight", "IOWeight", "StartupIOWeight") && u == CGROUP_WEIGHT_INVALID) ||
-                           (STR_IN_SET(name, "CPUShares", "StartupCPUShares") && u == CGROUP_CPU_SHARES_INVALID) ||
-                           (STR_IN_SET(name, "BlockIOWeight", "StartupBlockIOWeight") && u == CGROUP_BLKIO_WEIGHT_INVALID) ||
-                           (STR_IN_SET(name, "MemoryCurrent", "TasksCurrent") && u == (uint64_t) -1) ||
-                           (endswith(name, "NSec") && u == (uint64_t) -1))
-
-                        bus_print_property_value(name, expected_value, value, "[not set]");
-
-                else if ((STR_IN_SET(name, "DefaultMemoryLow", "DefaultMemoryMin", "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit") && u == CGROUP_LIMIT_MAX) ||
-                         (STR_IN_SET(name, "TasksMax", "DefaultTasksMax") && u == (uint64_t) -1) ||
-                         (startswith(name, "Limit") && u == (uint64_t) -1) ||
-                         (startswith(name, "DefaultLimit") && u == (uint64_t) -1))
-
-                        bus_print_property_value(name, expected_value, value, "infinity");
-                else if (STR_IN_SET(name, "IPIngressBytes", "IPIngressPackets", "IPEgressBytes", "IPEgressPackets") && u == (uint64_t) -1)
-                        bus_print_property_value(name, expected_value, value, "[no data]");
-                else
-                        bus_print_property_valuef(name, expected_value, value, "%"PRIu64, u);
-
-                return 1;
-        }
-
-        case SD_BUS_TYPE_INT64: {
-                int64_t i;
-
-                r = sd_bus_message_read_basic(m, type, &i);
-                if (r < 0)
-                        return r;
-
-                bus_print_property_valuef(name, expected_value, value, "%"PRIi64, i);
-                return 1;
-        }
-
-        case SD_BUS_TYPE_UINT32: {
-                uint32_t u;
-
-                r = sd_bus_message_read_basic(m, type, &u);
-                if (r < 0)
-                        return r;
-
-                if (strstr(name, "UMask") || strstr(name, "Mode"))
-                        bus_print_property_valuef(name, expected_value, value, "%04o", u);
-
-                else if (streq(name, "UID")) {
-                        if (u == UID_INVALID)
-                                bus_print_property_value(name, expected_value, value, "[not set]");
-                        else
-                                bus_print_property_valuef(name, expected_value, value, "%"PRIu32, u);
-                } else if (streq(name, "GID")) {
-                        if (u == GID_INVALID)
-                                bus_print_property_value(name, expected_value, value, "[not set]");
-                        else
-                                bus_print_property_valuef(name, expected_value, value, "%"PRIu32, u);
-                } else
-                        bus_print_property_valuef(name, expected_value, value, "%"PRIu32, u);
-
-                return 1;
-        }
-
-        case SD_BUS_TYPE_INT32: {
-                int32_t i;
-
-                r = sd_bus_message_read_basic(m, type, &i);
-                if (r < 0)
-                        return r;
-
-                bus_print_property_valuef(name, expected_value, value, "%"PRIi32, i);
-                return 1;
-        }
-
-        case SD_BUS_TYPE_DOUBLE: {
-                double d;
-
-                r = sd_bus_message_read_basic(m, type, &d);
-                if (r < 0)
-                        return r;
-
-                bus_print_property_valuef(name, expected_value, value, "%g", d);
-                return 1;
-        }
-
-        case SD_BUS_TYPE_ARRAY:
-                if (streq(contents, "s")) {
-                        bool first = true;
-                        const char *str;
-
-                        r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, contents);
-                        if (r < 0)
-                                return r;
-
-                        while ((r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &str)) > 0) {
-                                bool good;
-
-                                if (first && !value)
-                                        printf("%s=", name);
-
-                                /* This property has multiple space-separated values, so
-                                 * neither spaces nor newlines can be allowed in a value. */
-                                good = str[strcspn(str, " \n")] == '\0';
-
-                                printf("%s%s", first ? "" : " ", good ? str : "[unprintable]");
-
-                                first = false;
-                        }
-                        if (r < 0)
-                                return r;
-
-                        if (first && all && !value)
-                                printf("%s=", name);
-                        if (!first || all)
-                                puts("");
-
-                        r = sd_bus_message_exit_container(m);
-                        if (r < 0)
-                                return r;
-
-                        return 1;
-
-                } else if (streq(contents, "y")) {
-                        const uint8_t *u;
-                        size_t n;
-
-                        r = sd_bus_message_read_array(m, SD_BUS_TYPE_BYTE, (const void**) &u, &n);
-                        if (r < 0)
-                                return r;
-
-                        if (all || n > 0) {
-                                unsigned i;
-
-                                if (!value)
-                                        printf("%s=", name);
-
-                                for (i = 0; i < n; i++)
-                                        printf("%02x", u[i]);
-
-                                puts("");
-                        }
-
-                        return 1;
-
-                } else if (streq(contents, "u")) {
-                        uint32_t *u;
-                        size_t n;
-
-                        r = sd_bus_message_read_array(m, SD_BUS_TYPE_UINT32, (const void**) &u, &n);
-                        if (r < 0)
-                                return r;
-
-                        if (all || n > 0) {
-                                unsigned i;
-
-                                if (!value)
-                                        printf("%s=", name);
-
-                                for (i = 0; i < n; i++)
-                                        printf("%08x", u[i]);
-
-                                puts("");
-                        }
-
-                        return 1;
-                }
-
-                break;
-        }
-
-        return 0;
-}
-
-int bus_message_print_all_properties(
-                sd_bus_message *m,
-                bus_message_print_t func,
-                char **filter,
-                bool value,
-                bool all,
-                Set **found_properties) {
-
-        int r;
-
-        assert(m);
-
-        r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
-        if (r < 0)
-                return r;
-
-        while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
-                _cleanup_free_ char *name_with_equal = NULL;
-                const char *name, *contents, *expected_value = NULL;
-
-                r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &name);
-                if (r < 0)
-                        return r;
-
-                if (found_properties) {
-                        r = set_ensure_allocated(found_properties, &string_hash_ops);
-                        if (r < 0)
-                                return log_oom();
-
-                        r = set_put(*found_properties, name);
-                        if (r < 0 && r != -EEXIST)
-                                return log_oom();
-                }
-
-                name_with_equal = strjoin(name, "=");
-                if (!name_with_equal)
-                        return log_oom();
-
-                if (!filter || strv_find(filter, name) ||
-                    (expected_value = strv_find_startswith(filter, name_with_equal))) {
-                        r = sd_bus_message_peek_type(m, NULL, &contents);
-                        if (r < 0)
-                                return r;
-
-                        r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents);
-                        if (r < 0)
-                                return r;
-
-                        if (func)
-                                r = func(name, expected_value, m, value, all);
-                        if (!func || r == 0)
-                                r = bus_print_property(name, expected_value, m, value, all);
-                        if (r < 0)
-                                return r;
-                        if (r == 0) {
-                                if (all && !expected_value)
-                                        printf("%s=[unprintable]\n", name);
-                                /* skip what we didn't read */
-                                r = sd_bus_message_skip(m, contents);
-                                if (r < 0)
-                                        return r;
-                        }
-
-                        r = sd_bus_message_exit_container(m);
-                        if (r < 0)
-                                return r;
-                } else {
-                        r = sd_bus_message_skip(m, "v");
-                        if (r < 0)
-                                return r;
-                }
-
-                r = sd_bus_message_exit_container(m);
-                if (r < 0)
-                        return r;
-        }
-        if (r < 0)
-                return r;
-
-        r = sd_bus_message_exit_container(m);
-        if (r < 0)
-                return r;
-
-        return 0;
-}
-
-int bus_print_all_properties(
-                sd_bus *bus,
-                const char *dest,
-                const char *path,
-                bus_message_print_t func,
-                char **filter,
-                bool value,
-                bool all,
-                Set **found_properties) {
-
-        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
-        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
-        int r;
-
-        assert(bus);
-        assert(path);
-
-        r = sd_bus_call_method(bus,
-                        dest,
-                        path,
-                        "org.freedesktop.DBus.Properties",
-                        "GetAll",
-                        &error,
-                        &reply,
-                        "s", "");
-        if (r < 0)
-                return r;
-
-        return bus_message_print_all_properties(reply, func, filter, value, all, found_properties);
-}
-
-int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
-        sd_id128_t *p = userdata;
-        const void *v;
-        size_t n;
-        int r;
-
-        r = sd_bus_message_read_array(m, SD_BUS_TYPE_BYTE, &v, &n);
-        if (r < 0)
-                return r;
-
-        if (n == 0)
-                *p = SD_ID128_NULL;
-        else if (n == 16)
-                memcpy((*p).bytes, v, n);
-        else
-                return -EINVAL;
-
-        return 0;
-}
-
-static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, unsigned flags, sd_bus_error *error, void *userdata) {
-        char type;
-        int r;
-
-        r = sd_bus_message_peek_type(m, &type, NULL);
-        if (r < 0)
-                return r;
-
-        switch (type) {
-
-        case SD_BUS_TYPE_STRING:
-        case SD_BUS_TYPE_OBJECT_PATH: {
-                const char **p = userdata;
-                const char *s;
-
-                r = sd_bus_message_read_basic(m, type, &s);
-                if (r < 0)
-                        return r;
-
-                if (isempty(s))
-                        s = NULL;
-
-                if (flags & BUS_MAP_STRDUP)
-                        return free_and_strdup((char **) userdata, s);
-
-                *p = s;
-                return 0;
-        }
-
-        case SD_BUS_TYPE_ARRAY: {
-                _cleanup_strv_free_ char **l = NULL;
-                char ***p = userdata;
-
-                r = bus_message_read_strv_extend(m, &l);
-                if (r < 0)
-                        return r;
-
-                return strv_extend_strv(p, l, false);
-        }
-
-        case SD_BUS_TYPE_BOOLEAN: {
-                int b;
-
-                r = sd_bus_message_read_basic(m, type, &b);
-                if (r < 0)
-                        return r;
-
-                if (flags & BUS_MAP_BOOLEAN_AS_BOOL)
-                        *(bool*) userdata = b;
-                else
-                        *(int*) userdata = b;
-
-                return 0;
-        }
-
-        case SD_BUS_TYPE_INT32:
-        case SD_BUS_TYPE_UINT32: {
-                uint32_t u, *p = userdata;
-
-                r = sd_bus_message_read_basic(m, type, &u);
-                if (r < 0)
-                        return r;
-
-                *p = u;
-                return 0;
-        }
-
-        case SD_BUS_TYPE_INT64:
-        case SD_BUS_TYPE_UINT64: {
-                uint64_t t, *p = userdata;
-
-                r = sd_bus_message_read_basic(m, type, &t);
-                if (r < 0)
-                        return r;
-
-                *p = t;
-                return 0;
-        }
-
-        case SD_BUS_TYPE_DOUBLE: {
-                double d, *p = userdata;
-
-                r = sd_bus_message_read_basic(m, type, &d);
-                if (r < 0)
-                        return r;
-
-                *p = d;
-                return 0;
-        }}
-
-        return -EOPNOTSUPP;
-}
-
-int bus_message_map_all_properties(
-                sd_bus_message *m,
-                const struct bus_properties_map *map,
-                unsigned flags,
-                sd_bus_error *error,
-                void *userdata) {
-
-        int r;
-
-        assert(m);
-        assert(map);
-
-        r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
-        if (r < 0)
-                return r;
-
-        while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
-                const struct bus_properties_map *prop;
-                const char *member;
-                const char *contents;
-                void *v;
-                unsigned i;
-
-                r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &member);
-                if (r < 0)
-                        return r;
-
-                for (i = 0, prop = NULL; map[i].member; i++)
-                        if (streq(map[i].member, member)) {
-                                prop = &map[i];
-                                break;
-                        }
-
-                if (prop) {
-                        r = sd_bus_message_peek_type(m, NULL, &contents);
-                        if (r < 0)
-                                return r;
-
-                        r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents);
-                        if (r < 0)
-                                return r;
-
-                        v = (uint8_t *)userdata + prop->offset;
-                        if (map[i].set)
-                                r = prop->set(sd_bus_message_get_bus(m), member, m, error, v);
-                        else
-                                r = map_basic(sd_bus_message_get_bus(m), member, m, flags, error, v);
-                        if (r < 0)
-                                return r;
-
-                        r = sd_bus_message_exit_container(m);
-                        if (r < 0)
-                                return r;
-                } else {
-                        r = sd_bus_message_skip(m, "v");
-                        if (r < 0)
-                                return r;
-                }
-
-                r = sd_bus_message_exit_container(m);
-                if (r < 0)
-                        return r;
-        }
-        if (r < 0)
-                return r;
-
-        return sd_bus_message_exit_container(m);
-}
-
-int bus_map_all_properties(
-                sd_bus *bus,
-                const char *destination,
-                const char *path,
-                const struct bus_properties_map *map,
-                unsigned flags,
-                sd_bus_error *error,
-                sd_bus_message **reply,
-                void *userdata) {
-
-        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
-        int r;
-
-        assert(bus);
-        assert(destination);
-        assert(path);
-        assert(map);
-        assert(reply || (flags & BUS_MAP_STRDUP));
-
-        r = sd_bus_call_method(
-                        bus,
-                        destination,
-                        path,
-                        "org.freedesktop.DBus.Properties",
-                        "GetAll",
-                        error,
-                        &m,
-                        "s", "");
-        if (r < 0)
-                return r;
-
-        r = bus_message_map_all_properties(m, map, flags, error, userdata);
-        if (r < 0)
-                return r;
-
-        if (reply)
-                *reply = sd_bus_message_ref(m);
-
-        return r;
-}
-
 int bus_connect_transport(BusTransport transport, const char *host, bool user, sd_bus **ret) {
         _cleanup_(sd_bus_close_unrefp) sd_bus *bus = NULL;
         int r;
@@ -1027,102 +332,6 @@ int bus_connect_transport_systemd(BusTransport transport, const char *host, bool
         return r;
 }
 
-int bus_property_get_bool(
-                sd_bus *bus,
-                const char *path,
-                const char *interface,
-                const char *property,
-                sd_bus_message *reply,
-                void *userdata,
-                sd_bus_error *error) {
-
-        int b = *(bool*) userdata;
-
-        return sd_bus_message_append_basic(reply, 'b', &b);
-}
-
-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,
-                const char *interface,
-                const char *property,
-                sd_bus_message *reply,
-                void *userdata,
-                sd_bus_error *error) {
-
-        sd_id128_t *id = userdata;
-
-        if (sd_id128_is_null(*id)) /* Add an empty array if the ID is zero */
-                return sd_bus_message_append(reply, "ay", 0);
-        else
-                return sd_bus_message_append_array(reply, 'y', id->bytes, 16);
-}
-
-#if __SIZEOF_SIZE_T__ != 8
-int bus_property_get_size(
-                sd_bus *bus,
-                const char *path,
-                const char *interface,
-                const char *property,
-                sd_bus_message *reply,
-                void *userdata,
-                sd_bus_error *error) {
-
-        uint64_t sz = *(size_t*) userdata;
-
-        return sd_bus_message_append_basic(reply, 't', &sz);
-}
-#endif
-
-#if __SIZEOF_LONG__ != 8
-int bus_property_get_long(
-                sd_bus *bus,
-                const char *path,
-                const char *interface,
-                const char *property,
-                sd_bus_message *reply,
-                void *userdata,
-                sd_bus_error *error) {
-
-        int64_t l = *(long*) userdata;
-
-        return sd_bus_message_append_basic(reply, 'x', &l);
-}
-
-int bus_property_get_ulong(
-                sd_bus *bus,
-                const char *path,
-                const char *interface,
-                const char *property,
-                sd_bus_message *reply,
-                void *userdata,
-                sd_bus_error *error) {
-
-        uint64_t ul = *(unsigned long*) userdata;
-
-        return sd_bus_message_append_basic(reply, 't', &ul);
-}
-#endif
-
 /**
  * bus_path_encode_unique() - encode unique object path
  * @b: bus connection or NULL
@@ -1166,7 +375,7 @@ int bus_path_encode_unique(sd_bus *b, const char *prefix, const char *sender_id,
         int r;
 
         assert_return(b || (sender_id && external_id), -EINVAL);
-        assert_return(object_path_is_valid(prefix), -EINVAL);
+        assert_return(sd_bus_object_path_is_valid(prefix), -EINVAL);
         assert_return(ret_path, -EINVAL);
 
         if (!sender_id) {
@@ -1218,8 +427,8 @@ int bus_path_decode_unique(const char *path, const char *prefix, char **ret_send
         const char *p, *q;
         char *sender, *external;
 
-        assert(object_path_is_valid(path));
-        assert(object_path_is_valid(prefix));
+        assert(sd_bus_object_path_is_valid(path));
+        assert(sd_bus_object_path_is_valid(prefix));
         assert(ret_sender);
         assert(ret_external);
 
@@ -1250,54 +459,6 @@ int bus_path_decode_unique(const char *path, const char *prefix, char **ret_send
         return 1;
 }
 
-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) {
-
-        const char *is_soft;
-        struct rlimit *rl;
-        uint64_t u;
-        rlim_t x;
-
-        assert(bus);
-        assert(reply);
-        assert(userdata);
-
-        is_soft = endswith(property, "Soft");
-
-        rl = *(struct rlimit**) userdata;
-        if (rl)
-                x = is_soft ? rl->rlim_cur : rl->rlim_max;
-        else {
-                struct rlimit buf = {};
-                const char *s, *p;
-                int z;
-
-                /* Chop off "Soft" suffix */
-                s = is_soft ? strndupa(property, is_soft - property) : property;
-
-                /* Skip over any prefix, such as "Default" */
-                assert_se(p = strstr(s, "Limit"));
-
-                z = rlimit_from_string(p + 5);
-                assert(z >= 0);
-
-                (void) getrlimit(z, &buf);
-                x = is_soft ? buf.rlim_cur : buf.rlim_max;
-        }
-
-        /* rlim_t might have different sizes, let's map RLIMIT_INFINITY to (uint64_t) -1, so that it is the same on all
-         * archs */
-        u = x == RLIM_INFINITY ? (uint64_t) -1 : (uint64_t) x;
-
-        return sd_bus_message_append(reply, "t", u);
-}
-
 int bus_track_add_name_many(sd_bus_track *t, char **l) {
         int r = 0;
         char **i;
index db245a791ea4fa4c21450e0394f41993e5f38dde..d98e0040f3b682bddac13410c5ba2fac12047eb2 100644 (file)
@@ -10,7 +10,6 @@
 #include "sd-event.h"
 
 #include "macro.h"
-#include "set.h"
 #include "string-util.h"
 #include "time-util.h"
 
@@ -22,26 +21,6 @@ typedef enum BusTransport {
         _BUS_TRANSPORT_INVALID = -1
 } BusTransport;
 
-typedef int (*bus_property_set_t) (sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata);
-
-struct bus_properties_map {
-        const char *member;
-        const char *signature;
-        bus_property_set_t set;
-        size_t offset;
-};
-
-enum {
-        BUS_MAP_STRDUP          = 1 << 0, /* If set, each "s" message is duplicated. Thus, each pointer needs to be freed. */
-        BUS_MAP_BOOLEAN_AS_BOOL = 1 << 1, /* If set, each "b" message is written to a bool pointer. If not set, "b" is written to a int pointer. */
-};
-
-int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata);
-
-int bus_message_map_all_properties(sd_bus_message *m, const struct bus_properties_map *map, unsigned flags, sd_bus_error *error, void *userdata);
-int bus_map_all_properties(sd_bus *bus, const char *destination, const char *path, const struct bus_properties_map *map,
-                           unsigned flags, sd_bus_error *error, sd_bus_message **reply, void *userdata);
-
 int bus_async_unregister_and_exit(sd_event *e, sd_bus *bus, const char *name);
 
 typedef bool (*check_idle_t)(void *userdata);
@@ -58,56 +37,8 @@ int bus_connect_user_systemd(sd_bus **_bus);
 int bus_connect_transport(BusTransport transport, const char *host, bool user, sd_bus **bus);
 int bus_connect_transport_systemd(BusTransport transport, const char *host, bool user, sd_bus **bus);
 
-typedef int (*bus_message_print_t) (const char *name, const char *expected_value, sd_bus_message *m, bool value, bool all);
-
-int bus_print_property_value(const char *name, const char *expected_value, bool only_value, const char *value);
-int bus_print_property_valuef(const char *name, const char *expected_value, bool only_value, const char *fmt, ...) _printf_(4,5);
-int bus_message_print_all_properties(sd_bus_message *m, bus_message_print_t func, char **filter, bool value, bool all, Set **found_properties);
-int bus_print_all_properties(sd_bus *bus, const char *dest, const char *path, bus_message_print_t func, char **filter, bool value, bool all, Set **found_properties);
-
-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)
-#define bus_property_set_usec ((sd_bus_property_set_t) NULL)
-
-assert_cc(sizeof(int) == sizeof(int32_t));
-#define bus_property_get_int ((sd_bus_property_get_t) NULL)
-
-assert_cc(sizeof(unsigned) == sizeof(uint32_t));
-#define bus_property_get_unsigned ((sd_bus_property_get_t) NULL)
-
-/* On 64bit machines we can use the default serializer for size_t and
- * friends, otherwise we need to cast this manually */
-#if __SIZEOF_SIZE_T__ == 8
-#define bus_property_get_size ((sd_bus_property_get_t) NULL)
-#else
-int bus_property_get_size(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
-#endif
-
-#if __SIZEOF_LONG__ == 8
-#define bus_property_get_long ((sd_bus_property_get_t) NULL)
-#define bus_property_get_ulong ((sd_bus_property_get_t) NULL)
-#else
-int bus_property_get_long(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
-int bus_property_get_ulong(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
-#endif
-
-/* uid_t and friends on Linux 32 bit. This means we can just use the
- * default serializer for 32bit unsigned, for serializing it, and map
- * it to NULL here */
-assert_cc(sizeof(uid_t) == sizeof(uint32_t));
-#define bus_property_get_uid ((sd_bus_property_get_t) NULL)
-
-assert_cc(sizeof(gid_t) == sizeof(uint32_t));
-#define bus_property_get_gid ((sd_bus_property_get_t) NULL)
-
-assert_cc(sizeof(pid_t) == sizeof(uint32_t));
-#define bus_property_get_pid ((sd_bus_property_get_t) NULL)
-
-assert_cc(sizeof(mode_t) == sizeof(uint32_t));
-#define bus_property_get_mode ((sd_bus_property_get_t) NULL)
+#define bus_log_connect_error(r) \
+        log_error_errno(r, "Failed to create bus connection: %m")
 
 #define bus_log_parse_error(r) \
         log_error_errno(r, "Failed to parse bus message: %m")
@@ -115,60 +46,9 @@ assert_cc(sizeof(mode_t) == sizeof(uint32_t));
 #define bus_log_create_error(r) \
         log_error_errno(r, "Failed to create bus message: %m")
 
-#define BUS_DEFINE_PROPERTY_GET_GLOBAL(function, bus_type, val)         \
-        int function(sd_bus *bus,                                       \
-                     const char *path,                                  \
-                     const char *interface,                             \
-                     const char *property,                              \
-                     sd_bus_message *reply,                             \
-                     void *userdata,                                    \
-                     sd_bus_error *error) {                             \
-                                                                        \
-                assert(bus);                                            \
-                assert(reply);                                          \
-                                                                        \
-                return sd_bus_message_append(reply, bus_type, val);     \
-        }
-
-#define BUS_DEFINE_PROPERTY_GET2(function, bus_type, data_type, get1, get2) \
-        int function(sd_bus *bus,                                       \
-                     const char *path,                                  \
-                     const char *interface,                             \
-                     const char *property,                              \
-                     sd_bus_message *reply,                             \
-                     void *userdata,                                    \
-                     sd_bus_error *error) {                             \
-                                                                        \
-                data_type *data = userdata;                             \
-                                                                        \
-                assert(bus);                                            \
-                assert(reply);                                          \
-                assert(data);                                           \
-                                                                        \
-                return sd_bus_message_append(reply, bus_type,           \
-                                             get2(get1(data)));         \
-        }
-
-#define ident(x) (x)
-#define BUS_DEFINE_PROPERTY_GET(function, bus_type, data_type, get1) \
-        BUS_DEFINE_PROPERTY_GET2(function, bus_type, data_type, get1, ident)
-
-#define ref(x) (*(x))
-#define BUS_DEFINE_PROPERTY_GET_REF(function, bus_type, data_type, get) \
-        BUS_DEFINE_PROPERTY_GET2(function, bus_type, data_type, ref, get)
-
-#define BUS_DEFINE_PROPERTY_GET_ENUM(function, name, type)              \
-        BUS_DEFINE_PROPERTY_GET_REF(function, "s", type, name##_to_string)
-
-#define BUS_PROPERTY_DUAL_TIMESTAMP(name, offset, flags) \
-        SD_BUS_PROPERTY(name, "t", bus_property_get_usec, (offset) + offsetof(struct dual_timestamp, realtime), (flags)), \
-        SD_BUS_PROPERTY(name "Monotonic", "t", bus_property_get_usec, (offset) + offsetof(struct dual_timestamp, monotonic), (flags))
-
 int bus_path_encode_unique(sd_bus *b, const char *prefix, const char *sender_id, const char *external_id, char **ret_path);
 int bus_path_decode_unique(const char *path, const char *prefix, char **ret_sender, char **ret_external);
 
-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_with_description(sd_bus **ret, const char *description);
index 4e6b862d5efef71219552d6eaf1bcf262ee21e96..eb33ba23400d11d639d713c9c7f82c5ce102eeb3 100644 (file)
@@ -65,7 +65,7 @@ void bus_wait_for_jobs_free(BusWaitForJobs *d) {
         if (!d)
                 return;
 
-        set_free_free(d->jobs);
+        set_free(d->jobs);
 
         sd_bus_slot_unref(d->slot_disconnected);
         sd_bus_slot_unref(d->slot_job_removed);
@@ -315,15 +315,9 @@ int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_ar
 }
 
 int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path) {
-        int r;
-
         assert(d);
 
-        r = set_ensure_allocated(&d->jobs, &string_hash_ops);
-        if (r < 0)
-                return r;
-
-        return set_put_strdup(d->jobs, path);
+        return set_put_strdup(&d->jobs, path);
 }
 
 int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet) {
index 3ee3c0ccba659586b746f02f24a8dc6b98573688..7592dcf18d2d29f0463990502820e45f095e9b66 100644 (file)
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
-#include "bus-util.h"
+#include "bus-map-properties.h"
 #include "bus-wait-for-units.h"
 #include "hashmap.h"
 #include "string-util.h"
index 7dd87afd0ac277a235fae1e459cfa11e88b250ba..db6a103c42a74ed382359a1e32fba5795efcb63c 100644 (file)
@@ -17,6 +17,7 @@
 #include "process-util.h"
 #include "sort-util.h"
 #include "string-util.h"
+#include "strv.h"
 #include "time-util.h"
 
 #define BITS_WEEKDAYS 127
@@ -29,6 +30,9 @@
  * linked compenents anyway. */
 #define CALENDARSPEC_COMPONENTS_MAX 240
 
+/* Let's make sure that the microsecond component is safe to be stored in an 'int' */
+assert_cc(INT_MAX >= USEC_PER_SEC);
+
 static void chain_free(CalendarComponent *c) {
         CalendarComponent *n;
 
@@ -171,7 +175,7 @@ int calendar_spec_normalize(CalendarSpec *c) {
         return 0;
 }
 
-_pure_ static bool chain_valid(CalendarComponent *c, int from, int to, bool end_of_month) {
+static bool chain_valid(CalendarComponent *c, int from, int to, bool end_of_month) {
         assert(to >= from);
 
         if (!c)
@@ -980,9 +984,10 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
                 if (r < 0)
                         return r;
 
-        } else if (strcaseeq(p, "annually") ||
-                   strcaseeq(p, "yearly") ||
-                   strcaseeq(p, "anually") /* backwards compatibility */ ) {
+        } else if (STRCASE_IN_SET(p,
+                                  "annually",
+                                  "yearly",
+                                  "anually") /* backwards compatibility */ ) {
 
                 r = const_chain(1, &c->month);
                 if (r < 0)
@@ -1041,10 +1046,11 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
                 if (r < 0)
                         return r;
 
-        } else if (strcaseeq(p, "biannually") ||
-                   strcaseeq(p, "bi-annually") ||
-                   strcaseeq(p, "semiannually") ||
-                   strcaseeq(p, "semi-annually")) {
+        } else if (STRCASE_IN_SET(p,
+                                  "biannually",
+                                  "bi-annually",
+                                  "semiannually",
+                                  "semi-annually")) {
 
                 r = const_chain(1, &c->month);
                 if (r < 0)
index 3bf8a39e1a5afd615f885f078fea06f4b437c732..0a5d95b4b1299d36a56d6297a37612081725ed98 100644 (file)
@@ -19,9 +19,9 @@ typedef struct CalendarComponent {
 
 typedef struct CalendarSpec {
         int weekdays_bits;
-        bool end_of_month;
-        bool utc;
-        int dst;
+        bool end_of_month:1;
+        bool utc:1;
+        signed int dst:2;
         char *timezone;
 
         CalendarComponent *year;
index 208d27df1a3a2fb527ec4e9724014429b9f01707..b8bf3c27264295ac2142e072fec4f5daa126f1bf 100644 (file)
@@ -371,7 +371,7 @@ int show_cgroup_get_path_and_warn(
 
                 r = bus_connect_transport_systemd(BUS_TRANSPORT_LOCAL, NULL, false, &bus);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to create bus connection: %m");
+                        return bus_log_connect_error(r);
 
                 r = show_cgroup_get_unit_path_and_warn(bus, unit, &root);
                 if (r < 0)
index 1aebac307ac3042c96736398e06c4bbd81cb772a..636c0e2a7fdbf9a1ecb0d86639dba669e533cd84 100644 (file)
@@ -150,7 +150,7 @@ int fd_chown_recursive(
         struct stat st;
 
         /* Note that the slightly different order of fstat() and the checks here and in
-         * path_chown_recursive(). That's because when we open the dirctory ourselves we can specify
+         * path_chown_recursive(). That's because when we open the directory ourselves we can specify
          * O_DIRECTORY and we always want to ensure we are operating on a directory before deciding whether
          * the operation is otherwise redundant. */
 
index 9f4c7fe3380f7639644dfc541d7c61840cd99505..bf3b5fa1622800a26edfedaf75720bb03d61600c 100644 (file)
@@ -25,6 +25,7 @@
 #include "extract-word.h"
 #include "fd-util.h"
 #include "fileio.h"
+#include "fs-util.h"
 #include "glob-util.h"
 #include "hostname-util.h"
 #include "ima-util.h"
@@ -72,11 +73,11 @@ Condition* condition_new(ConditionType type, const char *parameter, bool trigger
         return c;
 }
 
-void condition_free(Condition *c) {
+Condition* condition_free(Condition *c) {
         assert(c);
 
         free(c->parameter);
-        free(c);
+        return mfree(c);
 }
 
 Condition* condition_free_list_type(Condition *head, ConditionType type) {
@@ -92,7 +93,7 @@ Condition* condition_free_list_type(Condition *head, ConditionType type) {
         return head;
 }
 
-static int condition_test_kernel_command_line(Condition *c) {
+static int condition_test_kernel_command_line(Condition *c, char **env) {
         _cleanup_free_ char *line = NULL;
         const char *p;
         bool equal;
@@ -201,7 +202,7 @@ static bool test_order(int k, OrderOperator p) {
         }
 }
 
-static int condition_test_kernel_version(Condition *c) {
+static int condition_test_kernel_version(Condition *c, char **env) {
         OrderOperator order;
         struct utsname u;
         const char *p;
@@ -259,7 +260,7 @@ static int condition_test_kernel_version(Condition *c) {
         return true;
 }
 
-static int condition_test_memory(Condition *c) {
+static int condition_test_memory(Condition *c, char **env) {
         OrderOperator order;
         uint64_t m, k;
         const char *p;
@@ -283,7 +284,7 @@ static int condition_test_memory(Condition *c) {
         return test_order(CMP(m, k), order);
 }
 
-static int condition_test_cpus(Condition *c) {
+static int condition_test_cpus(Condition *c, char **env) {
         OrderOperator order;
         const char *p;
         unsigned k;
@@ -309,7 +310,7 @@ static int condition_test_cpus(Condition *c) {
         return test_order(CMP((unsigned) n, k), order);
 }
 
-static int condition_test_user(Condition *c) {
+static int condition_test_user(Condition *c, char **env) {
         uid_t id;
         int r;
         _cleanup_free_ char *username = NULL;
@@ -344,7 +345,7 @@ static int condition_test_user(Condition *c) {
         return id == getuid() || id == geteuid();
 }
 
-static int condition_test_control_group_controller(Condition *c) {
+static int condition_test_control_group_controller(Condition *c, char **env) {
         int r;
         CGroupMask system_mask, wanted_mask = 0;
 
@@ -368,7 +369,7 @@ static int condition_test_control_group_controller(Condition *c) {
         return FLAGS_SET(system_mask, wanted_mask);
 }
 
-static int condition_test_group(Condition *c) {
+static int condition_test_group(Condition *c, char **env) {
         gid_t id;
         int r;
 
@@ -387,7 +388,7 @@ static int condition_test_group(Condition *c) {
         return in_group(c->parameter) > 0;
 }
 
-static int condition_test_virtualization(Condition *c) {
+static int condition_test_virtualization(Condition *c, char **env) {
         int b, v;
 
         assert(c);
@@ -417,7 +418,7 @@ static int condition_test_virtualization(Condition *c) {
         return v != VIRTUALIZATION_NONE && streq(c->parameter, virtualization_to_string(v));
 }
 
-static int condition_test_architecture(Condition *c) {
+static int condition_test_architecture(Condition *c, char **env) {
         int a, b;
 
         assert(c);
@@ -439,7 +440,7 @@ static int condition_test_architecture(Condition *c) {
         return a == b;
 }
 
-static int condition_test_host(Condition *c) {
+static int condition_test_host(Condition *c, char **env) {
         _cleanup_free_ char *h = NULL;
         sd_id128_t x, y;
         int r;
@@ -464,7 +465,7 @@ static int condition_test_host(Condition *c) {
         return fnmatch(c->parameter, h, FNM_CASEFOLD) == 0;
 }
 
-static int condition_test_ac_power(Condition *c) {
+static int condition_test_ac_power(Condition *c, char **env) {
         int r;
 
         assert(c);
@@ -478,7 +479,7 @@ static int condition_test_ac_power(Condition *c) {
         return (on_ac_power() != 0) == !!r;
 }
 
-static int condition_test_security(Condition *c) {
+static int condition_test_security(Condition *c, char **env) {
         assert(c);
         assert(c->parameter);
         assert(c->type == CONDITION_SECURITY);
@@ -501,7 +502,7 @@ static int condition_test_security(Condition *c) {
         return false;
 }
 
-static int condition_test_capability(Condition *c) {
+static int condition_test_capability(Condition *c, char **env) {
         unsigned long long capabilities = (unsigned long long) -1;
         _cleanup_fclose_ FILE *f = NULL;
         int value, r;
@@ -544,31 +545,48 @@ static int condition_test_capability(Condition *c) {
         return !!(capabilities & (1ULL << value));
 }
 
-static int condition_test_needs_update(Condition *c) {
-        const char *p;
+static int condition_test_needs_update(Condition *c, char **env) {
         struct stat usr, other;
+        const char *p;
+        bool b;
+        int r;
 
         assert(c);
         assert(c->parameter);
         assert(c->type == CONDITION_NEEDS_UPDATE);
 
+        r = proc_cmdline_get_bool("systemd.condition-needs-update", &b);
+        if (r < 0)
+                log_debug_errno(r, "Failed to parse systemd.condition-needs-update= kernel command line argument, ignoring: %m");
+        if (r > 0)
+                return b;
+
+        if (!path_is_absolute(c->parameter)) {
+                log_debug("Specified condition parameter '%s' is not absolute, assuming an update is needed.", c->parameter);
+                return true;
+        }
+
         /* If the file system is read-only we shouldn't suggest an update */
-        if (path_is_read_only_fs(c->parameter) > 0)
+        r = path_is_read_only_fs(c->parameter);
+        if (r < 0)
+                log_debug_errno(r, "Failed to determine if '%s' is read-only, ignoring: %m", c->parameter);
+        if (r > 0)
                 return false;
 
-        /* Any other failure means we should allow the condition to be true,
-         * so that we rather invoke too many update tools than too
-         * few. */
-
-        if (!path_is_absolute(c->parameter))
-                return true;
+        /* Any other failure means we should allow the condition to be true, so that we rather invoke too
+         * many update tools than too few. */
 
         p = strjoina(c->parameter, "/.updated");
-        if (lstat(p, &other) < 0)
+        if (lstat(p, &other) < 0) {
+                if (errno != ENOENT)
+                        log_debug_errno(errno, "Failed to stat() '%s', assuming an update is needed: %m", p);
                 return true;
+        }
 
-        if (lstat("/usr/", &usr) < 0)
+        if (lstat("/usr/", &usr) < 0) {
+                log_debug_errno(errno, "Failed to stat() /usr/, assuming an update is needed: %m");
                 return true;
+        }
 
         /*
          * First, compare seconds as they are always accurate...
@@ -584,47 +602,84 @@ static int condition_test_needs_update(Condition *c) {
          * AND the target file's nanoseconds == 0
          * (otherwise the filesystem supports nsec timestamps, see stat(2)).
          */
-        if (usr.st_mtim.tv_nsec > 0 && other.st_mtim.tv_nsec == 0) {
-                _cleanup_free_ char *timestamp_str = NULL;
-                uint64_t timestamp;
-                int r;
+        if (usr.st_mtim.tv_nsec == 0 || other.st_mtim.tv_nsec > 0)
+                return usr.st_mtim.tv_nsec > other.st_mtim.tv_nsec;
 
-                r = parse_env_file(NULL, p, "TIMESTAMP_NSEC", &timestamp_str);
-                if (r < 0) {
-                        log_error_errno(r, "Failed to parse timestamp file '%s', using mtime: %m", p);
-                        return true;
-                } else if (r == 0) {
-                        log_debug("No data in timestamp file '%s', using mtime", p);
-                        return true;
-                }
-
-                r = safe_atou64(timestamp_str, &timestamp);
-                if (r < 0) {
-                        log_error_errno(r, "Failed to parse timestamp value '%s' in file '%s', using mtime: %m", timestamp_str, p);
-                        return true;
-                }
+        _cleanup_free_ char *timestamp_str = NULL;
+        r = parse_env_file(NULL, p, "TIMESTAMP_NSEC", &timestamp_str);
+        if (r < 0) {
+                log_debug_errno(r, "Failed to parse timestamp file '%s', using mtime: %m", p);
+                return true;
+        } else if (r == 0) {
+                log_debug("No data in timestamp file '%s', using mtime.", p);
+                return true;
+        }
 
-                timespec_store(&other.st_mtim, timestamp);
+        uint64_t timestamp;
+        r = safe_atou64(timestamp_str, &timestamp);
+        if (r < 0) {
+                log_debug_errno(r, "Failed to parse timestamp value '%s' in file '%s', using mtime: %m", timestamp_str, p);
+                return true;
         }
 
-        return usr.st_mtim.tv_nsec > other.st_mtim.tv_nsec;
+        return timespec_load_nsec(&usr.st_mtim) > timestamp;
 }
 
-static int condition_test_first_boot(Condition *c) {
-        int r;
+static int condition_test_first_boot(Condition *c, char **env) {
+        int r, q;
+        bool b;
 
         assert(c);
         assert(c->parameter);
         assert(c->type == CONDITION_FIRST_BOOT);
 
+        r = proc_cmdline_get_bool("systemd.condition-first-boot", &b);
+        if (r < 0)
+                log_debug_errno(r, "Failed to parse systemd.condition-first-boot= kernel command line argument, ignoring: %m");
+        if (r > 0)
+                return b == !!r;
+
         r = parse_boolean(c->parameter);
         if (r < 0)
                 return r;
 
-        return (access("/run/systemd/first-boot", F_OK) >= 0) == !!r;
+        q = access("/run/systemd/first-boot", F_OK);
+        if (q < 0 && errno != ENOENT)
+                log_debug_errno(errno, "Failed to check if /run/systemd/first-boot exists, ignoring: %m");
+
+        return (q >= 0) == !!r;
+}
+
+static int condition_test_environment(Condition *c, char **env) {
+        bool equal;
+        char **i;
+
+        assert(c);
+        assert(c->parameter);
+        assert(c->type == CONDITION_ENVIRONMENT);
+
+        equal = strchr(c->parameter, '=');
+
+        STRV_FOREACH(i, env) {
+                bool found;
+
+                if (equal)
+                        found = streq(c->parameter, *i);
+                else {
+                        const char *f;
+
+                        f = startswith(*i, c->parameter);
+                        found = f && IN_SET(*f, 0, '=');
+                }
+
+                if (found)
+                        return true;
+        }
+
+        return false;
 }
 
-static int condition_test_path_exists(Condition *c) {
+static int condition_test_path_exists(Condition *c, char **env) {
         assert(c);
         assert(c->parameter);
         assert(c->type == CONDITION_PATH_EXISTS);
@@ -632,7 +687,7 @@ static int condition_test_path_exists(Condition *c) {
         return access(c->parameter, F_OK) >= 0;
 }
 
-static int condition_test_path_exists_glob(Condition *c) {
+static int condition_test_path_exists_glob(Condition *c, char **env) {
         assert(c);
         assert(c->parameter);
         assert(c->type == CONDITION_PATH_EXISTS_GLOB);
@@ -640,7 +695,7 @@ static int condition_test_path_exists_glob(Condition *c) {
         return glob_exists(c->parameter) > 0;
 }
 
-static int condition_test_path_is_directory(Condition *c) {
+static int condition_test_path_is_directory(Condition *c, char **env) {
         assert(c);
         assert(c->parameter);
         assert(c->type == CONDITION_PATH_IS_DIRECTORY);
@@ -648,7 +703,7 @@ static int condition_test_path_is_directory(Condition *c) {
         return is_dir(c->parameter, true) > 0;
 }
 
-static int condition_test_path_is_symbolic_link(Condition *c) {
+static int condition_test_path_is_symbolic_link(Condition *c, char **env) {
         assert(c);
         assert(c->parameter);
         assert(c->type == CONDITION_PATH_IS_SYMBOLIC_LINK);
@@ -656,7 +711,7 @@ static int condition_test_path_is_symbolic_link(Condition *c) {
         return is_symlink(c->parameter) > 0;
 }
 
-static int condition_test_path_is_mount_point(Condition *c) {
+static int condition_test_path_is_mount_point(Condition *c, char **env) {
         assert(c);
         assert(c->parameter);
         assert(c->type == CONDITION_PATH_IS_MOUNT_POINT);
@@ -664,7 +719,7 @@ static int condition_test_path_is_mount_point(Condition *c) {
         return path_is_mount_point(c->parameter, NULL, AT_SYMLINK_FOLLOW) > 0;
 }
 
-static int condition_test_path_is_read_write(Condition *c) {
+static int condition_test_path_is_read_write(Condition *c, char **env) {
         assert(c);
         assert(c->parameter);
         assert(c->type == CONDITION_PATH_IS_READ_WRITE);
@@ -672,7 +727,21 @@ static int condition_test_path_is_read_write(Condition *c) {
         return path_is_read_only_fs(c->parameter) <= 0;
 }
 
-static int condition_test_directory_not_empty(Condition *c) {
+static int condition_test_path_is_encrypted(Condition *c, char **env) {
+        int r;
+
+        assert(c);
+        assert(c->parameter);
+        assert(c->type == CONDITION_PATH_IS_ENCRYPTED);
+
+        r = path_is_encrypted(c->parameter);
+        if (r < 0 && r != -ENOENT)
+                log_debug_errno(r, "Failed to determine if '%s' is encrypted: %m", c->parameter);
+
+        return r > 0;
+}
+
+static int condition_test_directory_not_empty(Condition *c, char **env) {
         int r;
 
         assert(c);
@@ -683,7 +752,7 @@ static int condition_test_directory_not_empty(Condition *c) {
         return r <= 0 && r != -ENOENT;
 }
 
-static int condition_test_file_not_empty(Condition *c) {
+static int condition_test_file_not_empty(Condition *c, char **env) {
         struct stat st;
 
         assert(c);
@@ -695,7 +764,7 @@ static int condition_test_file_not_empty(Condition *c) {
                 st.st_size > 0);
 }
 
-static int condition_test_file_is_executable(Condition *c) {
+static int condition_test_file_is_executable(Condition *c, char **env) {
         struct stat st;
 
         assert(c);
@@ -707,7 +776,7 @@ static int condition_test_file_is_executable(Condition *c) {
                 (st.st_mode & 0111));
 }
 
-static int condition_test_null(Condition *c) {
+static int condition_test_null(Condition *c, char **env) {
         assert(c);
         assert(c->type == CONDITION_NULL);
 
@@ -716,15 +785,16 @@ static int condition_test_null(Condition *c) {
         return true;
 }
 
-int condition_test(Condition *c) {
+int condition_test(Condition *c, char **env) {
 
-        static int (*const condition_tests[_CONDITION_TYPE_MAX])(Condition *c) = {
+        static int (*const condition_tests[_CONDITION_TYPE_MAX])(Condition *c, char **env) = {
                 [CONDITION_PATH_EXISTS]              = condition_test_path_exists,
                 [CONDITION_PATH_EXISTS_GLOB]         = condition_test_path_exists_glob,
                 [CONDITION_PATH_IS_DIRECTORY]        = condition_test_path_is_directory,
                 [CONDITION_PATH_IS_SYMBOLIC_LINK]    = condition_test_path_is_symbolic_link,
                 [CONDITION_PATH_IS_MOUNT_POINT]      = condition_test_path_is_mount_point,
                 [CONDITION_PATH_IS_READ_WRITE]       = condition_test_path_is_read_write,
+                [CONDITION_PATH_IS_ENCRYPTED]        = condition_test_path_is_encrypted,
                 [CONDITION_DIRECTORY_NOT_EMPTY]      = condition_test_directory_not_empty,
                 [CONDITION_FILE_NOT_EMPTY]           = condition_test_file_not_empty,
                 [CONDITION_FILE_IS_EXECUTABLE]       = condition_test_file_is_executable,
@@ -744,6 +814,7 @@ int condition_test(Condition *c) {
                 [CONDITION_NULL]                     = condition_test_null,
                 [CONDITION_CPUS]                     = condition_test_cpus,
                 [CONDITION_MEMORY]                   = condition_test_memory,
+                [CONDITION_ENVIRONMENT]              = condition_test_environment,
         };
 
         int r, b;
@@ -752,7 +823,7 @@ int condition_test(Condition *c) {
         assert(c->type >= 0);
         assert(c->type < _CONDITION_TYPE_MAX);
 
-        r = condition_tests[c->type](c);
+        r = condition_tests[c->type](c, env);
         if (r < 0) {
                 c->result = CONDITION_ERROR;
                 return r;
@@ -763,7 +834,13 @@ int condition_test(Condition *c) {
         return b;
 }
 
-bool condition_test_list(Condition *first, const char *(*to_string)(ConditionType t), condition_test_logger_t logger, void *userdata) {
+bool condition_test_list(
+                Condition *first,
+                char **env,
+                condition_to_string_t to_string,
+                condition_test_logger_t logger,
+                void *userdata) {
+
         Condition *c;
         int triggered = -1;
 
@@ -779,7 +856,7 @@ bool condition_test_list(Condition *first, const char *(*to_string)(ConditionTyp
         LIST_FOREACH(conditions, c, first) {
                 int r;
 
-                r = condition_test(c);
+                r = condition_test(c, env);
 
                 if (logger) {
                         const char *p = c->type == CONDITION_NULL ? "true" : c->parameter;
@@ -812,9 +889,10 @@ bool condition_test_list(Condition *first, const char *(*to_string)(ConditionTyp
         return triggered != 0;
 }
 
-void condition_dump(Condition *c, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)) {
+void condition_dump(Condition *c, FILE *f, const char *prefix, condition_to_string_t to_string) {
         assert(c);
         assert(f);
+        assert(to_string);
 
         prefix = strempty(prefix);
 
@@ -828,7 +906,7 @@ void condition_dump(Condition *c, FILE *f, const char *prefix, const char *(*to_
                 condition_result_to_string(c->result));
 }
 
-void condition_dump_list(Condition *first, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)) {
+void condition_dump_list(Condition *first, FILE *f, const char *prefix, condition_to_string_t to_string) {
         Condition *c;
 
         LIST_FOREACH(conditions, c, first)
@@ -852,6 +930,7 @@ static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
         [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink",
         [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint",
         [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite",
+        [CONDITION_PATH_IS_ENCRYPTED] = "ConditionPathIsEncrypted",
         [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
         [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty",
         [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable",
@@ -861,6 +940,7 @@ static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
         [CONDITION_NULL] = "ConditionNull",
         [CONDITION_CPUS] = "ConditionCPUs",
         [CONDITION_MEMORY] = "ConditionMemory",
+        [CONDITION_ENVIRONMENT] = "ConditionEnvironment",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);
@@ -882,6 +962,7 @@ static const char* const assert_type_table[_CONDITION_TYPE_MAX] = {
         [CONDITION_PATH_IS_SYMBOLIC_LINK] = "AssertPathIsSymbolicLink",
         [CONDITION_PATH_IS_MOUNT_POINT] = "AssertPathIsMountPoint",
         [CONDITION_PATH_IS_READ_WRITE] = "AssertPathIsReadWrite",
+        [CONDITION_PATH_IS_ENCRYPTED] = "AssertPathIsEncrypted",
         [CONDITION_DIRECTORY_NOT_EMPTY] = "AssertDirectoryNotEmpty",
         [CONDITION_FILE_NOT_EMPTY] = "AssertFileNotEmpty",
         [CONDITION_FILE_IS_EXECUTABLE] = "AssertFileIsExecutable",
@@ -891,6 +972,7 @@ static const char* const assert_type_table[_CONDITION_TYPE_MAX] = {
         [CONDITION_NULL] = "AssertNull",
         [CONDITION_CPUS] = "AssertCPUs",
         [CONDITION_MEMORY] = "AssertMemory",
+        [CONDITION_ENVIRONMENT] = "AssertEnvironment",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(assert_type, ConditionType);
index 84322e74259a0cf1e2a80db64fd74af559383e8e..fea74d228d8d0210433c438f524fb1b50829293e 100644 (file)
@@ -18,6 +18,7 @@ typedef enum ConditionType {
         CONDITION_AC_POWER,
         CONDITION_MEMORY,
         CONDITION_CPUS,
+        CONDITION_ENVIRONMENT,
 
         CONDITION_NEEDS_UPDATE,
         CONDITION_FIRST_BOOT,
@@ -28,6 +29,7 @@ typedef enum ConditionType {
         CONDITION_PATH_IS_SYMBOLIC_LINK,
         CONDITION_PATH_IS_MOUNT_POINT,
         CONDITION_PATH_IS_READ_WRITE,
+        CONDITION_PATH_IS_ENCRYPTED,
         CONDITION_DIRECTORY_NOT_EMPTY,
         CONDITION_FILE_NOT_EMPTY,
         CONDITION_FILE_IS_EXECUTABLE,
@@ -66,18 +68,20 @@ typedef struct Condition {
 } Condition;
 
 Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate);
-void condition_free(Condition *c);
+Condition* condition_free(Condition *c);
 Condition* condition_free_list_type(Condition *first, ConditionType type);
 static inline Condition* condition_free_list(Condition *first) {
         return condition_free_list_type(first, _CONDITION_TYPE_INVALID);
 }
 
-int condition_test(Condition *c);
+int condition_test(Condition *c, char **env);
+
 typedef int (*condition_test_logger_t)(void *userdata, int level, int error, const char *file, int line, const char *func, const char *format, ...) _printf_(7, 8);
-bool condition_test_list(Condition *first, const char *(*to_string)(ConditionType t), condition_test_logger_t logger, void *userdata);
+typedef const char* (*condition_to_string_t)(ConditionType t) _const_;
+bool condition_test_list(Condition *first, char **env, condition_to_string_t to_string, condition_test_logger_t logger, void *userdata);
 
-void condition_dump(Condition *c, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t));
-void condition_dump_list(Condition *c, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t));
+void condition_dump(Condition *c, FILE *f, const char *prefix, condition_to_string_t to_string);
+void condition_dump_list(Condition *c, FILE *f, const char *prefix, condition_to_string_t to_string);
 
 const char* condition_type_to_string(ConditionType t) _const_;
 ConditionType condition_type_from_string(const char *s) _pure_;
@@ -96,6 +100,7 @@ static inline bool condition_takes_path(ConditionType t) {
                       CONDITION_PATH_IS_SYMBOLIC_LINK,
                       CONDITION_PATH_IS_MOUNT_POINT,
                       CONDITION_PATH_IS_READ_WRITE,
+                      CONDITION_PATH_IS_ENCRYPTED,
                       CONDITION_DIRECTORY_NOT_EMPTY,
                       CONDITION_FILE_NOT_EMPTY,
                       CONDITION_FILE_IS_EXECUTABLE,
index 811211ccf20c7535cc2af685d0af3ca32c0a8d99..0fec79f3d7979fe60ee7ea028de1ec5faf238a6b 100644 (file)
@@ -23,6 +23,7 @@
 #include "path-util.h"
 #include "process-util.h"
 #include "rlimit-util.h"
+#include "sd-id128.h"
 #include "signal-util.h"
 #include "socket-util.h"
 #include "string-util.h"
@@ -158,7 +159,7 @@ static int parse_line(
                 char *l,
                 void *userdata) {
 
-        char *e, *include;
+        char *e;
 
         assert(filename);
         assert(line > 0);
@@ -172,35 +173,6 @@ static int parse_line(
         if (*l == '\n')
                 return 0;
 
-        include = first_word(l, ".include");
-        if (include) {
-                _cleanup_free_ char *fn = NULL;
-
-                /* .includes are a bad idea, we only support them here
-                 * for historical reasons. They create cyclic include
-                 * problems and make it difficult to detect
-                 * configuration file changes with an easy
-                 * stat(). Better approaches, such as .d/ drop-in
-                 * snippets exist.
-                 *
-                 * Support for them should be eventually removed. */
-
-                if (!(flags & CONFIG_PARSE_ALLOW_INCLUDE)) {
-                        log_syntax(unit, LOG_ERR, filename, line, 0, ".include not allowed here. Ignoring.");
-                        return 0;
-                }
-
-                log_syntax(unit, LOG_WARNING, filename, line, 0,
-                           ".include directives are deprecated, and support for them will be removed in a future version of systemd. "
-                           "Please use drop-in files instead.");
-
-                fn = file_in_same_dir(filename, strstrip(include));
-                if (!fn)
-                        return -ENOMEM;
-
-                return config_parse(unit, fn, NULL, sections, lookup, table, flags, userdata);
-        }
-
         if (!utf8_is_valid(l))
                 return log_syntax_invalid_utf8(unit, LOG_WARNING, filename, line, l);
 
@@ -288,13 +260,15 @@ int config_parse(const char *unit,
                  ConfigItemLookup lookup,
                  const void *table,
                  ConfigParseFlags flags,
-                 void *userdata) {
+                 void *userdata,
+                 usec_t *ret_mtime) {
 
         _cleanup_free_ char *section = NULL, *continuation = NULL;
         _cleanup_fclose_ FILE *ours = NULL;
         unsigned line = 0, section_line = 0;
         bool section_ignored = false, bom_seen = false;
         int r, fd;
+        usec_t mtime;
 
         assert(filename);
         assert(lookup);
@@ -312,8 +286,16 @@ int config_parse(const char *unit,
         }
 
         fd = fileno(f);
-        if (fd >= 0) /* stream might not have an fd, let's be careful hence */
-                fd_warn_permissions(filename, fd);
+        if (fd >= 0) { /* stream might not have an fd, let's be careful hence */
+                struct stat st;
+
+                if (fstat(fd, &st) < 0)
+                        return log_full_errno(FLAGS_SET(flags, CONFIG_PARSE_WARN) ? LOG_ERR : LOG_DEBUG, errno,
+                                              "Failed to fstat(%s): %m", filename);
+
+                (void) stat_warn_permissions(filename, &st);
+                mtime = timespec_load(&st.st_mtim);
+        }
 
         for (;;) {
                 _cleanup_free_ char *buf = NULL;
@@ -433,6 +415,9 @@ int config_parse(const char *unit,
                 }
         }
 
+        if (ret_mtime)
+                *ret_mtime = mtime;
+
         return 0;
 }
 
@@ -443,23 +428,32 @@ static int config_parse_many_files(
                 ConfigItemLookup lookup,
                 const void *table,
                 ConfigParseFlags flags,
-                void *userdata) {
+                void *userdata,
+                usec_t *ret_mtime) {
 
+        usec_t mtime = 0;
         char **fn;
         int r;
 
         if (conf_file) {
-                r = config_parse(NULL, conf_file, NULL, sections, lookup, table, flags, userdata);
+                r = config_parse(NULL, conf_file, NULL, sections, lookup, table, flags, userdata, &mtime);
                 if (r < 0)
                         return r;
         }
 
         STRV_FOREACH(fn, files) {
-                r = config_parse(NULL, *fn, NULL, sections, lookup, table, flags, userdata);
+                usec_t t;
+
+                r = config_parse(NULL, *fn, NULL, sections, lookup, table, flags, userdata, &t);
                 if (r < 0)
                         return r;
+                if (t > mtime) /* Find the newest */
+                        mtime = t;
         }
 
+        if (ret_mtime)
+                *ret_mtime = mtime;
+
         return 0;
 }
 
@@ -471,7 +465,8 @@ int config_parse_many_nulstr(
                 ConfigItemLookup lookup,
                 const void *table,
                 ConfigParseFlags flags,
-                void *userdata) {
+                void *userdata,
+                usec_t *ret_mtime) {
 
         _cleanup_strv_free_ char **files = NULL;
         int r;
@@ -480,7 +475,7 @@ int config_parse_many_nulstr(
         if (r < 0)
                 return r;
 
-        return config_parse_many_files(conf_file, files, sections, lookup, table, flags, userdata);
+        return config_parse_many_files(conf_file, files, sections, lookup, table, flags, userdata, ret_mtime);
 }
 
 /* Parse each config file in the directories specified as strv. */
@@ -493,7 +488,7 @@ int config_parse_many(
                 const void *table,
                 ConfigParseFlags flags,
                 void *userdata,
-                char ***ret_dropins) {
+                usec_t *ret_mtime) {
 
         _cleanup_strv_free_ char **dropin_dirs = NULL;
         _cleanup_strv_free_ char **files = NULL;
@@ -509,14 +504,7 @@ int config_parse_many(
         if (r < 0)
                 return r;
 
-        r = config_parse_many_files(conf_file, files, sections, lookup, table, flags, userdata);
-        if (r < 0)
-                return r;
-
-        if (ret_dropins)
-                *ret_dropins = TAKE_PTR(files);
-
-        return 0;
+        return config_parse_many_files(conf_file, files, sections, lookup, table, flags, userdata, ret_mtime);
 }
 
 #define DEFINE_PARSER(type, vartype, conv_func)                         \
@@ -560,7 +548,7 @@ int config_parse_iec_size(const char* unit,
         if (r >= 0 && (uint64_t) (size_t) v != v)
                 r = -ERANGE;
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value '%s', ignoring: %m", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse size value '%s', ignoring: %m", rvalue);
                 return 0;
         }
 
@@ -589,10 +577,8 @@ int config_parse_si_uint64(
         assert(data);
 
         r = parse_size(rvalue, 1000, sz);
-        if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value '%s', ignoring: %m", rvalue);
-                return 0;
-        }
+        if (r < 0)
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse size value '%s', ignoring: %m", rvalue);
 
         return 0;
 }
@@ -619,7 +605,7 @@ int config_parse_iec_uint64(
 
         r = parse_size(rvalue, 1024, bytes);
         if (r < 0)
-                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
 
         return 0;
 }
@@ -646,7 +632,7 @@ int config_parse_bool(const char* unit,
 
         k = parse_boolean(rvalue);
         if (k < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, k,
+                log_syntax(unit, fatal ? LOG_ERR : LOG_WARNING, filename, line, k,
                            "Failed to parse boolean value%s: %s",
                            fatal ? "" : ", ignoring", rvalue);
                 return fatal ? -ENOEXEC : 0;
@@ -656,6 +642,40 @@ int config_parse_bool(const char* unit,
         return 0;
 }
 
+int config_parse_id128(
+                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) {
+
+        sd_id128_t t, *result = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+
+        r = sd_id128_from_string(rvalue, &t);
+        if (r < 0) {
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse 128bit ID/UUID, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        if (sd_id128_is_null(t)) {
+                log_syntax(unit, LOG_WARNING, filename, line, 0, "128bit ID/UUID is all 0, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        *result = t;
+        return 0;
+}
+
 int config_parse_tristate(
                 const char* unit,
                 const char *filename,
@@ -681,7 +701,7 @@ int config_parse_tristate(
 
         k = parse_boolean(rvalue);
         if (k < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue);
                 return 0;
         }
 
@@ -776,25 +796,23 @@ int config_parse_strv(
                 return 0;
         }
 
-        for (;;) {
+        for (const char *p = rvalue;;) {
                 char *word = NULL;
 
-                r = extract_first_word(&rvalue, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
+                r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
                 if (r == 0)
-                        break;
+                        return 0;
                 if (r == -ENOMEM)
                         return log_oom();
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
-                        break;
+                        log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
+                        return 0;
                 }
 
                 r = strv_consume(sv, word);
                 if (r < 0)
                         return log_oom();
         }
-
-        return 0;
 }
 
 int config_parse_warn_compat(
@@ -853,7 +871,7 @@ int config_parse_log_facility(
 
         x = log_facility_unshifted_from_string(rvalue);
         if (x < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log facility, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse log facility, ignoring: %s", rvalue);
                 return 0;
         }
 
@@ -883,7 +901,7 @@ int config_parse_log_level(
 
         x = log_level_from_string(rvalue);
         if (x < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log level, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse log level, ignoring: %s", rvalue);
                 return 0;
         }
 
@@ -916,7 +934,7 @@ int config_parse_signal(
 
         r = signal_from_string(rvalue);
         if (r <= 0) {
-                log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse signal name, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse signal name, ignoring: %s", rvalue);
                 return 0;
         }
 
@@ -948,7 +966,7 @@ int config_parse_personality(
         else {
                 p = personality_from_string(rvalue);
                 if (p == PERSONALITY_INVALID) {
-                        log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse personality, ignoring: %s", rvalue);
+                        log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse personality, ignoring: %s", rvalue);
                         return 0;
                 }
         }
@@ -983,7 +1001,7 @@ int config_parse_ifname(
         }
 
         if (!ifname_valid(rvalue)) {
-                log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not valid or too long, ignoring assignment: %s", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, 0, "Interface name is not valid or too long, ignoring assignment: %s", rvalue);
                 return 0;
         }
 
@@ -1008,7 +1026,6 @@ int config_parse_ifnames(
 
         _cleanup_strv_free_ char **names = NULL;
         char ***s = data;
-        const char *p;
         int r;
 
         assert(filename);
@@ -1021,13 +1038,14 @@ int config_parse_ifnames(
                 return 0;
         }
 
-        p = rvalue;
-        for (;;) {
+        for (const char *p = rvalue;;) {
                 _cleanup_free_ char *word = NULL;
 
                 r = extract_first_word(&p, &word, NULL, 0);
+                if (r == -ENOMEM)
+                        return log_oom();
                 if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r,
+                        log_syntax(unit, LOG_WARNING, filename, line, r,
                                    "Failed to extract interface name, ignoring assignment: %s",
                                    rvalue);
                         return 0;
@@ -1036,7 +1054,7 @@ int config_parse_ifnames(
                         break;
 
                 if (!ifname_valid_full(word, ltype)) {
-                        log_syntax(unit, LOG_ERR, filename, line, 0,
+                        log_syntax(unit, LOG_WARNING, filename, line, 0,
                                    "Interface name is not valid or too long, ignoring assignment: %s",
                                    word);
                         continue;
@@ -1082,7 +1100,7 @@ int config_parse_ip_port(
 
         r = parse_ip_port(rvalue, &port);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse port '%s'.", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse port '%s'.", rvalue);
                 return 0;
         }
 
@@ -1111,14 +1129,14 @@ int config_parse_mtu(
 
         r = parse_mtu(ltype, rvalue, mtu);
         if (r == -ERANGE) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Maximum transfer unit (MTU) value out of range. Permitted range is %" PRIu32 "…%" PRIu32 ", ignoring: %s",
                            (uint32_t) (ltype == AF_INET6 ? IPV6_MIN_MTU : IPV4_MIN_MTU), (uint32_t) UINT32_MAX,
                            rvalue);
                 return 0;
         }
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to parse MTU value '%s', ignoring: %m", rvalue);
                 return 0;
         }
@@ -1150,7 +1168,7 @@ int config_parse_rlimit(
                 return 0;
         }
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue);
+                log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue);
                 return 0;
         }
 
@@ -1186,7 +1204,7 @@ int config_parse_permille(const char* unit,
 
         r = parse_permille(rvalue);
         if (r < 0) {
-                log_syntax(unit, LOG_ERR, filename, line, r,
+                log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to parse permille value, ignoring: %s", rvalue);
                 return 0;
         }
@@ -1195,3 +1213,35 @@ int config_parse_permille(const char* unit,
 
         return 0;
 }
+
+int config_parse_vlanprotocol(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 *vlan_protocol = data;
+        assert(filename);
+        assert(lvalue);
+
+        if (isempty(rvalue)) {
+                *vlan_protocol = -1;
+                return 0;
+        }
+
+        if (STR_IN_SET(rvalue, "802.1ad", "802.1AD"))
+                *vlan_protocol = ETH_P_8021AD;
+        else if (STR_IN_SET(rvalue, "802.1q", "802.1Q"))
+                *vlan_protocol = ETH_P_8021Q;
+        else {
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "Failed to parse VLAN protocol value, ignoring: %s", rvalue);
+                return 0;
+        }
+
+        return 0;
+}
index 955c8b696433e88487b92213363168561b1dd6ec..7c9f5531b0cf08c29a9b4849b82dc60f0fae2baf 100644 (file)
 #include "alloc-util.h"
 #include "log.h"
 #include "macro.h"
+#include "time-util.h"
 
 /* An abstract parser for simple, line based, shallow configuration files consisting of variable assignments only. */
 
 typedef enum ConfigParseFlags {
         CONFIG_PARSE_RELAXED       = 1 << 0, /* Do not warn about unknown non-extension fields */
-        CONFIG_PARSE_ALLOW_INCLUDE = 1 << 1, /* Allow the deprecated .include stanza */
-        CONFIG_PARSE_WARN          = 1 << 2, /* Emit non-debug messages */
+        CONFIG_PARSE_WARN          = 1 << 1, /* Emit non-debug messages */
 } ConfigParseFlags;
 
 /* Argument list for parsers of specific configuration settings. */
@@ -84,11 +84,12 @@ int config_parse(
                 const char *unit,
                 const char *filename,
                 FILE *f,
-                const char *sections,  /* nulstr */
+                const char *sections,       /* nulstr */
                 ConfigItemLookup lookup,
                 const void *table,
                 ConfigParseFlags flags,
-                void *userdata);
+                void *userdata,
+                usec_t *ret_mtime);         /* possibly NULL */
 
 int config_parse_many_nulstr(
                 const char *conf_file,      /* possibly NULL */
@@ -97,7 +98,8 @@ int config_parse_many_nulstr(
                 ConfigItemLookup lookup,
                 const void *table,
                 ConfigParseFlags flags,
-                void *userdata);
+                void *userdata,
+                usec_t *ret_mtime);         /* possibly NULL */
 
 int config_parse_many(
                 const char *conf_file,      /* possibly NULL */
@@ -108,7 +110,7 @@ int config_parse_many(
                 const void *table,
                 ConfigParseFlags flags,
                 void *userdata,
-                char ***ret_dropins);       /* possibly NULL */
+                usec_t *ret_mtime);         /* possibly NULL */
 
 CONFIG_PARSER_PROTOTYPE(config_parse_int);
 CONFIG_PARSER_PROTOTYPE(config_parse_unsigned);
@@ -123,6 +125,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_iec_size);
 CONFIG_PARSER_PROTOTYPE(config_parse_si_uint64);
 CONFIG_PARSER_PROTOTYPE(config_parse_iec_uint64);
 CONFIG_PARSER_PROTOTYPE(config_parse_bool);
+CONFIG_PARSER_PROTOTYPE(config_parse_id128);
 CONFIG_PARSER_PROTOTYPE(config_parse_tristate);
 CONFIG_PARSER_PROTOTYPE(config_parse_string);
 CONFIG_PARSER_PROTOTYPE(config_parse_path);
@@ -143,6 +146,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_ifnames);
 CONFIG_PARSER_PROTOTYPE(config_parse_ip_port);
 CONFIG_PARSER_PROTOTYPE(config_parse_mtu);
 CONFIG_PARSER_PROTOTYPE(config_parse_rlimit);
+CONFIG_PARSER_PROTOTYPE(config_parse_vlanprotocol);
 
 typedef enum Disabled {
         DISABLED_CONFIGURATION,
@@ -161,7 +165,7 @@ typedef enum Disabled {
                                                                         \
                 r = parser(rvalue);                                     \
                 if (r < 0) {                                            \
-                        log_syntax(unit, LOG_ERR, filename, line, r,    \
+                        log_syntax(unit, LOG_WARNING, filename, line, r, \
                                    msg ", ignoring: %s", rvalue);       \
                         return 0;                                       \
                 }                                                       \
@@ -182,7 +186,7 @@ typedef enum Disabled {
                                                                         \
                 r = parser(rvalue, i);                                  \
                 if (r < 0)                                              \
-                        log_syntax(unit, LOG_ERR, filename, line, r,    \
+                        log_syntax(unit, LOG_WARNING, filename, line, r, \
                                    msg ", ignoring: %s", rvalue);       \
                                                                         \
                 return 0;                                               \
@@ -199,7 +203,7 @@ typedef enum Disabled {
                                                                         \
                 x = name##_from_string(rvalue);                         \
                 if (x < 0) {                                            \
-                        log_syntax(unit, LOG_ERR, filename, line, 0,    \
+                        log_syntax(unit, LOG_WARNING, filename, line, 0, \
                                    msg ", ignoring: %s", rvalue);       \
                         return 0;                                       \
                 }                                                       \
@@ -224,7 +228,7 @@ typedef enum Disabled {
                                                                         \
                 x = name##_from_string(rvalue);                         \
                 if (x < 0) {                                            \
-                        log_syntax(unit, LOG_ERR, filename, line, 0,    \
+                        log_syntax(unit, LOG_WARNING, filename, line, 0, \
                                    msg ", ignoring: %s", rvalue);       \
                         return 0;                                       \
                 }                                                       \
@@ -257,10 +261,10 @@ typedef enum Disabled {
                                                                                \
                         en = strndup(word, l);                                 \
                         if (!en)                                               \
-                                return -ENOMEM;                                \
+                                return log_oom();                              \
                                                                                \
                         if ((x = name##_from_string(en)) < 0) {                \
-                                log_syntax(unit, LOG_ERR, filename, line, 0,   \
+                                log_syntax(unit, LOG_WARNING, filename, line, 0, \
                                            msg ", ignoring: %s", en);          \
                                 continue;                                      \
                         }                                                      \
@@ -283,7 +287,7 @@ typedef enum Disabled {
                         if (new_xs)                                            \
                                 xs = new_xs;                                   \
                         else                                                   \
-                                return -ENOMEM;                                \
+                                return log_oom();                              \
                                                                                \
                         *(xs + i) = invalid;                                   \
                 }                                                              \
diff --git a/src/shared/coredump-util.c b/src/shared/coredump-util.c
new file mode 100644 (file)
index 0000000..6789741
--- /dev/null
@@ -0,0 +1,74 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "coredump-util.h"
+#include "extract-word.h"
+#include "fileio.h"
+#include "string-table.h"
+
+static const char *const coredump_filter_table[_COREDUMP_FILTER_MAX] = {
+        [COREDUMP_FILTER_PRIVATE_ANONYMOUS]   = "private-anonymous",
+        [COREDUMP_FILTER_SHARED_ANONYMOUS]    = "shared-anonymous",
+        [COREDUMP_FILTER_PRIVATE_FILE_BACKED] = "private-file-backed",
+        [COREDUMP_FILTER_SHARED_FILE_BACKED]  = "shared-file-backed",
+        [COREDUMP_FILTER_ELF_HEADERS]         = "elf-headers",
+        [COREDUMP_FILTER_PRIVATE_HUGE]        = "private-huge",
+        [COREDUMP_FILTER_SHARED_HUGE]         = "shared-huge",
+        [COREDUMP_FILTER_PRIVATE_DAX]         = "private-dax",
+        [COREDUMP_FILTER_SHARED_DAX]          = "shared-dax",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(coredump_filter, CoredumpFilter);
+
+int coredump_filter_mask_from_string(const char *s, uint64_t *ret) {
+        uint64_t m = 0;
+
+        assert(s);
+        assert(ret);
+
+        for (;;) {
+                _cleanup_free_ char *n = NULL;
+                CoredumpFilter v;
+                int r;
+
+                r = extract_first_word(&s, &n, NULL, 0);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        break;
+
+                if (streq(n, "default")) {
+                        m |= COREDUMP_FILTER_MASK_DEFAULT;
+                        continue;
+                }
+
+                if (streq(n, "all")) {
+                        m = UINT64_MAX;
+                        continue;
+                }
+
+                v = coredump_filter_from_string(n);
+                if (v >= 0) {
+                        m |= 1u << v;
+                        continue;
+                }
+
+                uint64_t x;
+                r = safe_atoux64(n, &x);
+                if (r < 0)
+                        return r;
+
+                m |= x;
+        }
+
+        *ret = m;
+        return 0;
+}
+
+int set_coredump_filter(uint64_t value) {
+        char t[STRLEN("0xFFFFFFFF")];
+
+        sprintf(t, "0x%"PRIx64, value);
+
+        return write_string_file("/proc/self/coredump_filter", t,
+                                 WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_DISABLE_BUFFER);
+}
diff --git a/src/shared/coredump-util.h b/src/shared/coredump-util.h
new file mode 100644 (file)
index 0000000..ff2e511
--- /dev/null
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "macro.h"
+
+typedef enum CoredumpFilter {
+        COREDUMP_FILTER_PRIVATE_ANONYMOUS = 0,
+        COREDUMP_FILTER_SHARED_ANONYMOUS,
+        COREDUMP_FILTER_PRIVATE_FILE_BACKED,
+        COREDUMP_FILTER_SHARED_FILE_BACKED,
+        COREDUMP_FILTER_ELF_HEADERS,
+        COREDUMP_FILTER_PRIVATE_HUGE,
+        COREDUMP_FILTER_SHARED_HUGE,
+        COREDUMP_FILTER_PRIVATE_DAX,
+        COREDUMP_FILTER_SHARED_DAX,
+        _COREDUMP_FILTER_MAX,
+        _COREDUMP_FILTER_INVALID = -1,
+} CoredumpFilter;
+
+#define COREDUMP_FILTER_MASK_DEFAULT (1u << COREDUMP_FILTER_PRIVATE_ANONYMOUS | \
+                                      1u << COREDUMP_FILTER_SHARED_ANONYMOUS | \
+                                      1u << COREDUMP_FILTER_ELF_HEADERS | \
+                                      1u << COREDUMP_FILTER_PRIVATE_HUGE)
+
+const char* coredump_filter_to_string(CoredumpFilter i) _const_;
+CoredumpFilter coredump_filter_from_string(const char *s) _pure_;
+int coredump_filter_mask_from_string(const char *s, uint64_t *ret);
+
+int set_coredump_filter(uint64_t value);
index 4bce8b167b551c9b64852d669d6be8660ccd1bac..6e57e2a99d3a79fd3f22d39239bfc4c93431d0ab 100644 (file)
@@ -56,31 +56,38 @@ int dev_setup(const char *prefix, uid_t uid, gid_t gid) {
         return 0;
 }
 
-int make_inaccessible_nodes(const char *root, uid_t uid, gid_t gid) {
+int make_inaccessible_nodes(
+                const char *runtime_dir,
+                uid_t uid,
+                gid_t gid) {
+
         static const struct {
                 const char *name;
                 mode_t mode;
         } table[] = {
-                { "",                   S_IFDIR  | 0755 },
-                { "/inaccessible",      S_IFDIR  | 0000 },
-                { "/inaccessible/reg",  S_IFREG  | 0000 },
-                { "/inaccessible/dir",  S_IFDIR  | 0000 },
-                { "/inaccessible/fifo", S_IFIFO  | 0000 },
-                { "/inaccessible/sock", S_IFSOCK | 0000 },
+                { "/systemd",                   S_IFDIR  | 0755 },
+                { "/systemd/inaccessible",      S_IFDIR  | 0000 },
+                { "/systemd/inaccessible/reg",  S_IFREG  | 0000 },
+                { "/systemd/inaccessible/dir",  S_IFDIR  | 0000 },
+                { "/systemd/inaccessible/fifo", S_IFIFO  | 0000 },
+                { "/systemd/inaccessible/sock", S_IFSOCK | 0000 },
 
                 /* The following two are likely to fail if we lack the privs for it (for example in an userns
                  * environment, if CAP_SYS_MKNOD is missing, or if a device node policy prohibit major/minor of 0
                  * device nodes to be created). But that's entirely fine. Consumers of these files should carry
                  * fallback to use a different node then, for example <root>/inaccessible/sock, which is close
                  * enough in behaviour and semantics for most uses. */
-                { "/inaccessible/chr",  S_IFCHR  | 0000 },
-                { "/inaccessible/blk",  S_IFBLK  | 0000 },
+                { "/systemd/inaccessible/chr",  S_IFCHR  | 0000 },
+                { "/systemd/inaccessible/blk",  S_IFBLK  | 0000 },
         };
 
         _cleanup_umask_ mode_t u;
         size_t i;
         int r;
 
+        if (!runtime_dir)
+                runtime_dir = "/run";
+
         u = umask(0000);
 
         /* Set up inaccessible (and empty) file nodes of all types. This are used to as mount sources for over-mounting
@@ -91,17 +98,17 @@ int make_inaccessible_nodes(const char *root, uid_t uid, gid_t gid) {
         for (i = 0; i < ELEMENTSOF(table); i++) {
                 _cleanup_free_ char *path = NULL;
 
-                path = path_join(root, table[i].name);
+                path = path_join(runtime_dir, table[i].name);
                 if (!path)
                         return log_oom();
 
                 if (S_ISDIR(table[i].mode))
-                        r = mkdir(path, table[i].mode & 07777);
+                        r = mkdir_label(path, table[i].mode & 07777);
                 else
-                        r = mknod(path, table[i].mode, makedev(0, 0));
+                        r = mknod_label(path, table[i].mode, makedev(0, 0));
                 if (r < 0) {
-                        if (errno != EEXIST)
-                                log_debug_errno(errno, "Failed to create '%s', ignoring: %m", path);
+                        if (r != -EEXIST)
+                                log_debug_errno(r, "Failed to create '%s', ignoring: %m", path);
                         continue;
                 }
 
index 7142b07038ae92c06637053f52ed6e442d953f29..24be6de6c546fa84e39afa693ff26051ebb8d6b7 100644 (file)
@@ -75,10 +75,9 @@ int probe_filesystem(const char *node, char **ret_fstype) {
                 log_debug("No type detected on partition %s", node);
                 goto not_found;
         }
-        if (r == -2) {
-                log_debug("Results ambiguous for partition %s", node);
-                return -EUCLEAN;
-        }
+        if (r == -2)
+                return log_debug_errno(SYNTHETIC_ERRNO(EUCLEAN),
+                                       "Results ambiguous for partition %s", node);
         if (r != 0)
                 return errno_or_else(EIO);
 
@@ -308,6 +307,7 @@ int dissect_image(
                 int fd,
                 const void *root_hash,
                 size_t root_hash_size,
+                const char *verity_data,
                 DissectImageFlags flags,
                 DissectedImage **ret) {
 
@@ -330,6 +330,7 @@ int dissect_image(
         assert(fd >= 0);
         assert(ret);
         assert(root_hash || root_hash_size == 0);
+        assert(!((flags & DISSECT_IMAGE_GPT_ONLY) && (flags & DISSECT_IMAGE_NO_PARTITION_TABLE)));
 
         /* Probes a disk image, and returns information about what it found in *ret.
          *
@@ -392,8 +393,9 @@ int dissect_image(
         if (r < 0)
                 return r;
 
-        if (!(flags & DISSECT_IMAGE_GPT_ONLY) &&
-            (flags & DISSECT_IMAGE_REQUIRE_ROOT)) {
+        if ((!(flags & DISSECT_IMAGE_GPT_ONLY) &&
+            (flags & DISSECT_IMAGE_REQUIRE_ROOT)) ||
+            (flags & DISSECT_IMAGE_NO_PARTITION_TABLE)) {
                 const char *usage = NULL;
 
                 (void) blkid_probe_lookup_value(b, "USAGE", &usage, NULL);
@@ -414,9 +416,13 @@ int dissect_image(
                         if (r < 0)
                                 return r;
 
+                        m->single_file_system = true;
+                        m->verity = root_hash && verity_data;
+                        m->can_verity = !!verity_data;
+
                         m->partitions[PARTITION_ROOT] = (DissectedPartition) {
                                 .found = true,
-                                .rw = true,
+                                .rw = !m->verity,
                                 .partno = -1,
                                 .architecture = _ARCHITECTURE_INVALID,
                                 .fstype = TAKE_PTR(t),
@@ -1134,8 +1140,9 @@ static int make_dm_name_and_node(const void *original_node, const char *suffix,
 
         base = strrchr(original_node, '/');
         if (!base)
-                return -EINVAL;
-        base++;
+                base = original_node;
+        else
+                base++;
         if (isempty(base))
                 return -EINVAL;
 
@@ -1211,40 +1218,102 @@ static int decrypt_partition(
         return 0;
 }
 
+static int verity_can_reuse(const void *root_hash, size_t root_hash_size, bool has_sig, const char *name, struct crypt_device **ret_cd) {
+        /* If the same volume was already open, check that the root hashes match, and reuse it if they do */
+        _cleanup_free_ char *root_hash_existing = NULL;
+        _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
+        struct crypt_params_verity crypt_params = {};
+        size_t root_hash_existing_size = root_hash_size;
+        int r;
+
+        assert(ret_cd);
+
+        r = crypt_init_by_name(&cd, name);
+        if (r < 0)
+                return log_debug_errno(r, "Error opening verity device, crypt_init_by_name failed: %m");
+        r = crypt_get_verity_info(cd, &crypt_params);
+        if (r < 0)
+                return log_debug_errno(r, "Error opening verity device, crypt_get_verity_info failed: %m");
+        root_hash_existing = malloc0(root_hash_size);
+        if (!root_hash_existing)
+                return -ENOMEM;
+        r = crypt_volume_key_get(cd, CRYPT_ANY_SLOT, root_hash_existing, &root_hash_existing_size, NULL, 0);
+        if (r < 0)
+                return log_debug_errno(r, "Error opening verity device, crypt_volume_key_get failed: %m");
+        if (root_hash_size != root_hash_existing_size || memcmp(root_hash_existing, root_hash, root_hash_size) != 0)
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Error opening verity device, it already exists but root hashes are different.");
+#if HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY
+        /* Ensure that, if signatures are supported, we only reuse the device if the previous mount
+         * used the same settings, so that a previous unsigned mount will not be reused if the user
+         * asks to use signing for the new one, and viceversa. */
+        if (has_sig != !!(crypt_params.flags & CRYPT_VERITY_ROOT_HASH_SIGNATURE))
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Error opening verity device, it already exists but signature settings are not the same.");
+#endif
+
+        *ret_cd = TAKE_PTR(cd);
+        return 0;
+}
+
+static inline void dm_deferred_remove_clean(char *name) {
+        if (!name)
+                return;
+        (void) crypt_deactivate_by_name(NULL, name, CRYPT_DEACTIVATE_DEFERRED);
+        free(name);
+}
+DEFINE_TRIVIAL_CLEANUP_FUNC(char *, dm_deferred_remove_clean);
+
 static int verity_partition(
                 DissectedPartition *m,
                 DissectedPartition *v,
                 const void *root_hash,
                 size_t root_hash_size,
+                const char *verity_data,
+                const char *root_hash_sig_path,
+                const void *root_hash_sig,
+                size_t root_hash_sig_size,
                 DissectImageFlags flags,
                 DecryptedImage *d) {
 
-        _cleanup_free_ char *node = NULL, *name = NULL;
+        _cleanup_free_ char *node = NULL, *name = NULL, *hash_sig_from_file = NULL;
         _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
+        _cleanup_(dm_deferred_remove_cleanp) char *restore_deferred_remove = NULL;
         int r;
 
         assert(m);
-        assert(v);
+        assert(v || verity_data);
 
         if (!root_hash)
                 return 0;
 
         if (!m->found || !m->node || !m->fstype)
                 return 0;
-        if (!v->found || !v->node || !v->fstype)
-                return 0;
+        if (!verity_data) {
+                if (!v->found || !v->node || !v->fstype)
+                        return 0;
 
-        if (!streq(v->fstype, "DM_verity_hash"))
-                return 0;
+                if (!streq(v->fstype, "DM_verity_hash"))
+                        return 0;
+        }
 
-        r = make_dm_name_and_node(m->node, "-verity", &name, &node);
+        if (FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE)) {
+                /* Use the roothash, which is unique per volume, as the device node name, so that it can be reused */
+                _cleanup_free_ char *root_hash_encoded = NULL;
+                root_hash_encoded = hexmem(root_hash, root_hash_size);
+                if (!root_hash_encoded)
+                        return -ENOMEM;
+                r = make_dm_name_and_node(root_hash_encoded, "-verity", &name, &node);
+        } else
+                r = make_dm_name_and_node(m->node, "-verity", &name, &node);
         if (r < 0)
                 return r;
 
-        if (!GREEDY_REALLOC0(d->decrypted, d->n_allocated, d->n_decrypted + 1))
-                return -ENOMEM;
+        if (!root_hash_sig && root_hash_sig_path) {
+                r = read_full_file_full(AT_FDCWD, root_hash_sig_path, 0, &hash_sig_from_file, &root_hash_sig_size);
+                if (r < 0)
+                        return r;
+        }
 
-        r = crypt_init(&cd, v->node);
+        r = crypt_init(&cd, verity_data ?: v->node);
         if (r < 0)
                 return r;
 
@@ -1258,9 +1327,69 @@ static int verity_partition(
         if (r < 0)
                 return r;
 
-        r = crypt_activate_by_volume_key(cd, name, root_hash, root_hash_size, CRYPT_ACTIVATE_READONLY);
-        if (r < 0)
-                return r;
+        if (!GREEDY_REALLOC0(d->decrypted, d->n_allocated, d->n_decrypted + 1))
+                return -ENOMEM;
+
+        /* If activating fails because the device already exists, check the metadata and reuse it if it matches.
+         * In case of ENODEV/ENOENT, which can happen if another process is activating at the exact same time,
+         * retry a few times before giving up. */
+        for (unsigned i = 0; i < N_DEVICE_NODE_LIST_ATTEMPTS; i++) {
+                if (root_hash_sig || hash_sig_from_file) {
+#if HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY
+                        r = crypt_activate_by_signed_key(cd, name, root_hash, root_hash_size, root_hash_sig ?: hash_sig_from_file, root_hash_sig_size, CRYPT_ACTIVATE_READONLY);
+#else
+                        r = log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "activation of verity device with signature requested, but not supported by cryptsetup due to missing crypt_activate_by_signed_key()");
+#endif
+                } else
+                        r = crypt_activate_by_volume_key(cd, name, root_hash, root_hash_size, CRYPT_ACTIVATE_READONLY);
+                /* libdevmapper can return EINVAL when the device is already in the activation stage.
+                 * There's no way to distinguish this situation from a genuine error due to invalid
+                 * parameters, so immediately fallback to activating the device with a unique name.
+                 * Improvements in libcrypsetup can ensure this never happens: https://gitlab.com/cryptsetup/cryptsetup/-/merge_requests/96 */
+                if (r == -EINVAL && FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE))
+                        return verity_partition(m, v, root_hash, root_hash_size, verity_data, NULL, root_hash_sig ?: hash_sig_from_file, root_hash_sig_size, flags & ~DISSECT_IMAGE_VERITY_SHARE, d);
+                if (!IN_SET(r, 0, -EEXIST, -ENODEV))
+                        return r;
+                if (r == -EEXIST) {
+                        struct crypt_device *existing_cd = NULL;
+
+                        if (!restore_deferred_remove){
+                                /* To avoid races, disable automatic removal on umount while setting up the new device. Restore it on failure. */
+                                r = dm_deferred_remove_cancel(name);
+                                if (r < 0)
+                                        return log_debug_errno(r, "Disabling automated deferred removal for verity device %s failed: %m", node);
+                                restore_deferred_remove = strdup(name);
+                                if (!restore_deferred_remove)
+                                        return -ENOMEM;
+                        }
+
+                        r = verity_can_reuse(root_hash, root_hash_size, !!root_hash_sig || !!hash_sig_from_file, name, &existing_cd);
+                        /* Same as above, -EINVAL can randomly happen when it actually means -EEXIST */
+                        if (r == -EINVAL && FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE))
+                                return verity_partition(m, v, root_hash, root_hash_size, verity_data, NULL, root_hash_sig ?: hash_sig_from_file, root_hash_sig_size, flags & ~DISSECT_IMAGE_VERITY_SHARE, d);
+                        if (!IN_SET(r, 0, -ENODEV, -ENOENT))
+                                return log_debug_errno(r, "Checking whether existing verity device %s can be reused failed: %m", node);
+                        if (r == 0) {
+                                if (cd)
+                                        crypt_free(cd);
+                                cd = existing_cd;
+                        }
+                }
+                if (r == 0)
+                        break;
+        }
+
+        /* Sanity check: libdevmapper is known to report that the device already exists and is active,
+        * but it's actually not there, so the later filesystem probe or mount would fail. */
+        if (r == 0)
+                r = access(node, F_OK);
+        /* An existing verity device was reported by libcryptsetup/libdevmapper, but we can't use it at this time.
+         * Fall back to activating it with a unique device name. */
+        if (r != 0 && FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE))
+                return verity_partition(m, v, root_hash, root_hash_size, verity_data, NULL, root_hash_sig ?: hash_sig_from_file, root_hash_sig_size, flags & ~DISSECT_IMAGE_VERITY_SHARE, d);
+
+        /* Everything looks good and we'll be able to mount the device, so deferred remove will be re-enabled at that point. */
+        restore_deferred_remove = mfree(restore_deferred_remove);
 
         d->decrypted[d->n_decrypted].name = TAKE_PTR(name);
         d->decrypted[d->n_decrypted].device = TAKE_PTR(cd);
@@ -1277,6 +1406,10 @@ int dissected_image_decrypt(
                 const char *passphrase,
                 const void *root_hash,
                 size_t root_hash_size,
+                const char *verity_data,
+                const char *root_hash_sig_path,
+                const void *root_hash_sig,
+                size_t root_hash_sig_size,
                 DissectImageFlags flags,
                 DecryptedImage **ret) {
 
@@ -1323,7 +1456,7 @@ int dissected_image_decrypt(
 
                 k = PARTITION_VERITY_OF(i);
                 if (k >= 0) {
-                        r = verity_partition(p, m->partitions + k, root_hash, root_hash_size, flags, d);
+                        r = verity_partition(p, m->partitions + k, root_hash, root_hash_size, verity_data, root_hash_sig_path, root_hash_sig, root_hash_sig_size, flags | DISSECT_IMAGE_VERITY_SHARE, d);
                         if (r < 0)
                                 return r;
                 }
@@ -1348,6 +1481,10 @@ int dissected_image_decrypt_interactively(
                 const char *passphrase,
                 const void *root_hash,
                 size_t root_hash_size,
+                const char *verity_data,
+                const char *root_hash_sig_path,
+                const void *root_hash_sig,
+                size_t root_hash_sig_size,
                 DissectImageFlags flags,
                 DecryptedImage **ret) {
 
@@ -1358,7 +1495,7 @@ int dissected_image_decrypt_interactively(
                 n--;
 
         for (;;) {
-                r = dissected_image_decrypt(m, passphrase, root_hash, root_hash_size, flags, ret);
+                r = dissected_image_decrypt(m, passphrase, root_hash, root_hash_size, verity_data, root_hash_sig_path, root_hash_sig, root_hash_sig_size, flags, ret);
                 if (r >= 0)
                         return r;
                 if (r == -EKEYREJECTED)
@@ -1399,7 +1536,7 @@ int decrypted_image_relinquish(DecryptedImage *d) {
                 if (p->relinquished)
                         continue;
 
-                r = dm_deferred_remove(p->name);
+                r = crypt_deactivate_by_name(NULL, p->name, CRYPT_DEACTIVATE_DEFERRED);
                 if (r < 0)
                         return log_debug_errno(r, "Failed to mark %s for auto-removal: %m", p->name);
 
@@ -1410,56 +1547,119 @@ int decrypted_image_relinquish(DecryptedImage *d) {
         return 0;
 }
 
-int root_hash_load(const char *image, void **ret, size_t *ret_size) {
-        _cleanup_free_ char *text = NULL;
-        _cleanup_free_ void *k = NULL;
-        size_t l;
+int verity_metadata_load(const char *image, const char *root_hash_path, void **ret_roothash, size_t *ret_roothash_size, char **ret_verity_data, char **ret_roothashsig) {
+        _cleanup_free_ char *verity_filename = NULL, *roothashsig_filename = NULL;
+        _cleanup_free_ void *roothash_decoded = NULL;
+        size_t roothash_decoded_size = 0;
         int r;
 
         assert(image);
-        assert(ret);
-        assert(ret_size);
 
         if (is_device_path(image)) {
                 /* If we are asked to load the root hash for a device node, exit early */
-                *ret = NULL;
-                *ret_size = 0;
+                if (ret_roothash)
+                        *ret_roothash = NULL;
+                if (ret_roothash_size)
+                        *ret_roothash_size = 0;
+                if (ret_verity_data)
+                        *ret_verity_data = NULL;
+                if (ret_roothashsig)
+                        *ret_roothashsig = NULL;
                 return 0;
         }
 
-        r = getxattr_malloc(image, "user.verity.roothash", &text, true);
-        if (r < 0) {
-                char *fn, *e, *n;
+        if (ret_verity_data) {
+                char *e;
 
-                if (!IN_SET(r, -ENODATA, -EOPNOTSUPP, -ENOENT))
-                        return r;
-
-                fn = newa(char, strlen(image) + STRLEN(".roothash") + 1);
-                n = stpcpy(fn, image);
-                e = endswith(fn, ".raw");
+                verity_filename = new(char, strlen(image) + STRLEN(".verity") + 1);
+                if (!verity_filename)
+                        return -ENOMEM;
+                strcpy(verity_filename, image);
+                e = endswith(verity_filename, ".raw");
                 if (e)
-                        n = e;
+                        strcpy(e, ".verity");
+                else
+                        strcat(verity_filename, ".verity");
 
-                strcpy(n, ".roothash");
+                r = access(verity_filename, F_OK);
+                if (r < 0) {
+                        if (errno != ENOENT)
+                                return -errno;
+                        verity_filename = mfree(verity_filename);
+                }
+        }
 
-                r = read_one_line_file(fn, &text);
-                if (r == -ENOENT) {
-                        *ret = NULL;
-                        *ret_size = 0;
-                        return 0;
+        if (ret_roothashsig) {
+                char *e;
+
+                /* Follow naming convention recommended by the relevant RFC:
+                 * https://tools.ietf.org/html/rfc5751#section-3.2.1 */
+                roothashsig_filename = new(char, strlen(image) + STRLEN(".roothash.p7s") + 1);
+                if (!roothashsig_filename)
+                        return -ENOMEM;
+                strcpy(roothashsig_filename, image);
+                e = endswith(roothashsig_filename, ".raw");
+                if (e)
+                        strcpy(e, ".roothash.p7s");
+                else
+                        strcat(roothashsig_filename, ".roothash.p7s");
+
+                r = access(roothashsig_filename, R_OK);
+                if (r < 0) {
+                        if (errno != ENOENT)
+                                return -errno;
+                        roothashsig_filename = mfree(roothashsig_filename);
                 }
-                if (r < 0)
-                        return r;
         }
 
-        r = unhexmem(text, strlen(text), &k, &l);
-        if (r < 0)
-                return r;
-        if (l < sizeof(sd_id128_t))
-                return -EINVAL;
+        if (ret_roothash) {
+                _cleanup_free_ char *text = NULL;
+                assert(ret_roothash_size);
 
-        *ret = TAKE_PTR(k);
-        *ret_size = l;
+                if (root_hash_path) {
+                        /* We have the path to a roothash to load and decode, eg: RootHash=/foo/bar.roothash */
+                        r = read_one_line_file(root_hash_path, &text);
+                        if (r < 0)
+                                return r;
+                } else {
+                        r = getxattr_malloc(image, "user.verity.roothash", &text, true);
+                        if (r < 0) {
+                                char *fn, *e, *n;
+
+                                if (!IN_SET(r, -ENODATA, -EOPNOTSUPP, -ENOENT))
+                                        return r;
+
+                                fn = newa(char, strlen(image) + STRLEN(".roothash") + 1);
+                                n = stpcpy(fn, image);
+                                e = endswith(fn, ".raw");
+                                if (e)
+                                        n = e;
+
+                                strcpy(n, ".roothash");
+
+                                r = read_one_line_file(fn, &text);
+                                if (r < 0 && r != -ENOENT)
+                                        return r;
+                        }
+                }
+
+                if (text) {
+                        r = unhexmem(text, strlen(text), &roothash_decoded, &roothash_decoded_size);
+                        if (r < 0)
+                                return r;
+                        if (roothash_decoded_size < sizeof(sd_id128_t))
+                                return -EINVAL;
+                }
+        }
+
+        if (ret_roothash) {
+                *ret_roothash = TAKE_PTR(roothash_decoded);
+                *ret_roothash_size = roothash_decoded_size;
+        }
+        if (ret_verity_data)
+                *ret_verity_data = TAKE_PTR(verity_filename);
+        if (roothashsig_filename)
+                *ret_roothashsig = TAKE_PTR(roothashsig_filename);
 
         return 1;
 }
@@ -1545,14 +1745,12 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
 
                 fds[2*k+1] = safe_close(fds[2*k+1]);
 
-                f = fdopen(fds[2*k], "r");
+                f = take_fdopen(&fds[2*k], "r");
                 if (!f) {
                         r = -errno;
                         goto finish;
                 }
 
-                fds[2*k] = -1;
-
                 switch (k) {
 
                 case META_HOSTNAME:
@@ -1620,6 +1818,7 @@ int dissect_image_and_warn(
                 const char *name,
                 const void *root_hash,
                 size_t root_hash_size,
+                const char *verity_data,
                 DissectImageFlags flags,
                 DissectedImage **ret) {
 
@@ -1634,7 +1833,7 @@ int dissect_image_and_warn(
                 name = buffer;
         }
 
-        r = dissect_image(fd, root_hash, root_hash_size, flags, ret);
+        r = dissect_image(fd, root_hash, root_hash_size, verity_data, flags, ret);
 
         switch (r) {
 
@@ -1664,6 +1863,23 @@ int dissect_image_and_warn(
         }
 }
 
+bool dissected_image_can_do_verity(const DissectedImage *image, unsigned partition_designator) {
+        if (image->single_file_system)
+                return partition_designator == PARTITION_ROOT && image->can_verity;
+
+        return PARTITION_VERITY_OF(partition_designator) >= 0;
+}
+
+bool dissected_image_has_verity(const DissectedImage *image, unsigned partition_designator) {
+        int k;
+
+        if (image->single_file_system)
+                return partition_designator == PARTITION_ROOT && image->verity;
+
+        k = PARTITION_VERITY_OF(partition_designator);
+        return k >= 0 && image->partitions[k].found;
+}
+
 static const char *const partition_designator_table[] = {
         [PARTITION_ROOT] = "root",
         [PARTITION_ROOT_SECONDARY] = "root-secondary",
index 6a666ca7c70bec70eaf39666b3705fdc10816040..84ec1ce331163c6bffd61474ec233d09119ad549 100644 (file)
@@ -63,12 +63,15 @@ typedef enum DissectImageFlags {
         DISSECT_IMAGE_NO_UDEV             = 1 << 9,  /* Don't wait for udev initializing things */
         DISSECT_IMAGE_RELAX_VAR_CHECK     = 1 << 10, /* Don't insist that the UUID of /var is hashed from /etc/machine-id */
         DISSECT_IMAGE_FSCK                = 1 << 11, /* File system check the partition before mounting (no effect when combined with DISSECT_IMAGE_READ_ONLY) */
+        DISSECT_IMAGE_NO_PARTITION_TABLE  = 1 << 12, /* Only recognize single file system images */
+        DISSECT_IMAGE_VERITY_SHARE        = 1 << 13, /* When activating a verity device, reuse existing one if already open */
 } DissectImageFlags;
 
 struct DissectedImage {
         bool encrypted:1;
         bool verity:1;     /* verity available and usable */
         bool can_verity:1; /* verity available, but not necessarily used */
+        bool single_file_system:1; /* MBR/GPT or single file system */
 
         DissectedPartition partitions[_PARTITION_DESIGNATOR_MAX];
 
@@ -79,14 +82,14 @@ struct DissectedImage {
 };
 
 int probe_filesystem(const char *node, char **ret_fstype);
-int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectImageFlags flags, DissectedImage **ret);
-int dissect_image_and_warn(int fd, const char *name, const void *root_hash, size_t root_hash_size, DissectImageFlags flags, DissectedImage **ret);
+int dissect_image(int fd, const void *root_hash, size_t root_hash_size, const char *verity_data, DissectImageFlags flags, DissectedImage **ret);
+int dissect_image_and_warn(int fd, const char *name, const void *root_hash, size_t root_hash_size, const char *verity_data, DissectImageFlags flags, DissectedImage **ret);
 
 DissectedImage* dissected_image_unref(DissectedImage *m);
 DEFINE_TRIVIAL_CLEANUP_FUNC(DissectedImage*, dissected_image_unref);
 
-int dissected_image_decrypt(DissectedImage *m, const char *passphrase, const void *root_hash, size_t root_hash_size, DissectImageFlags flags, DecryptedImage **ret);
-int dissected_image_decrypt_interactively(DissectedImage *m, const char *passphrase, const void *root_hash, size_t root_hash_size, DissectImageFlags flags, DecryptedImage **ret);
+int dissected_image_decrypt(DissectedImage *m, const char *passphrase, const void *root_hash, size_t root_hash_size, const char *verity_data, const char *root_hash_sig_path, const void *root_hash_sig, size_t root_hash_sig_size, DissectImageFlags flags, DecryptedImage **ret);
+int dissected_image_decrypt_interactively(DissectedImage *m, const char *passphrase, const void *root_hash, size_t root_hash_size, const char *verity_data, const char *root_hash_sig_path, const void *root_hash_sig, size_t root_hash_sig_size, DissectImageFlags flags, DecryptedImage **ret);
 int dissected_image_mount(DissectedImage *m, const char *dest, uid_t uid_shift, DissectImageFlags flags);
 
 int dissected_image_acquire_metadata(DissectedImage *m);
@@ -98,4 +101,6 @@ int decrypted_image_relinquish(DecryptedImage *d);
 const char* partition_designator_to_string(int i) _const_;
 int partition_designator_from_string(const char *name) _pure_;
 
-int root_hash_load(const char *image, void **ret, size_t *ret_size);
+int verity_metadata_load(const char *image, const char *root_hash_path, void **ret_roothash, size_t *ret_roothash_size, char **ret_verity_data, char **ret_roothashsig);
+bool dissected_image_can_do_verity(const DissectedImage *image, unsigned partition_designator);
+bool dissected_image_has_verity(const DissectedImage *image, unsigned partition_designator);
index d817e5b0e5e2a14207cf54d15da907fd655f4881..7efcb6b2aae432599a511819eb5f90ee743a15b5 100644 (file)
@@ -6,35 +6,37 @@
 #include "fd-util.h"
 #include "string-util.h"
 
-int dm_deferred_remove(const char *name) {
-
-        struct dm_ioctl dm = {
-                .version = {
-                        DM_VERSION_MAJOR,
-                        DM_VERSION_MINOR,
-                        DM_VERSION_PATCHLEVEL
+int dm_deferred_remove_cancel(const char *name) {
+        _cleanup_close_ int fd = -1;
+        struct message {
+                struct dm_ioctl dm_ioctl;
+                struct dm_target_msg dm_target_msg;
+                char msg_text[STRLEN("@cancel_deferred_remove") + 1];
+        } _packed_ message = {
+                .dm_ioctl = {
+                        .version = {
+                                DM_VERSION_MAJOR,
+                                DM_VERSION_MINOR,
+                                DM_VERSION_PATCHLEVEL
+                        },
+                        .data_size = sizeof(struct message),
+                        .data_start = sizeof(struct dm_ioctl),
                 },
-                .data_size = sizeof(dm),
-                .flags = DM_DEFERRED_REMOVE,
+                .msg_text = "@cancel_deferred_remove",
         };
 
-        _cleanup_close_ int fd = -1;
-
         assert(name);
 
-        /* Unfortunately, libcryptsetup doesn't provide a proper API for this, hence call the ioctl()
-         * directly. */
-
-        if (strlen(name) >= sizeof(dm.name))
+        if (strlen(name) >= sizeof(message.dm_ioctl.name))
                 return -ENODEV; /* A device with a name longer than this cannot possibly exist */
 
+        strncpy_exact(message.dm_ioctl.name, name, sizeof(message.dm_ioctl.name));
+
         fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC);
         if (fd < 0)
                 return -errno;
 
-        strncpy_exact(dm.name, name, sizeof(dm.name));
-
-        if (ioctl(fd, DM_DEV_REMOVE, &dm))
+        if (ioctl(fd, DM_TARGET_MSG, &message))
                 return -errno;
 
         return 0;
index 6c78bfe75b7f745fa0bb2ec44ce20e076b4f07a6..997963c042b6858b9a1c53305711686d1be68a1e 100644 (file)
@@ -1,4 +1,4 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 #pragma once
 
-int dm_deferred_remove(const char *name);
+int dm_deferred_remove_cancel(const char *name);
index 6844c2b647b5ed07e8059544db5aeae65b3027ee..932da0c8531eb5fb0e949b361ad6f4977da4ebb5 100644 (file)
@@ -204,7 +204,7 @@ static int unit_file_find_dirs(
         type = unit_name_to_type(name);
         if (type < 0)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "Failed to to derive unit type from unit name: %s",
+                                       "Failed to derive unit type from unit name: %s",
                                        name);
 
         if (is_instance) {
@@ -226,30 +226,35 @@ int unit_file_find_dropin_paths(
                 Set *unit_path_cache,
                 const char *dir_suffix,
                 const char *file_suffix,
-                const Set *names,
+                const char *name,
+                const Set *aliases,
                 char ***ret) {
 
         _cleanup_strv_free_ char **dirs = NULL;
-        char *name, **p;
+        const char *n;
+        char **p;
         Iterator i;
         int r;
 
         assert(ret);
 
-        SET_FOREACH(name, names, i)
+        if (name)
                 STRV_FOREACH(p, lookup_path)
                         (void) unit_file_find_dirs(original_root, unit_path_cache, *p, name, dir_suffix, &dirs);
 
+        SET_FOREACH(n, aliases, i)
+                STRV_FOREACH(p, lookup_path)
+                        (void) unit_file_find_dirs(original_root, unit_path_cache, *p, n, dir_suffix, &dirs);
+
         /* All the names in the unit are of the same type so just grab one. */
-        name = (char*) set_first(names);
-        if (name) {
+        n = name ?: (const char*) set_first(aliases);
+        if (n) {
                 UnitType type = _UNIT_TYPE_INVALID;
 
-                type = unit_name_to_type(name);
+                type = unit_name_to_type(n);
                 if (type < 0)
                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                               "Failed to to derive unit type from unit name: %s",
-                                               name);
+                                               "Failed to derive unit type from unit name: %s", n);
 
                 /* Special top level drop in for "<unit type>.<suffix>". Add this last as it's the most generic
                  * and should be able to be overridden by more specific drop-ins. */
index 89a2ab1098b7f0ed70147b0975ad1b9b03fc4ee9..addf1dab140af2ef3985a7effbdd2a7c69121292 100644 (file)
@@ -21,5 +21,6 @@ int unit_file_find_dropin_paths(
                 Set *unit_path_cache,
                 const char *dir_suffix,
                 const char *file_suffix,
-                const Set *names,
+                const char *name,
+                const Set *aliases,
                 char ***paths);
index 12d48f32d54b218d8e2af56ecc0b79c7d64926d2..9411fc8cef8647b2ab243734bc6f1109a11960e5 100644 (file)
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
 #include <stdlib.h>
+#include <sys/stat.h>
 #include <unistd.h>
 
 #include "alloc-util.h"
@@ -11,6 +12,7 @@
 #include "io-util.h"
 #include "parse-util.h"
 #include "sort-util.h"
+#include "stat-util.h"
 #include "stdio-util.h"
 #include "string-util.h"
 #include "utf8.h"
 #define END_ENTIRE_DEVICE_PATH_SUBTYPE      0xff
 #define EFI_OS_INDICATIONS_BOOT_TO_FW_UI    0x0000000000000001
 
-#define boot_option__contents {                 \
-        uint32_t attr;                          \
-        uint16_t path_len;                      \
-        uint16_t title[];                       \
+#define boot_option__contents                   \
+        {                                       \
+                uint32_t attr;                  \
+                uint16_t path_len;              \
+                uint16_t title[];               \
         }
 
 struct boot_option boot_option__contents;
@@ -49,33 +52,39 @@ struct drive_path {
         uint8_t signature_type;
 } _packed_;
 
-#define device_path__contents {                 \
-        uint8_t type;                           \
-        uint8_t sub_type;                       \
-        uint16_t length;                        \
-        union {                                 \
-                uint16_t path[0];               \
-                struct drive_path drive;        \
-        };                                      \
+#define device_path__contents                           \
+        {                                               \
+                uint8_t type;                           \
+                uint8_t sub_type;                       \
+                uint16_t length;                        \
+                union {                                 \
+                        uint16_t path[0];               \
+                        struct drive_path drive;        \
+                };                                      \
         }
 
 struct device_path device_path__contents;
 struct device_path__packed device_path__contents _packed_;
 assert_cc(sizeof(struct device_path) == sizeof(struct device_path__packed));
 
-
 int efi_reboot_to_firmware_supported(void) {
         _cleanup_free_ void *v = NULL;
+        static int cache = -1;
         uint64_t b;
         size_t s;
         int r;
 
-        if (!is_efi_boot())
+        if (cache > 0)
+                return 0;
+        if (cache == 0)
                 return -EOPNOTSUPP;
 
+        if (!is_efi_boot())
+                goto not_supported;
+
         r = efi_get_variable(EFI_VENDOR_GLOBAL, "OsIndicationsSupported", NULL, &v, &s);
-        if (r == -ENOENT) /* variable doesn't exist? it's not supported then */
-                return -EOPNOTSUPP;
+        if (r == -ENOENT)
+                goto not_supported; /* variable doesn't exist? it's not supported then */
         if (r < 0)
                 return r;
         if (s != sizeof(uint64_t))
@@ -83,36 +92,68 @@ int efi_reboot_to_firmware_supported(void) {
 
         b = *(uint64_t*) v;
         if (!(b & EFI_OS_INDICATIONS_BOOT_TO_FW_UI))
-                return -EOPNOTSUPP; /* bit unset? it's not supported then */
+                goto not_supported; /* bit unset? it's not supported then */
 
+        cache = 1;
         return 0;
+
+not_supported:
+        cache = 0;
+        return -EOPNOTSUPP;
 }
 
-static int get_os_indications(uint64_t *os_indication) {
+static int get_os_indications(uint64_t *ret) {
+        static struct stat cache_stat = {};
         _cleanup_free_ void *v = NULL;
+        _cleanup_free_ char *fn = NULL;
+        static uint64_t cache;
+        struct stat new_stat;
         size_t s;
         int r;
 
+        assert(ret);
+
         /* Let's verify general support first */
         r = efi_reboot_to_firmware_supported();
         if (r < 0)
                 return r;
 
+        fn = efi_variable_path(EFI_VENDOR_GLOBAL, "OsIndications");
+        if (!fn)
+                return -ENOMEM;
+
+        /* stat() the EFI variable, to see if the mtime changed. If it did we need to cache again. */
+        if (stat(fn, &new_stat) < 0) {
+                if (errno != ENOENT)
+                        return -errno;
+
+                /* Doesn't exist? Then we can exit early (also see below) */
+                *ret = 0;
+                return 0;
+
+        } else if (stat_inode_unmodified(&new_stat, &cache_stat)) {
+                /* inode didn't change, we can return the cached value */
+                *ret = cache;
+                return 0;
+        }
+
         r = efi_get_variable(EFI_VENDOR_GLOBAL, "OsIndications", NULL, &v, &s);
         if (r == -ENOENT) {
                 /* Some firmware implementations that do support OsIndications and report that with
-                 * OsIndicationsSupported will remove the OsIndications variable when it is unset. Let's pretend it's 0
-                 * then, to hide this implementation detail. Note that this call will return -ENOENT then only if the
-                 * support for OsIndications is missing entirely, as determined by efi_reboot_to_firmware_supported()
-                 * above. */
-                *os_indication = 0;
+                 * OsIndicationsSupported will remove the OsIndications variable when it is unset. Let's
+                 * pretend it's 0 then, to hide this implementation detail. Note that this call will return
+                 * -ENOENT then only if the support for OsIndications is missing entirely, as determined by
+                 * efi_reboot_to_firmware_supported() above. */
+                *ret = 0;
                 return 0;
-        } else if (r < 0)
+        }
+        if (r < 0)
                 return r;
-        else if (s != sizeof(uint64_t))
+        if (s != sizeof(uint64_t))
                 return -EINVAL;
 
-        *os_indication = *(uint64_t *)v;
+        cache_stat = new_stat;
+        *ret = cache = *(uint64_t *)v;
         return 0;
 }
 
@@ -135,10 +176,7 @@ int efi_set_reboot_to_firmware(bool value) {
         if (r < 0)
                 return r;
 
-        if (value)
-                b_new = b | EFI_OS_INDICATIONS_BOOT_TO_FW_UI;
-        else
-                b_new = b & ~EFI_OS_INDICATIONS_BOOT_TO_FW_UI;
+        b_new = UPDATE_FLAG(b, EFI_OS_INDICATIONS_BOOT_TO_FW_UI, value);
 
         /* Avoid writing to efi vars store if we can due to firmware bugs. */
         if (b != b_new)
@@ -675,6 +713,76 @@ int efi_loader_get_features(uint64_t *ret) {
         return 0;
 }
 
+int efi_loader_get_config_timeout_one_shot(usec_t *ret) {
+        _cleanup_free_ char *v = NULL, *fn = NULL;
+        static struct stat cache_stat = {};
+        struct stat new_stat;
+        static usec_t cache;
+        uint64_t sec;
+        int r;
+
+        assert(ret);
+
+        fn = efi_variable_path(EFI_VENDOR_LOADER, "LoaderConfigTimeoutOneShot");
+        if (!fn)
+                return -ENOMEM;
+
+        /* stat() the EFI variable, to see if the mtime changed. If it did we need to cache again. */
+        if (stat(fn, &new_stat) < 0)
+                return -errno;
+
+        if (stat_inode_unmodified(&new_stat, &cache_stat)) {
+                *ret = cache;
+                return 0;
+        }
+
+        r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderConfigTimeoutOneShot", &v);
+        if (r < 0)
+                return r;
+
+        r = safe_atou64(v, &sec);
+        if (r < 0)
+                return r;
+        if (sec > USEC_INFINITY / USEC_PER_SEC)
+                return -ERANGE;
+
+        cache_stat = new_stat;
+        *ret = cache = sec * USEC_PER_SEC; /* return in µs */
+        return 0;
+}
+
+int efi_loader_update_entry_one_shot_cache(char **cache, struct stat *cache_stat) {
+        _cleanup_free_ char *fn = NULL, *v = NULL;
+        struct stat new_stat;
+        int r;
+
+        assert(cache);
+        assert(cache_stat);
+
+        fn = efi_variable_path(EFI_VENDOR_LOADER, "LoaderEntryOneShot");
+        if (!fn)
+                return -ENOMEM;
+
+        /* stat() the EFI variable, to see if the mtime changed. If it did we need to cache again. */
+        if (stat(fn, &new_stat) < 0)
+                return -errno;
+
+        if (stat_inode_unmodified(&new_stat, cache_stat))
+                return 0;
+
+        r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntryOneShot", &v);
+        if (r < 0)
+                return r;
+
+        if (!efi_loader_entry_name_valid(v))
+                return -EINVAL;
+
+        *cache_stat = new_stat;
+        free_and_replace(*cache, v);
+
+        return 0;
+}
+
 #endif
 
 bool efi_loader_entry_name_valid(const char *s) {
index 96208d25bf5ea053687d156e579063cf72164d64..171274a0e342edafdc7cf798c45584deab8f035d 100644 (file)
@@ -3,6 +3,8 @@
 
 #include "efivars.h"
 
+#include <sys/stat.h>
+
 #if ENABLE_EFI
 
 int efi_reboot_to_firmware_supported(void);
@@ -23,6 +25,9 @@ int efi_loader_get_entries(char ***ret);
 
 int efi_loader_get_features(uint64_t *ret);
 
+int efi_loader_get_config_timeout_one_shot(usec_t *ret);
+int efi_loader_update_entry_one_shot_cache(char **cache, struct stat *cache_stat);
+
 #else
 
 static inline int efi_reboot_to_firmware_supported(void) {
@@ -77,6 +82,14 @@ static inline int efi_loader_get_features(uint64_t *ret) {
         return -EOPNOTSUPP;
 }
 
+static inline int efi_loader_get_config_timeout_one_shot(usec_t *ret) {
+        return -EOPNOTSUPP;
+}
+
+static inline int efi_loader_update_entry_one_shot_cache(char **cache, struct stat *cache_stat) {
+        return -EOPNOTSUPP;
+}
+
 #endif
 
 bool efi_loader_entry_name_valid(const char *s);
index c985b5aea4f212ad5d8d54a0b8204ad395b725e7..3bb12f92257983071463aeb6c97a447c6fd92a9c 100644 (file)
@@ -441,6 +441,16 @@ int ethtool_set_nic_buffer_size(int *ethtool_fd, const char *ifname, netdev_ring
                 need_update = true;
         }
 
+        if (ring->rx_mini_pending_set && ecmd.rx_mini_pending != ring->rx_mini_pending) {
+                ecmd.rx_mini_pending = ring->rx_mini_pending;
+                need_update = true;
+        }
+
+        if (ring->rx_jumbo_pending_set && ecmd.rx_jumbo_pending != ring->rx_jumbo_pending) {
+                ecmd.rx_jumbo_pending = ring->rx_jumbo_pending;
+                need_update = true;
+        }
+
         if (ring->tx_pending_set && ecmd.tx_pending != ring->tx_pending) {
                 ecmd.tx_pending = ring->tx_pending;
                 need_update = true;
@@ -857,6 +867,55 @@ int ethtool_set_channels(int *fd, const char *ifname, netdev_channels *channels)
         return 0;
 }
 
+int ethtool_set_flow_control(int *fd, const char *ifname, int rx, int tx, int autoneg) {
+        struct ethtool_pauseparam ecmd = {
+                .cmd = ETHTOOL_GPAUSEPARAM
+        };
+        struct ifreq ifr = {
+                .ifr_data = (void*) &ecmd
+        };
+
+        bool need_update = false;
+        int r;
+
+        if (*fd < 0) {
+                r = ethtool_connect_or_warn(fd, true);
+                if (r < 0)
+                        return r;
+        }
+
+        strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
+
+        r = ioctl(*fd, SIOCETHTOOL, &ifr);
+        if (r < 0)
+                return -errno;
+
+        if (rx >= 0 && ecmd.rx_pause != (uint32_t) rx) {
+                ecmd.rx_pause = rx;
+                need_update = true;
+        }
+
+        if (tx >= 0 && ecmd.tx_pause != (uint32_t) tx) {
+                ecmd.tx_pause = tx;
+                need_update = true;
+        }
+
+        if (autoneg >= 0 && ecmd.autoneg != (uint32_t) autoneg) {
+                ecmd.autoneg = autoneg;
+                need_update = true;
+        }
+
+        if (need_update) {
+                ecmd.cmd = ETHTOOL_SPAUSEPARAM;
+
+                r = ioctl(*fd, SIOCETHTOOL, &ifr);
+                if (r < 0)
+                        return -errno;
+        }
+
+        return 0;
+}
+
 int config_parse_channel(const char *unit,
                          const char *filename,
                          unsigned line,
@@ -993,6 +1052,12 @@ int config_parse_nic_buffer_size(const char *unit,
         if (streq(lvalue, "RxBufferSize")) {
                 ring->rx_pending = k;
                 ring->rx_pending_set = true;
+        } else if (streq(lvalue, "RxMiniBufferSize")) {
+                ring->rx_mini_pending = k;
+                ring->rx_mini_pending_set = true;
+        } else if (streq(lvalue, "RxJumboBufferSize")) {
+                ring->rx_jumbo_pending = k;
+                ring->rx_jumbo_pending_set = true;
         } else if (streq(lvalue, "TxBufferSize")) {
                 ring->tx_pending = k;
                 ring->tx_pending_set = true;
index c1d5d7590ef9a2a777d8fa40d1922a0294a59922..47302417089874025badb0723cd47baa4cdde676 100644 (file)
@@ -84,9 +84,13 @@ typedef struct netdev_channels {
 
 typedef struct netdev_ring_param {
         uint32_t rx_pending;
+        uint32_t rx_mini_pending;
+        uint32_t rx_jumbo_pending;
         uint32_t tx_pending;
 
         bool rx_pending_set;
+        bool rx_mini_pending_set;
+        bool rx_jumbo_pending_set;
         bool tx_pending_set;
 } netdev_ring_param;
 
@@ -103,6 +107,7 @@ int ethtool_set_glinksettings(int *ethtool_fd, const char *ifname,
                               int autonegotiation, uint32_t advertise[static N_ADVERTISE],
                               uint64_t speed, Duplex duplex, NetDevPort port);
 int ethtool_set_channels(int *ethtool_fd, const char *ifname, netdev_channels *channels);
+int ethtool_set_flow_control(int *fd, const char *ifname, int rx, int tx, int autoneg);
 
 const char *duplex_to_string(Duplex d) _const_;
 Duplex duplex_from_string(const char *d) _pure_;
index 64aa2b68e9665d25c83e70fcc32db4707ef80ffc..87ef5c3f00c98172ea7d85ab3e4a3d4c086aaab3 100644 (file)
@@ -73,6 +73,7 @@ typedef struct TableData {
         bool uppercase;             /* Uppercase string on display */
 
         const char *color;          /* ANSI color string to use for this cell. When written to terminal should not move cursor. Will automatically be reset after the cell */
+        const char *rgap_color;     /* The ANSI color to use for the gap right of this cell. Usually used to underline entire rows in a gapless fashion */
         char *url;                  /* A URL to use for a clickable hyperlink */
         char *formatted;            /* A cached textual representation of the cell data, before ellipsation/alignment */
 
@@ -334,7 +335,7 @@ static bool table_data_matches(
                 return false;
 
         /* If a color/url/uppercase flag is set, refuse to merge */
-        if (d->color)
+        if (d->color || d->rgap_color)
                 return false;
         if (d->url)
                 return false;
@@ -542,6 +543,7 @@ static int table_dedup_cell(Table *t, TableCell *cell) {
                 return -ENOMEM;
 
         nd->color = od->color;
+        nd->rgap_color = od->rgap_color;
         nd->url = TAKE_PTR(curl);
         nd->uppercase = od->uppercase;
 
@@ -671,6 +673,20 @@ int table_set_color(Table *t, TableCell *cell, const char *color) {
         return 0;
 }
 
+int table_set_rgap_color(Table *t, TableCell *cell, const char *color) {
+        int r;
+
+        assert(t);
+        assert(cell);
+
+        r = table_dedup_cell(t, cell);
+        if (r < 0)
+                return r;
+
+        table_get_data(t, cell)->rgap_color = empty_to_null(color);
+        return 0;
+}
+
 int table_set_url(Table *t, TableCell *cell, const char *url) {
         _cleanup_free_ char *copy = NULL;
         int r;
@@ -744,6 +760,7 @@ int table_update(Table *t, TableCell *cell, TableDataType type, const void *data
                 return -ENOMEM;
 
         nd->color = od->color;
+        nd->rgap_color = od->rgap_color;
         nd->url = TAKE_PTR(curl);
         nd->uppercase = od->uppercase;
 
@@ -952,6 +969,25 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) {
                         break;
                 }
 
+                case TABLE_SET_RGAP_COLOR: {
+                        const char *c = va_arg(ap, const char*);
+                        r = table_set_rgap_color(t, last_cell, c);
+                        break;
+                }
+
+                case TABLE_SET_BOTH_COLORS: {
+                        const char *c = va_arg(ap, const char*);
+
+                        r = table_set_color(t, last_cell, c);
+                        if (r < 0) {
+                                va_end(ap);
+                                return r;
+                        }
+
+                        r = table_set_rgap_color(t, last_cell, c);
+                        break;
+                }
+
                 case TABLE_SET_URL: {
                         const char *u = va_arg(ap, const char*);
                         r = table_set_url(t, last_cell, u);
@@ -1241,7 +1277,7 @@ static int table_data_compare(const size_t *a, const size_t *b, Table *t) {
         return CMP(*a, *b);
 }
 
-static const char *table_data_format(Table *t, TableData *d) {
+static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercasing) {
         assert(d);
 
         if (d->formatted)
@@ -1253,7 +1289,7 @@ static const char *table_data_format(Table *t, TableData *d) {
 
         case TABLE_STRING:
         case TABLE_PATH:
-                if (d->uppercase) {
+                if (d->uppercase && !avoid_uppercasing) {
                         char *p, *q;
 
                         d->formatted = new(char, strlen(d->string) + 1);
@@ -1272,6 +1308,9 @@ static const char *table_data_format(Table *t, TableData *d) {
         case TABLE_STRV: {
                 char *p;
 
+                if (strv_isempty(d->strv))
+                        return strempty(t->empty_string);
+
                 p = strv_join(d->strv, "\n");
                 if (!p)
                         return NULL;
@@ -1602,7 +1641,7 @@ static int table_data_requested_width_height(
         const char *t;
         int r;
 
-        t = table_data_format(table, d);
+        t = table_data_format(table, d, false);
         if (!t)
                 return -ENOMEM;
 
@@ -1694,6 +1733,20 @@ static char *align_string_mem(const char *str, const char *url, size_t new_lengt
         return ret;
 }
 
+static bool table_data_isempty(TableData *d) {
+        assert(d);
+
+        if (d->type == TABLE_EMPTY)
+                return true;
+
+        /* Let's also consider an empty strv as truly empty. */
+        if (d->type == TABLE_STRV)
+                return strv_isempty(d->strv);
+
+        /* Note that an empty string we do not consider empty here! */
+        return false;
+}
+
 static const char* table_data_color(TableData *d) {
         assert(d);
 
@@ -1701,12 +1754,21 @@ static const char* table_data_color(TableData *d) {
                 return d->color;
 
         /* Let's implicitly color all "empty" cells in grey, in case an "empty_string" is set that is not empty */
-        if (d->type == TABLE_EMPTY)
+        if (table_data_isempty(d))
                 return ansi_grey();
 
         return NULL;
 }
 
+static const char* table_data_rgap_color(TableData *d) {
+        assert(d);
+
+        if (d->rgap_color)
+                return d->rgap_color;
+
+        return NULL;
+}
+
 int table_print(Table *t, FILE *f) {
         size_t n_rows, *minimum_width, *maximum_width, display_columns, *requested_width,
                 i, j, table_minimum_width, table_maximum_width, table_requested_width, table_effective_width,
@@ -1784,7 +1846,7 @@ int table_print(Table *t, FILE *f) {
                                  * ellipsis. Hence, let's figure out the last line, and account for its
                                  * length plus ellipsis. */
 
-                                field = table_data_format(t, d);
+                                field = table_data_format(t, d, false);
                                 if (!field)
                                         return -ENOMEM;
 
@@ -1958,18 +2020,19 @@ int table_print(Table *t, FILE *f) {
                         row = t->data + i * t->n_columns;
 
                 do {
+                        const char *gap_color = NULL;
                         more_sublines = false;
 
                         for (j = 0; j < display_columns; j++) {
                                 _cleanup_free_ char *buffer = NULL, *extracted = NULL;
                                 bool lines_truncated = false;
-                                const char *field;
+                                const char *field, *color = NULL;
                                 TableData *d;
                                 size_t l;
 
                                 assert_se(d = row[t->display_map ? t->display_map[j] : j]);
 
-                                field = table_data_format(t, d);
+                                field = table_data_format(t, d, false);
                                 if (!field)
                                         return -ENOMEM;
 
@@ -2001,7 +2064,7 @@ int table_print(Table *t, FILE *f) {
                                                 _cleanup_free_ char *padded = NULL;
 
                                                 /* We truncated more lines of this cell, let's add an
-                                                 * ellipsis. We first append it, but thta might make our
+                                                 * ellipsis. We first append it, but that might make our
                                                  * string grow above what we have space for, hence ellipsize
                                                  * right after. This will truncate the ellipsis and add a new
                                                  * one. */
@@ -2042,23 +2105,35 @@ int table_print(Table *t, FILE *f) {
                                         field = buffer;
                                 }
 
-                                if (row == t->data) /* underline header line fully, including the column separator */
-                                        fputs(ansi_underline(), f);
+                                if (colors_enabled()) {
+                                        if (gap_color)
+                                                fputs(gap_color, f);
+                                        else if (row == t->data) /* underline header line fully, including the column separator */
+                                                fputs(ansi_underline(), f);
+                                }
 
                                 if (j > 0)
-                                        fputc(' ', f); /* column separator */
+                                        fputc(' ', f); /* column separator left of cell */
+
+                                if (colors_enabled()) {
+                                        color = table_data_color(d);
 
-                                if (table_data_color(d) && colors_enabled()) {
-                                        if (row == t->data) /* first undo header underliner */
+                                        /* Undo gap color */
+                                        if (gap_color || (color && row == t->data))
                                                 fputs(ANSI_NORMAL, f);
 
-                                        fputs(table_data_color(d), f);
+                                        if (color)
+                                                fputs(color, f);
+                                        else if (gap_color && row == t->data) /* underline header line cell */
+                                                fputs(ansi_underline(), f);
                                 }
 
                                 fputs(field, f);
 
-                                if (colors_enabled() && (table_data_color(d) || row == t->data))
+                                if (colors_enabled() && (color || row == t->data))
                                         fputs(ANSI_NORMAL, f);
+
+                                gap_color = table_data_rgap_color(d);
                         }
 
                         fputc('\n', f);
@@ -2256,6 +2331,24 @@ static int table_data_to_json(TableData *d, JsonVariant **ret) {
         }
 }
 
+static char* string_to_json_field_name(const char *f) {
+        char *c, *x;
+
+        /* Tries to make a string more suitable as JSON field name. There are no strict rules defined what a
+         * field name can be hence this is a bit vague and black magic. Right now we only convert spaces to
+         * underscores and leave everything as is. */
+
+        c = strdup(f);
+        if (!c)
+                return NULL;
+
+        for (x = c; *x; x++)
+                if (isspace(*x))
+                        *x = '_';
+
+        return c;
+}
+
 int table_to_json(Table *t, JsonVariant **ret) {
         JsonVariant **rows = NULL, **elements = NULL;
         _cleanup_free_ size_t *sorted = NULL;
@@ -2298,11 +2391,27 @@ int table_to_json(Table *t, JsonVariant **ret) {
         }
 
         for (j = 0; j < display_columns; j++) {
+                _cleanup_free_ char *mangled = NULL;
+                const char *formatted;
                 TableData *d;
 
                 assert_se(d = t->data[t->display_map ? t->display_map[j] : j]);
 
-                r = table_data_to_json(d, elements + j*2);
+                /* Field names must be strings, hence format whatever we got here as a string first */
+                formatted = table_data_format(t, d, true);
+                if (!formatted) {
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                /* Arbitrary strings suck as field names, try to mangle them into something more suitable hence */
+                mangled = string_to_json_field_name(formatted);
+                if (!mangled) {
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                r = json_variant_new_string(elements + j*2, mangled);
                 if (r < 0)
                         goto finish;
         }
index 62f1ed740d90296afe0c2f5aa3423632232aae52..1851f1d14a22a56e01427763c3926255ead3072c 100644 (file)
@@ -47,6 +47,8 @@ typedef enum TableDataType {
         TABLE_SET_ALIGN_PERCENT,
         TABLE_SET_ELLIPSIZE_PERCENT,
         TABLE_SET_COLOR,
+        TABLE_SET_RGAP_COLOR,
+        TABLE_SET_BOTH_COLORS,
         TABLE_SET_URL,
         TABLE_SET_UPPERCASE,
 
@@ -89,6 +91,7 @@ int table_set_weight(Table *t, TableCell *cell, unsigned weight);
 int table_set_align_percent(Table *t, TableCell *cell, unsigned percent);
 int table_set_ellipsize_percent(Table *t, TableCell *cell, unsigned percent);
 int table_set_color(Table *t, TableCell *cell, const char *color);
+int table_set_rgap_color(Table *t, TableCell *cell, const char *color);
 int table_set_url(Table *t, TableCell *cell, const char *url);
 int table_set_uppercase(Table *t, TableCell *cell, bool b);
 
@@ -127,3 +130,9 @@ int table_print_json(Table *t, FILE *f, JsonFormatFlags json_flags);
 
 #define table_log_add_error(r) \
         log_error_errno(r, "Failed to add cell(s) to table: %m")
+
+#define table_log_print_error(r) \
+        log_error_errno(r, "Failed to print table: %m")
+
+#define table_log_sort_error(r) \
+        log_error_errno(r, "Failed to sort table: %m")
index b19127be09a73facb69ddadd74844e3a8b0f0154..806dda84754cab596bb9caf858b1c0ca0b537ade 100644 (file)
@@ -80,7 +80,7 @@ int fstab_is_mount_point(const char *mount) {
 }
 
 int fstab_filter_options(const char *opts, const char *names,
-                         const char **namefound, char **value, char **filtered) {
+                         const char **ret_namefound, char **ret_value, char **ret_filtered) {
         const char *name, *n = NULL, *x;
         _cleanup_strv_free_ char **stor = NULL;
         _cleanup_free_ char *v = NULL, **strv = NULL;
@@ -92,7 +92,7 @@ int fstab_filter_options(const char *opts, const char *names,
 
         /* If !value and !filtered, this function is not allowed to fail. */
 
-        if (!filtered) {
+        if (!ret_filtered) {
                 const char *word, *state;
                 size_t l;
 
@@ -108,7 +108,7 @@ int fstab_filter_options(const char *opts, const char *names,
                                 x = word + strlen(name);
                                 if (IN_SET(*x, '\0', '=', ',')) {
                                         n = name;
-                                        if (value) {
+                                        if (ret_value) {
                                                 free(v);
                                                 if (IN_SET(*x, '\0', ','))
                                                         v = NULL;
@@ -145,7 +145,7 @@ int fstab_filter_options(const char *opts, const char *names,
                 found:
                         /* Keep the last occurrence found */
                         n = name;
-                        if (value) {
+                        if (ret_value) {
                                 free(v);
                                 if (*x == '\0')
                                         v = NULL;
@@ -162,19 +162,19 @@ int fstab_filter_options(const char *opts, const char *names,
         }
 
 answer:
-        if (namefound)
-                *namefound = n;
-        if (filtered) {
+        if (ret_namefound)
+                *ret_namefound = n;
+        if (ret_filtered) {
                 char *f;
 
                 f = strv_join(strv, ",");
                 if (!f)
                         return -ENOMEM;
 
-                *filtered = f;
+                *ret_filtered = f;
         }
-        if (value)
-                *value = TAKE_PTR(v);
+        if (ret_value)
+                *ret_value = TAKE_PTR(v);
 
         return !!n;
 }
diff --git a/src/shared/geneve-util.c b/src/shared/geneve-util.c
new file mode 100644 (file)
index 0000000..fad01e2
--- /dev/null
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "geneve-util.h"
+#include "string-table.h"
+
+static const char* const geneve_df_table[_NETDEV_GENEVE_DF_MAX] = {
+        [NETDEV_GENEVE_DF_UNSET]   = "unset",
+        [NETDEV_GENEVE_DF_SET]     = "set",
+        [NETDEV_GENEVE_DF_INHERIT] = "inherit",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(geneve_df, GeneveDF);
diff --git a/src/shared/geneve-util.h b/src/shared/geneve-util.h
new file mode 100644 (file)
index 0000000..63c03ae
--- /dev/null
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <linux/if_link.h>
+
+#include "conf-parser.h"
+
+typedef enum GeneveDF {
+        NETDEV_GENEVE_DF_UNSET    = GENEVE_DF_UNSET,
+        NETDEV_GENEVE_DF_SET      = GENEVE_DF_SET,
+        NETDEV_GENEVE_DF_INHERIT  = GENEVE_DF_INHERIT,
+        _NETDEV_GENEVE_DF_MAX,
+        _NETDEV_GENEVE_DF_INVALID = -1,
+} GeneveDF;
+
+const char *geneve_df_to_string(GeneveDF d) _const_;
+GeneveDF geneve_df_from_string(const char *d) _pure_;
index 77924f1c4067e077f9af65ef510d10726968d701..5c4fae865aefa0700d038598819c4d1b0f0f7a48 100644 (file)
@@ -106,12 +106,16 @@ int nss_sgrp_for_group(const struct group *grp, struct sgrp *ret_sgrp, char **re
         }
 }
 
-int nss_group_record_by_name(const char *name, GroupRecord **ret) {
+int nss_group_record_by_name(
+                const char *name,
+                bool with_shadow,
+                GroupRecord **ret) {
+
         _cleanup_free_ char *buf = NULL, *sbuf = NULL;
         struct group grp, *result;
         bool incomplete = false;
         size_t buflen = 4096;
-        struct sgrp sgrp;
+        struct sgrp sgrp, *sresult = NULL;
         int r;
 
         assert(name);
@@ -141,13 +145,17 @@ int nss_group_record_by_name(const char *name, GroupRecord **ret) {
                 buf = mfree(buf);
         }
 
-        r = nss_sgrp_for_group(result, &sgrp, &sbuf);
-        if (r < 0) {
-                log_debug_errno(r, "Failed to do shadow lookup for group %s, ignoring: %m", result->gr_name);
-                incomplete = ERRNO_IS_PRIVILEGE(r);
-        }
-
-        r = nss_group_to_group_record(result, r >= 0 ? &sgrp : NULL, ret);
+        if (with_shadow) {
+                r = nss_sgrp_for_group(result, &sgrp, &sbuf);
+                if (r < 0) {
+                        log_debug_errno(r, "Failed to do shadow lookup for group %s, ignoring: %m", result->gr_name);
+                        incomplete = ERRNO_IS_PRIVILEGE(r);
+                } else
+                        sresult = &sgrp;
+        } else
+                incomplete = true;
+
+        r = nss_group_to_group_record(result, sresult, ret);
         if (r < 0)
                 return r;
 
@@ -155,12 +163,16 @@ int nss_group_record_by_name(const char *name, GroupRecord **ret) {
         return 0;
 }
 
-int nss_group_record_by_gid(gid_t gid, GroupRecord **ret) {
+int nss_group_record_by_gid(
+                gid_t gid,
+                bool with_shadow,
+                GroupRecord **ret) {
+
         _cleanup_free_ char *buf = NULL, *sbuf = NULL;
         struct group grp, *result;
         bool incomplete = false;
         size_t buflen = 4096;
-        struct sgrp sgrp;
+        struct sgrp sgrp, *sresult = NULL;
         int r;
 
         assert(ret);
@@ -188,13 +200,17 @@ int nss_group_record_by_gid(gid_t gid, GroupRecord **ret) {
                 buf = mfree(buf);
         }
 
-        r = nss_sgrp_for_group(result, &sgrp, &sbuf);
-        if (r < 0) {
-                log_debug_errno(r, "Failed to do shadow lookup for group %s, ignoring: %m", result->gr_name);
-                incomplete = ERRNO_IS_PRIVILEGE(r);
-        }
-
-        r = nss_group_to_group_record(result, r >= 0 ? &sgrp : NULL, ret);
+        if (with_shadow) {
+                r = nss_sgrp_for_group(result, &sgrp, &sbuf);
+                if (r < 0) {
+                        log_debug_errno(r, "Failed to do shadow lookup for group %s, ignoring: %m", result->gr_name);
+                        incomplete = ERRNO_IS_PRIVILEGE(r);
+                } else
+                        sresult = &sgrp;
+        } else
+                incomplete = true;
+
+        r = nss_group_to_group_record(result, sresult, ret);
         if (r < 0)
                 return r;
 
index 38b2995178ff7d90629e829e62d35698d49fd9fb..077c22d89f12b4a65476a8e7f9fe2a55b60a1a61 100644 (file)
@@ -11,5 +11,5 @@
 int nss_group_to_group_record(const struct group *grp, const struct sgrp *sgrp, GroupRecord **ret);
 int nss_sgrp_for_group(const struct group *grp, struct sgrp *ret_sgrp, char **ret_buffer);
 
-int nss_group_record_by_name(const char *name, GroupRecord **ret);
-int nss_group_record_by_gid(gid_t gid, GroupRecord **ret);
+int nss_group_record_by_name(const char *name, bool with_shadow, GroupRecord **ret);
+int nss_group_record_by_gid(gid_t gid, bool with_shadow, GroupRecord **ret);
index 2061384afe30d0e589d9121c3ba4de7a9f55dc5f..9267d52b96e993f0a9501808a35a3fbf58ec07db 100644 (file)
@@ -115,7 +115,7 @@ int install_full_printf(const UnitFileInstallInfo *i, const char *format, char *
          * %U the UID of the running user
          * %u the username of running user
          * %m the machine ID of the running system
-         * %H the host name of the running system
+         * %H the hostname of the running system
          * %b the boot ID of the running system
          * %v `uname -r` of the running system
          */
@@ -133,9 +133,14 @@ int install_full_printf(const UnitFileInstallInfo *i, const char *format, char *
                 { 'u', specifier_user_name,           NULL },
 
                 { 'm', specifier_machine_id,          NULL },
-                { 'H', specifier_host_name,           NULL },
                 { 'b', specifier_boot_id,             NULL },
+                { 'H', specifier_host_name,           NULL },
                 { 'v', specifier_kernel_release,      NULL },
+                { 'a', specifier_architecture,        NULL },
+                { 'o', specifier_os_id,               NULL },
+                { 'w', specifier_os_version_id,       NULL },
+                { 'B', specifier_os_build_id,         NULL },
+                { 'W', specifier_os_variant_id,       NULL },
                 {}
         };
 
index 78532573c43314ab0ce0d88b3fb8affa245d109e..3d19387504f19008084c1441fbe06aedc7bd05e4 100644 (file)
@@ -55,16 +55,11 @@ typedef enum {
         PRESET_DISABLE,
 } PresetAction;
 
-typedef struct {
+struct UnitFilePresetRule {
         char *pattern;
         PresetAction action;
         char **instances;
-} PresetRule;
-
-typedef struct {
-        PresetRule *rules;
-        size_t n_rules;
-} Presets;
+};
 
 static bool unit_file_install_info_has_rules(const UnitFileInstallInfo *i) {
         assert(i);
@@ -80,7 +75,7 @@ static bool unit_file_install_info_has_also(const UnitFileInstallInfo *i) {
         return !strv_isempty(i->also);
 }
 
-static void presets_freep(Presets *p) {
+void unit_file_presets_freep(UnitFilePresets *p) {
         size_t i;
 
         if (!p)
@@ -259,7 +254,7 @@ static int path_is_vendor_or_generator(const LookupPaths *p, const char *path) {
 int unit_file_changes_add(
                 UnitFileChange **changes,
                 size_t *n_changes,
-                UnitFileChangeType type,
+                int type,
                 const char *path,
                 const char *source) {
 
@@ -1241,8 +1236,7 @@ static int unit_file_load(
                                                "%s: unit type %s cannot be templated, ignoring.", path, unit_type_to_string(type));
 
                 if (!(flags & SEARCH_LOAD)) {
-                        r = lstat(path, &st);
-                        if (r < 0)
+                        if (lstat(path, &st) < 0)
                                 return -errno;
 
                         if (null_or_empty(&st))
@@ -1287,10 +1281,9 @@ static int unit_file_load(
         if (r < 0)
                 return r;
 
-        f = fdopen(fd, "r");
+        f = take_fdopen(&fd, "r");
         if (!f)
                 return -errno;
-        fd = -1;
 
         /* c is only needed if we actually load the file (it's referenced from items[] btw, in case you wonder.) */
         assert(c);
@@ -1310,7 +1303,8 @@ static int unit_file_load(
                          "-Target\0"
                          "-Timer\0",
                          config_item_table_lookup, items,
-                         CONFIG_PARSE_ALLOW_INCLUDE, info);
+                         0, info,
+                         NULL);
         if (r < 0)
                 return log_debug_errno(r, "Failed to parse %s: %m", info->name);
 
@@ -1329,26 +1323,40 @@ static int unit_file_load_or_readlink(
                 const char *path,
                 const char *root_dir,
                 SearchFlags flags) {
-
-        _cleanup_free_ char *target = NULL;
+        _cleanup_free_ char *resolved = NULL;
+        struct stat st;
         int r;
 
         r = unit_file_load(c, info, path, root_dir, flags);
         if (r != -ELOOP || (flags & SEARCH_DROPIN))
                 return r;
 
-        /* This is a symlink, let's read it. */
+        r = chase_symlinks(path, root_dir, CHASE_WARN | CHASE_NONEXISTENT, &resolved, NULL);
+        if (r >= 0 &&
+            root_dir &&
+            path_equal_ptr(path_startswith(resolved, root_dir), "dev/null"))
+                /* When looking under root_dir, we can't expect /dev/ to be mounted,
+                 * so let's see if the path is a (possibly dangling) symlink to /dev/null. */
+                info->type = UNIT_FILE_TYPE_MASKED;
 
-        r = readlink_malloc(path, &target);
-        if (r < 0)
-                return r;
+        else if (r > 0 &&
+                 stat(resolved, &st) >= 0 &&
+                 null_or_empty(&st))
 
-        if (path_equal(target, "/dev/null"))
                 info->type = UNIT_FILE_TYPE_MASKED;
+
         else {
+                _cleanup_free_ char *target = NULL;
                 const char *bn;
                 UnitType a, b;
 
+                /* This is a symlink, let's read it. We read the link again, because last time
+                 * we followed the link until resolution, and here we need to do one step. */
+
+                r = readlink_malloc(path, &target);
+                if (r < 0)
+                        return r;
+
                 bn = basename(target);
 
                 if (unit_name_is_valid(info->name, UNIT_NAME_PLAIN)) {
@@ -1719,7 +1727,7 @@ int unit_file_verify_alias(const UnitFileInstallInfo *i, const char *dst, char *
          *
          * ret_dst is set in cases where "instance propagation" happens, i.e. when the instance part is
          * inserted into dst. It is not normally set, even on success, so that the caller can easily
-         * distinguish the case where instance propagation occured.
+         * distinguish the case where instance propagation occurred.
          */
 
         const char *path_alias = strrchr(dst, '/');
@@ -2780,6 +2788,12 @@ int unit_file_lookup_state(
                 break;
 
         case UNIT_FILE_TYPE_REGULAR:
+                /* Check if the name we were querying is actually an alias */
+                if (!streq(name, basename(i->path)) && !unit_name_is_valid(i->name, UNIT_NAME_INSTANCE)) {
+                        state = UNIT_FILE_ALIAS;
+                        break;
+                }
+
                 r = path_is_generator(paths, i->path);
                 if (r < 0)
                         return r;
@@ -2921,8 +2935,8 @@ static int presets_find_config(UnitFileScope scope, const char *root_dir, char *
         return conf_files_list_strv(files, ".preset", root_dir, 0, dirs);
 }
 
-static int read_presets(UnitFileScope scope, const char *root_dir, Presets *presets) {
-        _cleanup_(presets_freep) Presets ps = {};
+static int read_presets(UnitFileScope scope, const char *root_dir, UnitFilePresets *presets) {
+        _cleanup_(unit_file_presets_freep) UnitFilePresets ps = {};
         size_t n_allocated = 0;
         _cleanup_strv_free_ char **files = NULL;
         char **p;
@@ -2950,7 +2964,7 @@ static int read_presets(UnitFileScope scope, const char *root_dir, Presets *pres
 
                 for (;;) {
                         _cleanup_free_ char *line = NULL;
-                        PresetRule rule = {};
+                        UnitFilePresetRule rule = {};
                         const char *parameter;
                         char *l;
 
@@ -2980,7 +2994,7 @@ static int read_presets(UnitFileScope scope, const char *root_dir, Presets *pres
                                         continue;
                                 }
 
-                                rule = (PresetRule) {
+                                rule = (UnitFilePresetRule) {
                                         .pattern = unit_name,
                                         .action = PRESET_ENABLE,
                                         .instances = instances,
@@ -2995,7 +3009,7 @@ static int read_presets(UnitFileScope scope, const char *root_dir, Presets *pres
                                 if (!pattern)
                                         return -ENOMEM;
 
-                                rule = (PresetRule) {
+                                rule = (UnitFilePresetRule) {
                                         .pattern = pattern,
                                         .action = PRESET_DISABLE,
                                 };
@@ -3013,14 +3027,15 @@ static int read_presets(UnitFileScope scope, const char *root_dir, Presets *pres
                 }
         }
 
+        ps.initialized = true;
         *presets = ps;
-        ps = (Presets){};
+        ps = (UnitFilePresets){};
 
         return 0;
 }
 
 static int pattern_match_multiple_instances(
-                        const PresetRule rule,
+                        const UnitFilePresetRule rule,
                         const char *unit_name,
                         char ***ret) {
 
@@ -3074,17 +3089,17 @@ static int pattern_match_multiple_instances(
         return 0;
 }
 
-static int query_presets(const char *name, const Presets presets, char ***instance_name_list) {
+static int query_presets(const char *name, const UnitFilePresets *presets, char ***instance_name_list) {
         PresetAction action = PRESET_UNKNOWN;
         size_t i;
         char **s;
         if (!unit_name_is_valid(name, UNIT_NAME_ANY))
                 return -EINVAL;
 
-        for (i = 0; i < presets.n_rules; i++)
-                if (pattern_match_multiple_instances(presets.rules[i], name, instance_name_list) > 0 ||
-                    fnmatch(presets.rules[i].pattern, name, FNM_NOESCAPE) == 0) {
-                        action = presets.rules[i].action;
+        for (i = 0; i < presets->n_rules; i++)
+                if (pattern_match_multiple_instances(presets->rules[i], name, instance_name_list) > 0 ||
+                    fnmatch(presets->rules[i].pattern, name, FNM_NOESCAPE) == 0) {
+                        action = presets->rules[i].action;
                         break;
                 }
 
@@ -3107,15 +3122,19 @@ static int query_presets(const char *name, const Presets presets, char ***instan
         }
 }
 
-int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name) {
-        _cleanup_(presets_freep) Presets presets = {};
+int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name, UnitFilePresets *cached) {
+        _cleanup_(unit_file_presets_freep) UnitFilePresets tmp = {};
         int r;
 
-        r = read_presets(scope, root_dir, &presets);
-        if (r < 0)
-                return r;
+        if (!cached)
+                cached = &tmp;
+        if (!cached->initialized) {
+                r = read_presets(scope, root_dir, cached);
+                if (r < 0)
+                        return r;
+        }
 
-        return query_presets(name, presets, NULL);
+        return query_presets(name, cached, NULL);
 }
 
 static int execute_preset(
@@ -3170,7 +3189,7 @@ static int preset_prepare_one(
                 InstallContext *minus,
                 LookupPaths *paths,
                 const char *name,
-                Presets presets,
+                const UnitFilePresets *presets,
                 UnitFileChange **changes,
                 size_t *n_changes) {
 
@@ -3229,7 +3248,7 @@ int unit_file_preset(
 
         _cleanup_(install_context_done) InstallContext plus = {}, minus = {};
         _cleanup_(lookup_paths_free) LookupPaths paths = {};
-        _cleanup_(presets_freep) Presets presets = {};
+        _cleanup_(unit_file_presets_freep) UnitFilePresets presets = {};
         const char *config_path;
         char **i;
         int r;
@@ -3251,7 +3270,7 @@ int unit_file_preset(
                 return r;
 
         STRV_FOREACH(i, files) {
-                r = preset_prepare_one(scope, &plus, &minus, &paths, *i, presets, changes, n_changes);
+                r = preset_prepare_one(scope, &plus, &minus, &paths, *i, &presets, changes, n_changes);
                 if (r < 0)
                         return r;
         }
@@ -3269,7 +3288,7 @@ int unit_file_preset_all(
 
         _cleanup_(install_context_done) InstallContext plus = {}, minus = {};
         _cleanup_(lookup_paths_free) LookupPaths paths = {};
-        _cleanup_(presets_freep) Presets presets = {};
+        _cleanup_(unit_file_presets_freep) UnitFilePresets presets = {};
         const char *config_path = NULL;
         char **i;
         int r;
@@ -3313,7 +3332,7 @@ int unit_file_preset_all(
                                 continue;
 
                         /* we don't pass changes[] in, because we want to handle errors on our own */
-                        r = preset_prepare_one(scope, &plus, &minus, &paths, de->d_name, presets, NULL, 0);
+                        r = preset_prepare_one(scope, &plus, &minus, &paths, de->d_name, &presets, NULL, 0);
                         if (r == -ERFKILL)
                                 r = unit_file_changes_add(changes, n_changes,
                                                           UNIT_FILE_IS_MASKED, de->d_name, NULL);
@@ -3352,7 +3371,7 @@ int unit_file_get_list(
                 char **patterns) {
 
         _cleanup_(lookup_paths_free) LookupPaths paths = {};
-        char **i;
+        char **dirname;
         int r;
 
         assert(scope >= 0);
@@ -3363,16 +3382,16 @@ int unit_file_get_list(
         if (r < 0)
                 return r;
 
-        STRV_FOREACH(i, paths.search_path) {
+        STRV_FOREACH(dirname, paths.search_path) {
                 _cleanup_closedir_ DIR *d = NULL;
                 struct dirent *de;
 
-                d = opendir(*i);
+                d = opendir(*dirname);
                 if (!d) {
                         if (errno == ENOENT)
                                 continue;
                         if (IN_SET(errno, ENOTDIR, EACCES)) {
-                                log_debug_errno(errno, "Failed to open \"%s\": %m", *i);
+                                log_debug_errno(errno, "Failed to open \"%s\": %m", *dirname);
                                 continue;
                         }
 
@@ -3400,7 +3419,7 @@ int unit_file_get_list(
                         if (!f)
                                 return -ENOMEM;
 
-                        f->path = path_make_absolute(de->d_name, *i);
+                        f->path = path_make_absolute(de->d_name, *dirname);
                         if (!f->path)
                                 return -ENOMEM;
 
@@ -3424,34 +3443,35 @@ int unit_file_get_list(
 }
 
 static const char* const unit_file_state_table[_UNIT_FILE_STATE_MAX] = {
-        [UNIT_FILE_ENABLED] = "enabled",
+        [UNIT_FILE_ENABLED]         = "enabled",
         [UNIT_FILE_ENABLED_RUNTIME] = "enabled-runtime",
-        [UNIT_FILE_LINKED] = "linked",
-        [UNIT_FILE_LINKED_RUNTIME] = "linked-runtime",
-        [UNIT_FILE_MASKED] = "masked",
-        [UNIT_FILE_MASKED_RUNTIME] = "masked-runtime",
-        [UNIT_FILE_STATIC] = "static",
-        [UNIT_FILE_DISABLED] = "disabled",
-        [UNIT_FILE_INDIRECT] = "indirect",
-        [UNIT_FILE_GENERATED] = "generated",
-        [UNIT_FILE_TRANSIENT] = "transient",
-        [UNIT_FILE_BAD] = "bad",
+        [UNIT_FILE_LINKED]          = "linked",
+        [UNIT_FILE_LINKED_RUNTIME]  = "linked-runtime",
+        [UNIT_FILE_ALIAS]           = "alias",
+        [UNIT_FILE_MASKED]          = "masked",
+        [UNIT_FILE_MASKED_RUNTIME]  = "masked-runtime",
+        [UNIT_FILE_STATIC]          = "static",
+        [UNIT_FILE_DISABLED]        = "disabled",
+        [UNIT_FILE_INDIRECT]        = "indirect",
+        [UNIT_FILE_GENERATED]       = "generated",
+        [UNIT_FILE_TRANSIENT]       = "transient",
+        [UNIT_FILE_BAD]             = "bad",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(unit_file_state, UnitFileState);
 
 static const char* const unit_file_change_type_table[_UNIT_FILE_CHANGE_TYPE_MAX] = {
-        [UNIT_FILE_SYMLINK] = "symlink",
-        [UNIT_FILE_UNLINK] = "unlink",
-        [UNIT_FILE_IS_MASKED] = "masked",
+        [UNIT_FILE_SYMLINK]     = "symlink",
+        [UNIT_FILE_UNLINK]      = "unlink",
+        [UNIT_FILE_IS_MASKED]   = "masked",
         [UNIT_FILE_IS_DANGLING] = "dangling",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(unit_file_change_type, UnitFileChangeType);
 
 static const char* const unit_file_preset_mode_table[_UNIT_FILE_PRESET_MAX] = {
-        [UNIT_FILE_PRESET_FULL] = "full",
-        [UNIT_FILE_PRESET_ENABLE_ONLY] = "enable-only",
+        [UNIT_FILE_PRESET_FULL]         = "full",
+        [UNIT_FILE_PRESET_ENABLE_ONLY]  = "enable-only",
         [UNIT_FILE_PRESET_DISABLE_ONLY] = "disable-only",
 };
 
index 54d22a45d3476f428efdaa49eea41d674c117478..788517d23e91b4f0f19b01b047d606d7691caf56 100644 (file)
@@ -183,13 +183,22 @@ int unit_file_exists(UnitFileScope scope, const LookupPaths *paths, const char *
 int unit_file_get_list(UnitFileScope scope, const char *root_dir, Hashmap *h, char **states, char **patterns);
 Hashmap* unit_file_list_free(Hashmap *h);
 
-int unit_file_changes_add(UnitFileChange **changes, size_t *n_changes, UnitFileChangeType type, const char *path, const char *source);
+int unit_file_changes_add(UnitFileChange **changes, size_t *n_changes, int type, const char *path, const char *source);
 void unit_file_changes_free(UnitFileChange *changes, size_t n_changes);
 void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *changes, size_t n_changes, bool quiet);
 
 int unit_file_verify_alias(const UnitFileInstallInfo *i, const char *dst, char **ret_dst);
 
-int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name);
+typedef struct UnitFilePresetRule UnitFilePresetRule;
+
+typedef struct {
+        UnitFilePresetRule *rules;
+        size_t n_rules;
+        bool initialized;
+} UnitFilePresets;
+
+void unit_file_presets_freep(UnitFilePresets *p);
+int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name, UnitFilePresets *cached);
 
 const char *unit_file_state_to_string(UnitFileState s) _const_;
 UnitFileState unit_file_state_from_string(const char *s) _pure_;
diff --git a/src/shared/ipvlan-util.c b/src/shared/ipvlan-util.c
new file mode 100644 (file)
index 0000000..da6be76
--- /dev/null
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <net/if.h>
+
+#include "ipvlan-util.h"
+#include "string-table.h"
+
+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);
+
+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);
diff --git a/src/shared/ipvlan-util.h b/src/shared/ipvlan-util.h
new file mode 100644 (file)
index 0000000..dda659d
--- /dev/null
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <netinet/in.h>
+#include <linux/if_link.h>
+
+#include "macro.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;
+
+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_;
index 7a79acdee8b96119d357df85ec171fb0ad4f231b..27a3a518fef4270a754ef909369202e7f0cab095 100644 (file)
@@ -253,10 +253,9 @@ static JsonVariant *json_variant_formalize(JsonVariant *v) {
                 return json_variant_unsigned(v) == 0 ? JSON_VARIANT_MAGIC_ZERO_UNSIGNED : v;
 
         case JSON_VARIANT_REAL:
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
+                DISABLE_WARNING_FLOAT_EQUAL;
                 return json_variant_real(v) == 0.0 ? JSON_VARIANT_MAGIC_ZERO_REAL : v;
-#pragma GCC diagnostic pop
+                REENABLE_WARNING;
 
         case JSON_VARIANT_STRING:
                 return isempty(json_variant_string(v)) ? JSON_VARIANT_MAGIC_EMPTY_STRING : v;
@@ -353,13 +352,12 @@ int json_variant_new_real(JsonVariant **ret, long double d) {
 
         assert_return(ret, -EINVAL);
 
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
+        DISABLE_WARNING_FLOAT_EQUAL;
         if (d == 0.0) {
-#pragma GCC diagnostic pop
                 *ret = JSON_VARIANT_MAGIC_ZERO_REAL;
                 return 0;
         }
+        REENABLE_WARNING;
 
         r = json_variant_new(&v, JSON_VARIANT_REAL, sizeof(d));
         if (r < 0)
@@ -896,11 +894,10 @@ intmax_t json_variant_integer(JsonVariant *v) {
 
                 converted = (intmax_t) v->value.real;
 
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
+                DISABLE_WARNING_FLOAT_EQUAL;
                 if ((long double) converted == v->value.real)
-#pragma GCC diagnostic pop
                         return converted;
+                REENABLE_WARNING;
 
                 log_debug("Real %Lg requested as integer, and cannot be converted losslessly, returning 0.", v->value.real);
                 return 0;
@@ -944,11 +941,10 @@ uintmax_t json_variant_unsigned(JsonVariant *v) {
 
                 converted = (uintmax_t) v->value.real;
 
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
+                DISABLE_WARNING_FLOAT_EQUAL;
                 if ((long double) converted == v->value.real)
-#pragma GCC diagnostic pop
                         return converted;
+                REENABLE_WARNING;
 
                 log_debug("Real %Lg requested as unsigned integer, and cannot be converted losslessly, returning 0.", v->value.real);
                 return 0;
@@ -1097,9 +1093,12 @@ JsonVariantType json_variant_type(JsonVariant *v) {
         return v->type;
 }
 
-bool json_variant_has_type(JsonVariant *v, JsonVariantType type) {
+_function_no_sanitize_float_cast_overflow_ bool json_variant_has_type(JsonVariant *v, JsonVariantType type) {
         JsonVariantType rt;
 
+        /* Note: we turn off ubsan float cast overflo detection for this function, since it would complain
+         * about our float casts but we do them explicitly to detect conversion errors. */
+
         v = json_variant_dereference(v);
         if (!v)
                 return false;
@@ -1137,14 +1136,15 @@ bool json_variant_has_type(JsonVariant *v, JsonVariantType type) {
         if (rt == JSON_VARIANT_UNSIGNED && type == JSON_VARIANT_REAL)
                 return (uintmax_t) (long double) v->value.unsig == v->value.unsig;
 
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
+        DISABLE_WARNING_FLOAT_EQUAL;
+
         /* Any real that can be converted losslessly to an integer and back may also be considered an integer */
         if (rt == JSON_VARIANT_REAL && type == JSON_VARIANT_INTEGER)
                 return (long double) (intmax_t) v->value.real == v->value.real;
         if (rt == JSON_VARIANT_REAL && type == JSON_VARIANT_UNSIGNED)
                 return (long double) (uintmax_t) v->value.real == v->value.real;
-#pragma GCC diagnostic pop
+
+        REENABLE_WARNING;
 
         return false;
 }
@@ -1298,10 +1298,9 @@ bool json_variant_equal(JsonVariant *a, JsonVariant *b) {
                 return json_variant_unsigned(a) == json_variant_unsigned(b);
 
         case JSON_VARIANT_REAL:
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
+                DISABLE_WARNING_FLOAT_EQUAL;
                 return json_variant_real(a) == json_variant_real(b);
-#pragma GCC diagnostic pop
+                REENABLE_WARNING;
 
         case JSON_VARIANT_BOOLEAN:
                 return json_variant_boolean(a) == json_variant_boolean(b);
@@ -1381,7 +1380,7 @@ void json_variant_sensitive(JsonVariant *v) {
 
         /* Marks a variant as "sensitive", so that it is erased from memory when it is destroyed. This is a
          * one-way operation: as soon as it is marked this way it remains marked this way until it's
-         * destoryed. A magic variant is never sensitive though, even when asked, since it's too
+         * destroyed. A magic variant is never sensitive though, even when asked, since it's too
          * basic. Similar, const string variant are never sensitive either, after all they are included in
          * the source code as they are, which is not suitable for inclusion of secrets.
          *
@@ -1396,6 +1395,19 @@ void json_variant_sensitive(JsonVariant *v) {
         v->sensitive = true;
 }
 
+bool json_variant_is_sensitive(JsonVariant *v) {
+        v = json_variant_formalize(v);
+        if (!json_variant_is_regular(v))
+                return false;
+
+        return v->sensitive;
+}
+
+static void json_variant_propagate_sensitive(JsonVariant *from, JsonVariant *to) {
+        if (json_variant_is_sensitive(from))
+                json_variant_sensitive(to);
+}
+
 int json_variant_get_source(JsonVariant *v, const char **ret_source, unsigned *ret_line, unsigned *ret_column) {
         assert_return(v, -EINVAL);
 
@@ -1829,6 +1841,8 @@ int json_variant_filter(JsonVariant **v, char **to_remove) {
         if (r < 0)
                 return r;
 
+        json_variant_propagate_sensitive(*v, w);
+
         json_variant_unref(*v);
         *v = TAKE_PTR(w);
 
@@ -1898,6 +1912,8 @@ int json_variant_set_field(JsonVariant **v, const char *field, JsonVariant *valu
         if (r < 0)
                 return r;
 
+        json_variant_propagate_sensitive(*v, w);
+
         json_variant_unref(*v);
         *v = TAKE_PTR(w);
 
@@ -2005,6 +2021,9 @@ int json_variant_merge(JsonVariant **v, JsonVariant *m) {
         if (r < 0)
                 return r;
 
+        json_variant_propagate_sensitive(*v, w);
+        json_variant_propagate_sensitive(m, w);
+
         json_variant_unref(*v);
         *v = TAKE_PTR(w);
 
@@ -2044,10 +2063,11 @@ int json_variant_append_array(JsonVariant **v, JsonVariant *element) {
 
                 r = json_variant_new_array(&nv, array, i + 1);
         }
-
         if (r < 0)
                 return r;
 
+        json_variant_propagate_sensitive(*v, nv);
+
         json_variant_unref(*v);
         *v = TAKE_PTR(nv);
 
@@ -2193,6 +2213,8 @@ static int json_variant_copy(JsonVariant **nv, JsonVariant *v) {
 
         memcpy_safe(&c->value, source, k);
 
+        json_variant_propagate_sensitive(v, c);
+
         *nv = c;
         return 0;
 }
@@ -4072,10 +4094,9 @@ int json_dispatch_uid_gid(const char *name, JsonVariant *variant, JsonDispatchFl
         assert_cc(sizeof(uid_t) == sizeof(uint32_t));
         assert_cc(sizeof(gid_t) == sizeof(uint32_t));
 
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wtype-limits"
+        DISABLE_WARNING_TYPE_LIMITS;
         assert_cc(((uid_t) -1 < (uid_t) 0) == ((gid_t) -1 < (gid_t) 0));
-#pragma GCC diagnostic pop
+        REENABLE_WARNING;
 
         if (json_variant_is_null(variant)) {
                 *uid = UID_INVALID;
@@ -4178,6 +4199,9 @@ int json_variant_sort(JsonVariant **v) {
         r = json_variant_new_object(&n, a, m);
         if (r < 0)
                 return r;
+
+        json_variant_propagate_sensitive(*v, n);
+
         if (!n->sorted) /* Check if this worked. This will fail if there are multiple identical keys used. */
                 return -ENOTUNIQ;
 
@@ -4226,6 +4250,9 @@ int json_variant_normalize(JsonVariant **v) {
         }
         if (r < 0)
                 goto finish;
+
+        json_variant_propagate_sensitive(*v, n);
+
         if (!n->normalized) { /* Let's see if normalization worked. It will fail if there are multiple
                                * identical keys used in the same object anywhere, or if there are floating
                                * point numbers used (see below) */
index a4e5b6f507bae7af81c858baa858e4dbe93ef5c8..ceb01a2028a461838bddfceb7f6424eb48d7edb9 100644 (file)
@@ -135,6 +135,7 @@ JsonVariant *json_variant_by_key_full(JsonVariant *v, const char *key, JsonVaria
 bool json_variant_equal(JsonVariant *a, JsonVariant *b);
 
 void json_variant_sensitive(JsonVariant *v);
+bool json_variant_is_sensitive(JsonVariant *v);
 
 struct json_variant_foreach_state {
         JsonVariant *variant;
index beee59c831a735042a94521b40eb41840363e35b..b065c1fe4df5d5d71da9c204e97c0c5bc142c57b 100644 (file)
@@ -1435,7 +1435,7 @@ enum nl80211_commands {
  *     rates as defined by IEEE 802.11 7.3.2.2 but without the length
  *     restriction (at most %NL80211_MAX_SUPP_RATES).
  * @NL80211_ATTR_STA_VLAN: interface index of VLAN interface to move station
- *     to, or the AP interface the station was originally added to to.
+ *     to, or the AP interface the station was originally added to.
  * @NL80211_ATTR_STA_INFO: information about a station, part of station info
  *     given for %NL80211_CMD_GET_STATION, nested attribute containing
  *     info as possible, see &enum nl80211_sta_info.
index 2bfd0b60c26b1198eb1802ec91ab8c52123a5b1e..899e894ab7e9fad318710aef15bcdc893849acfb 100644 (file)
@@ -22,6 +22,7 @@
 #include "journal-internal.h"
 #include "journal-util.h"
 #include "json.h"
+#include "locale-util.h"
 #include "log.h"
 #include "logs-show.h"
 #include "macro.h"
@@ -39,6 +40,7 @@
 #include "time-util.h"
 #include "utf8.h"
 #include "util.h"
+#include "web-util.h"
 
 /* up to three lines (each up to 100 characters) or 300 characters, whichever is less */
 #define PRINT_LINE_THRESHOLD 3
 #define JSON_THRESHOLD 4096U
 
 static int print_catalog(FILE *f, sd_journal *j) {
-        int r;
         _cleanup_free_ char *t = NULL, *z = NULL;
+        const char *newline, *prefix;
+        int r;
+
+        assert(j);
 
         r = sd_journal_get_catalog(j, &t);
+        if (r == -ENOENT)
+                return 0;
         if (r < 0)
-                return r;
+                return log_error_errno(r, "Failed to find catalog entry: %m");
 
-        z = strreplace(strstrip(t), "\n", "\n-- ");
+        if (is_locale_utf8())
+                prefix = strjoina(special_glyph(SPECIAL_GLYPH_LIGHT_SHADE), special_glyph(SPECIAL_GLYPH_LIGHT_SHADE));
+        else
+                prefix = "--";
+
+        if (colors_enabled())
+                newline = strjoina(ANSI_NORMAL "\n" ANSI_GREY, prefix, ANSI_NORMAL " " ANSI_GREEN);
+        else
+                newline = strjoina("\n", prefix, " ");
+
+        z = strreplace(strstrip(t), "\n", newline);
         if (!z)
                 return log_oom();
 
-        fputs("-- ", f);
+        if (colors_enabled())
+                fprintf(f, ANSI_GREY "%s" ANSI_NORMAL " " ANSI_GREEN, prefix);
+        else
+                fprintf(f, "%s ", prefix);
+
         fputs(z, f);
-        fputc('\n', f);
 
+        if (colors_enabled())
+                fputs(ANSI_NORMAL "\n", f);
+        else
+                fputc('\n', f);
+
+        return 1;
+}
+
+static int url_from_catalog(sd_journal *j, char **ret) {
+        _cleanup_free_ char *t = NULL, *url = NULL;
+        const char *weblink;
+        int r;
+
+        assert(j);
+        assert(ret);
+
+        r = sd_journal_get_catalog(j, &t);
+        if (r == -ENOENT)
+                goto notfound;
+        if (r < 0)
+                return log_error_errno(r, "Failed to find catalog entry: %m");
+
+        weblink = startswith(t, "Documentation:");
+        if (!weblink) {
+                weblink = strstr(t + 1, "\nDocumentation:");
+                if (!weblink)
+                        goto notfound;
+
+                weblink += 15;
+        }
+
+        /* Skip whitespace to value */
+        weblink += strspn(weblink, " \t");
+
+        /* Cut out till next whitespace/newline */
+        url = strndup(weblink, strcspn(weblink, WHITESPACE));
+        if (!url)
+                return log_oom();
+
+        if (!documentation_url_is_valid(url))
+                goto notfound;
+
+        *ret = TAKE_PTR(url);
+        return 1;
+
+notfound:
+        *ret = NULL;
         return 0;
 }
 
@@ -121,17 +188,14 @@ static int parse_fieldv(const void *data, size_t length, const ParseFieldVec *fi
         return 0;
 }
 
-static int field_set_test(Set *fields, const char *name, size_t n) {
-        char *s = NULL;
+static int field_set_test(const Set *fields, const char *name, size_t n) {
+        char *s;
 
         if (!fields)
                 return 1;
 
         s = strndupa(name, n);
-        if (!s)
-                return log_oom();
-
-        return set_get(fields, s) ? 1 : 0;
+        return set_contains(fields, s);
 }
 
 static bool shall_print(const char *p, size_t l, OutputFlags flags) {
@@ -369,15 +433,18 @@ static int output_short(
                 OutputMode mode,
                 unsigned n_columns,
                 OutputFlags flags,
-                Set *output_fields,
+                const Set *output_fields,
                 const size_t highlight[2]) {
 
         int r;
         const void *data;
-        size_t length;
-        size_t n = 0;
-        _cleanup_free_ char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL, *message = NULL, *realtime = NULL, *monotonic = NULL, *priority = NULL, *transport = NULL, *config_file = NULL, *unit = NULL, *user_unit = NULL;
-        size_t hostname_len = 0, identifier_len = 0, comm_len = 0, pid_len = 0, fake_pid_len = 0, message_len = 0, realtime_len = 0, monotonic_len = 0, priority_len = 0, transport_len = 0, config_file_len = 0, unit_len = 0, user_unit_len = 0;
+        size_t length, n = 0;
+        _cleanup_free_ char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL,
+                *message = NULL, *realtime = NULL, *monotonic = NULL, *priority = NULL, *transport = NULL,
+                *config_file = NULL, *unit = NULL, *user_unit = NULL, *documentation_url = NULL;
+        size_t hostname_len = 0, identifier_len = 0, comm_len = 0, pid_len = 0, fake_pid_len = 0, message_len = 0,
+                realtime_len = 0, monotonic_len = 0, priority_len = 0, transport_len = 0, config_file_len = 0,
+                unit_len = 0, user_unit_len = 0, documentation_url_len = 0;
         int p = LOG_INFO;
         bool ellipsized = false, audit;
         const ParseFieldVec fields[] = {
@@ -394,6 +461,7 @@ static int output_short(
                 PARSE_FIELD_VEC_ENTRY("CONFIG_FILE=", &config_file, &config_file_len),
                 PARSE_FIELD_VEC_ENTRY("_SYSTEMD_UNIT=", &unit, &unit_len),
                 PARSE_FIELD_VEC_ENTRY("_SYSTEMD_USER_UNIT=", &user_unit, &user_unit_len),
+                PARSE_FIELD_VEC_ENTRY("DOCUMENTATION=", &documentation_url, &documentation_url_len),
         };
         size_t highlight_shifted[] = {highlight ? highlight[0] : 0, highlight ? highlight[1] : 0};
 
@@ -482,30 +550,64 @@ static int output_short(
                 n += fake_pid_len + 2;
         }
 
+        fputs(": ", f);
+
+        if (urlify_enabled()) {
+                _cleanup_free_ char *c = NULL;
+
+                /* Insert a hyperlink to a documentation URL before the message. Note that we don't make the
+                 * whole message a hyperlink, since otherwise the whole screen might end up being just
+                 * hyperlinks. Moreover, we want to be able to highlight parts of the message (such as the
+                 * config file, see below) hence let's keep the documentation URL link separate. */
+
+                if (documentation_url && shall_print(documentation_url, documentation_url_len, flags)) {
+                        c = strndup(documentation_url, documentation_url_len);
+                        if (!c)
+                                return log_oom();
+
+                        if (!documentation_url_is_valid(c)) /* Eat up invalid links */
+                                c = mfree(c);
+                }
+
+                if (!c)
+                        (void) url_from_catalog(j, &c); /* Acquire from catalog if not embedded in log message itself */
+
+                if (c) {
+                        _cleanup_free_ char *urlified = NULL;
+
+                        if (terminal_urlify(c, special_glyph(SPECIAL_GLYPH_EXTERNAL_LINK), &urlified) >= 0) {
+                                fputs(urlified, f);
+                                fputc(' ', f);
+                        }
+                }
+        }
+
         if (!(flags & OUTPUT_SHOW_ALL) && !utf8_is_printable(message, message_len)) {
                 char bytes[FORMAT_BYTES_MAX];
-                fprintf(f, "[%s blob data]\n", format_bytes(bytes, sizeof(bytes), message_len));
+                fprintf(f, "[%s blob data]\n", format_bytes(bytes, sizeof(bytes), message_len));
         } else {
-                fputs(": ", f);
 
                 /* URLify config_file string in message, if the message starts with it.
                  * Skip URLification if the highlighted pattern overlaps. */
                 if (config_file &&
                     message_len >= config_file_len &&
                     memcmp(message, config_file, config_file_len) == 0 &&
-                    IN_SET(message[config_file_len], ':', ' ', '\0') &&
+                    (message_len == config_file_len || IN_SET(message[config_file_len], ':', ' ')) &&
                     (!highlight || highlight_shifted[0] == 0 || highlight_shifted[0] > config_file_len)) {
 
                         _cleanup_free_ char *t = NULL, *urlified = NULL;
 
                         t = strndup(config_file, config_file_len);
                         if (t && terminal_urlify_path(t, NULL, &urlified) >= 0) {
-                                size_t shift = strlen(urlified) - config_file_len;
+                                size_t urlified_len = strlen(urlified);
+                                size_t shift = urlified_len - config_file_len;
                                 char *joined;
 
-                                joined = strjoin(urlified, message + config_file_len);
+                                joined = realloc(urlified, message_len + shift);
                                 if (joined) {
+                                        memcpy(joined + urlified_len, message + config_file_len, message_len - config_file_len);
                                         free_and_replace(message, joined);
+                                        TAKE_PTR(urlified);
                                         message_len += shift;
                                         if (highlight) {
                                                 highlight_shifted[0] += shift;
@@ -522,7 +624,7 @@ static int output_short(
         }
 
         if (flags & OUTPUT_CATALOG)
-                print_catalog(f, j);
+                (void) print_catalog(f, j);
 
         return ellipsized;
 }
@@ -533,7 +635,7 @@ static int output_verbose(
                 OutputMode mode,
                 unsigned n_columns,
                 OutputFlags flags,
-                Set *output_fields,
+                const Set *output_fields,
                 const size_t highlight[2]) {
 
         const void *data;
@@ -641,7 +743,7 @@ static int output_verbose(
                 return r;
 
         if (flags & OUTPUT_CATALOG)
-                print_catalog(f, j);
+                (void) print_catalog(f, j);
 
         return 0;
 }
@@ -652,7 +754,7 @@ static int output_export(
                 OutputMode mode,
                 unsigned n_columns,
                 OutputFlags flags,
-                Set *output_fields,
+                const Set *output_fields,
                 const size_t highlight[2]) {
 
         sd_id128_t boot_id;
@@ -849,7 +951,7 @@ static int update_json_data(
 static int update_json_data_split(
                 Hashmap *h,
                 OutputFlags flags,
-                Set *output_fields,
+                const Set *output_fields,
                 const void *data,
                 size_t size) {
 
@@ -870,7 +972,7 @@ static int update_json_data_split(
                 return 0;
 
         name = strndupa(data, eq - (const char*) data);
-        if (output_fields && !set_get(output_fields, name))
+        if (output_fields && !set_contains(output_fields, name))
                 return 0;
 
         return update_json_data(h, flags, name, eq + 1, size - (eq - (const char*) data) - 1);
@@ -882,7 +984,7 @@ static int output_json(
                 OutputMode mode,
                 unsigned n_columns,
                 OutputFlags flags,
-                Set *output_fields,
+                const Set *output_fields,
                 const size_t highlight[2]) {
 
         char sid[SD_ID128_STRING_MAX], usecbuf[DECIMAL_STR_MAX(usec_t)];
@@ -1016,57 +1118,83 @@ finish:
         return r;
 }
 
-static int output_cat(
+static int output_cat_field(
                 FILE *f,
                 sd_journal *j,
-                OutputMode mode,
-                unsigned n_columns,
                 OutputFlags flags,
-                Set *output_fields,
+                const char *field,
                 const size_t highlight[2]) {
 
+        const char *highlight_on, *highlight_off;
         const void *data;
-        size_t l;
+        size_t l, fl;
         int r;
-        const char *highlight_on = "", *highlight_off = "";
 
-        assert(j);
-        assert(f);
-
-        if (flags & OUTPUT_COLOR) {
+        if (FLAGS_SET(flags, OUTPUT_COLOR)) {
                 highlight_on = ANSI_HIGHLIGHT_RED;
                 highlight_off = ANSI_NORMAL;
-        }
-
-        sd_journal_set_data_threshold(j, 0);
+        } else
+                highlight_on = highlight_off = "";
 
-        r = sd_journal_get_data(j, "MESSAGE", &data, &l);
+        r = sd_journal_get_data(j, field, &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)
-                        return 0;
-
+        if (r == -ENOENT) /* An entry without the requested field */
+                return 0;
+        if (r < 0)
                 return log_error_errno(r, "Failed to get data: %m");
-        }
 
-        assert(l >= 8);
+        fl = strlen(field);
+        assert(l >= fl + 1);
+        assert(((char*) data)[fl] == '=');
+
+        data = (const uint8_t*) data + fl + 1;
+        l -= fl + 1;
 
-        if (highlight && (flags & OUTPUT_COLOR)) {
+        if (highlight && FLAGS_SET(flags, OUTPUT_COLOR)) {
                 assert(highlight[0] <= highlight[1]);
-                assert(highlight[1] <= l - 8);
+                assert(highlight[1] <= l);
 
-                fwrite((const char*) data + 8, 1, highlight[0], f);
+                fwrite((const char*) data, 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((const char*) data + 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);
+                fwrite((const char*) data + highlight[1], 1, l - highlight[1], f);
         } else
-                fwrite((const char*) data + 8, 1, l - 8, f);
+                fwrite((const char*) data, 1, l, f);
+
         fputc('\n', f);
+        return 0;
+}
+
+static int output_cat(
+                FILE *f,
+                sd_journal *j,
+                OutputMode mode,
+                unsigned n_columns,
+                OutputFlags flags,
+                const Set *output_fields,
+                const size_t highlight[2]) {
+
+        const char *field;
+        Iterator iterator;
+        int r;
+
+        assert(j);
+        assert(f);
+
+        (void) sd_journal_set_data_threshold(j, 0);
+
+        if (set_isempty(output_fields))
+                return output_cat_field(f, j, flags, "MESSAGE", highlight);
+
+        SET_FOREACH(field, output_fields, iterator) {
+                r = output_cat_field(f, j, flags, field, streq(field, "MESSAGE") ? highlight : NULL);
+                if (r < 0)
+                        return r;
+        }
 
         return 0;
 }
@@ -1077,7 +1205,7 @@ static int (*output_funcs[_OUTPUT_MODE_MAX])(
                 OutputMode mode,
                 unsigned n_columns,
                 OutputFlags flags,
-                Set *output_fields,
+                const Set *output_fields,
                 const size_t highlight[2]) = {
 
         [OUTPUT_SHORT]             = output_short,
@@ -1107,30 +1235,25 @@ int show_journal_entry(
                 const size_t highlight[2],
                 bool *ellipsized) {
 
-        int ret;
-        _cleanup_set_free_free_ Set *fields = NULL;
+        _cleanup_set_free_ Set *fields = NULL;
+        int r;
+
         assert(mode >= 0);
         assert(mode < _OUTPUT_MODE_MAX);
 
         if (n_columns <= 0)
                 n_columns = columns();
 
-        if (output_fields) {
-                fields = set_new(&string_hash_ops);
-                if (!fields)
-                        return log_oom();
-
-                ret = set_put_strdupv(fields, output_fields);
-                if (ret < 0)
-                        return ret;
-        }
+        r = set_put_strdupv(&fields, output_fields);
+        if (r < 0)
+                return r;
 
-        ret = output_funcs[mode](f, j, mode, n_columns, flags, fields, highlight);
+        r = output_funcs[mode](f, j, mode, n_columns, flags, fields, highlight);
 
-        if (ellipsized && ret > 0)
+        if (ellipsized && r > 0)
                 *ellipsized = true;
 
-        return ret;
+        return r;
 }
 
 static int maybe_print_begin_newline(FILE *f, OutputFlags *flags) {
index 7aee239e33a81c82b2e314407dc62758aa50be45..4a593b05f3c90073b6fd87e8ad60d66d040b7562 100644 (file)
@@ -191,6 +191,10 @@ LoopDevice* loop_device_unref(LoopDevice *d) {
                 return NULL;
 
         if (d->fd >= 0) {
+                /* Implicitly sync the device, since otherwise in-flight blocks might not get written */
+                if (fsync(d->fd) < 0)
+                        log_debug_errno(errno, "Failed to sync loop block device, ignoring: %m");
+
                 if (d->nr >= 0 && !d->relinquished) {
                         if (ioctl(d->fd, LOOP_CLR_FD) < 0)
                                 log_debug_errno(errno, "Failed to clear loop device: %m");
@@ -216,7 +220,7 @@ LoopDevice* loop_device_unref(LoopDevice *d) {
                                         log_warning_errno(errno, "Failed to remove device %s: %m", strna(d->node));
                                         break;
                                 }
-                                usleep(50 * USEC_PER_MSEC);
+                                (void) usleep(50 * USEC_PER_MSEC);
                         }
         }
 
index b45efcd1e6657d812b2953d76cfe41205dcbaf2f..1b7cfb5028adf763b2724dc9733c69e4774f24ee 100644 (file)
@@ -1171,7 +1171,7 @@ int image_read_metadata(Image *i) {
                 if (r < 0)
                         return r;
 
-                r = dissect_image(d->fd, NULL, 0, DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_RELAX_VAR_CHECK, &m);
+                r = dissect_image(d->fd, NULL, 0, NULL, DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_RELAX_VAR_CHECK, &m);
                 if (r < 0)
                         return r;
 
diff --git a/src/shared/macvlan-util.c b/src/shared/macvlan-util.c
new file mode 100644 (file)
index 0000000..926b4d4
--- /dev/null
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "conf-parser.h"
+#include "macvlan-util.h"
+#include "string-table.h"
+
+static const char* const macvlan_mode_table[_NETDEV_MACVLAN_MODE_MAX] = {
+        [NETDEV_MACVLAN_MODE_PRIVATE] = "private",
+        [NETDEV_MACVLAN_MODE_VEPA] = "vepa",
+        [NETDEV_MACVLAN_MODE_BRIDGE] = "bridge",
+        [NETDEV_MACVLAN_MODE_PASSTHRU] = "passthru",
+        [NETDEV_MACVLAN_MODE_SOURCE] = "source",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(macvlan_mode, MacVlanMode);
diff --git a/src/shared/macvlan-util.h b/src/shared/macvlan-util.h
new file mode 100644 (file)
index 0000000..7670bbf
--- /dev/null
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <linux/if_link.h>
+
+typedef enum MacVlanMode {
+        NETDEV_MACVLAN_MODE_PRIVATE = MACVLAN_MODE_PRIVATE,
+        NETDEV_MACVLAN_MODE_VEPA = MACVLAN_MODE_VEPA,
+        NETDEV_MACVLAN_MODE_BRIDGE = MACVLAN_MODE_BRIDGE,
+        NETDEV_MACVLAN_MODE_PASSTHRU = MACVLAN_MODE_PASSTHRU,
+        NETDEV_MACVLAN_MODE_SOURCE = MACVLAN_MODE_SOURCE,
+        _NETDEV_MACVLAN_MODE_MAX,
+        _NETDEV_MACVLAN_MODE_INVALID = -1
+} MacVlanMode;
+
+const char *macvlan_mode_to_string(MacVlanMode d) _const_;
+MacVlanMode macvlan_mode_from_string(const char *d) _pure_;
index 0af8ed77cd9f8a6fb9ac0c97764e5d7b44df254e..0da733c3fe7b3e61c53970834781151d3bb2340f 100644 (file)
@@ -17,20 +17,38 @@ shared_sources = files('''
         bitmap.c
         bitmap.h
         blkid-util.h
+        bond-util.c
+        bond-util.h
         boot-timestamps.c
         boot-timestamps.h
         bootspec.c
         bootspec.h
         bpf-program.c
         bpf-program.h
+        bridge-util.c
+        bridge-util.h
+        bus-get-properties.c
+        bus-get-properties.h
+        bus-locator.c
+        bus-locator.h
+        bus-log-control-api.c
+        bus-log-control-api.h
+        bus-map-properties.c
+        bus-map-properties.h
+        bus-message-util.c
+        bus-message-util.h
+        bus-object.c
+        bus-object.h
+        bus-polkit.c
+        bus-polkit.h
+        bus-print-properties.c
+        bus-print-properties.h
         bus-unit-procs.c
         bus-unit-procs.h
         bus-unit-util.c
         bus-unit-util.h
         bus-util.c
         bus-util.h
-        bus-polkit.c
-        bus-polkit.h
         bus-wait-for-jobs.c
         bus-wait-for-jobs.h
         bus-wait-for-units.c
@@ -51,6 +69,8 @@ shared_sources = files('''
         condition.h
         conf-parser.c
         conf-parser.h
+        coredump-util.c
+        coredump-util.h
         cpu-set-util.c
         cpu-set-util.h
         crypt-util.c
@@ -89,6 +109,8 @@ shared_sources = files('''
         fstab-util.h
         generator.c
         generator.h
+        geneve-util.c
+        geneve-util.h
         gpt.c
         gpt.h
         group-record-nss.c
@@ -108,6 +130,8 @@ shared_sources = files('''
         install-printf.h
         install.c
         install.h
+        ipvlan-util.c
+        ipvlan-util.h
         ip-protocol-list.c
         ip-protocol-list.h
         journal-importer.c
@@ -139,6 +163,8 @@ shared_sources = files('''
         machine-image.h
         machine-pool.c
         machine-pool.h
+        macvlan-util.c
+        macvlan-util.h
         main-func.h
         module-util.h
         mount-util.c
@@ -158,8 +184,6 @@ shared_sources = files('''
         output-mode.h
         pager.c
         pager.h
-        path-lookup.c
-        path-lookup.h
         pe-header.h
         pkcs11-util.c
         pkcs11-util.h
@@ -178,6 +202,8 @@ shared_sources = files('''
         securebits-util.h
         serialize.c
         serialize.h
+        service-util.c
+        service-util.h
         sleep-config.c
         sleep-config.h
         socket-netlink.c
@@ -323,6 +349,7 @@ libshared_deps = [threads,
                   librt,
                   libseccomp,
                   libselinux,
+                  libzstd,
                   libxz]
 
 libshared_sym_path = '@0@/libshared.sym'.format(meson.current_source_dir())
index 9173cf2ffde0a65c8481b4891809773deaeaab08..3e64d423c837df89b1d528318d39bb772525982a 100644 (file)
@@ -51,7 +51,7 @@ int module_load_and_warn(struct kmod_ctx *ctx, const char *module, bool verbose)
                                          "Inserted module '%s'", kmod_module_get_name(mod));
                         else if (err == KMOD_PROBE_APPLY_BLACKLIST)
                                 log_full(verbose ? LOG_INFO : LOG_DEBUG,
-                                         "Module '%s' is blacklisted", kmod_module_get_name(mod));
+                                         "Module '%s' is deny-listed", kmod_module_get_name(mod));
                         else {
                                 assert(err < 0);
 
index 32c5332822f01832a90ce07710a43432aaf81a1a..b3fac13f7ee9cd487c1edd518c8435c0f1de9ca3 100644 (file)
@@ -58,8 +58,8 @@ int umount_recursive(const char *prefix, int flags) {
                         if (!path_startswith(path, prefix))
                                 continue;
 
-                        if (umount2(path, flags) < 0) {
-                                r = log_debug_errno(errno, "Failed to umount %s: %m", path);
+                        if (umount2(path, flags | UMOUNT_NOFOLLOW) < 0) {
+                                log_debug_errno(errno, "Failed to umount %s, ignoring: %m", path);
                                 continue;
                         }
 
@@ -70,7 +70,6 @@ int umount_recursive(const char *prefix, int flags) {
 
                         break;
                 }
-
         } while (again);
 
         return n;
@@ -136,7 +135,7 @@ int bind_remount_recursive_with_mountinfo(
                 const char *prefix,
                 unsigned long new_flags,
                 unsigned long flags_mask,
-                char **blacklist,
+                char **deny_list,
                 FILE *proc_self_mountinfo) {
 
         _cleanup_set_free_free_ Set *done = NULL;
@@ -151,11 +150,11 @@ int bind_remount_recursive_with_mountinfo(
          * operation). If it isn't we first make it one. Afterwards we apply MS_BIND|MS_RDONLY (or remove MS_RDONLY) to
          * all submounts we can access, too. When mounts are stacked on the same mount point we only care for each
          * individual "top-level" mount on each point, as we cannot influence/access the underlying mounts anyway. We
-         * do not have any effect on future submounts that might get propagated, they migt be writable. This includes
+         * do not have any effect on future submounts that might get propagated, they might be writable. This includes
          * future submounts that have been triggered via autofs.
          *
-         * If the "blacklist" parameter is specified it may contain a list of subtrees to exclude from the
-         * remount operation. Note that we'll ignore the blacklist for the top-level path. */
+         * If the "deny_list" parameter is specified it may contain a list of subtrees to exclude from the
+         * remount operation. Note that we'll ignore the deny list for the top-level path. */
 
         simplified = strdup(prefix);
         if (!simplified)
@@ -203,13 +202,13 @@ int bind_remount_recursive_with_mountinfo(
                         if (!path_startswith(path, simplified))
                                 continue;
 
-                        /* Ignore this mount if it is blacklisted, but only if it isn't the top-level mount
+                        /* Ignore this mount if it is deny-listed, but only if it isn't the top-level mount
                          * we shall operate on. */
                         if (!path_equal(path, simplified)) {
-                                bool blacklisted = false;
+                                bool deny_listed = false;
                                 char **i;
 
-                                STRV_FOREACH(i, blacklist) {
+                                STRV_FOREACH(i, deny_list) {
                                         if (path_equal(*i, simplified))
                                                 continue;
 
@@ -217,13 +216,13 @@ int bind_remount_recursive_with_mountinfo(
                                                 continue;
 
                                         if (path_startswith(path, *i)) {
-                                                blacklisted = true;
-                                                log_debug("Not remounting %s blacklisted by %s, called for %s",
+                                                deny_listed = true;
+                                                log_debug("Not remounting %s deny-listed by %s, called for %s",
                                                           path, *i, simplified);
                                                 break;
                                         }
                                 }
-                                if (blacklisted)
+                                if (deny_listed)
                                         continue;
                         }
 
@@ -239,7 +238,7 @@ int bind_remount_recursive_with_mountinfo(
                         }
 
                         if (!set_contains(done, path)) {
-                                r = set_put_strdup(todo, path);
+                                r = set_put_strdup(&todo, path);
                                 if (r < 0)
                                         return r;
                         }
@@ -266,7 +265,7 @@ int bind_remount_recursive_with_mountinfo(
 
                         log_debug("Made top-level directory %s a mount point.", prefix);
 
-                        r = set_put_strdup(done, simplified);
+                        r = set_put_strdup(&done, simplified);
                         if (r < 0)
                                 return r;
                 }
@@ -314,7 +313,7 @@ int bind_remount_recursive(
                 const char *prefix,
                 unsigned long new_flags,
                 unsigned long flags_mask,
-                char **blacklist) {
+                char **deny_list) {
 
         _cleanup_fclose_ FILE *proc_self_mountinfo = NULL;
         int r;
@@ -323,7 +322,7 @@ int bind_remount_recursive(
         if (r < 0)
                 return r;
 
-        return bind_remount_recursive_with_mountinfo(prefix, new_flags, flags_mask, blacklist, proc_self_mountinfo);
+        return bind_remount_recursive_with_mountinfo(prefix, new_flags, flags_mask, deny_list, proc_self_mountinfo);
 }
 
 int bind_remount_one_with_mountinfo(
@@ -397,71 +396,73 @@ int repeat_unmount(const char *path, int flags) {
         }
 }
 
-int mode_to_inaccessible_node(const char *runtime_dir, mode_t mode, char **dest) {
-        /* This function maps a node type to a corresponding inaccessible file node. These nodes are created during
-         * early boot by PID 1. In some cases we lacked the privs to create the character and block devices (maybe
-         * because we run in an userns environment, or miss CAP_SYS_MKNOD, or run with a devices policy that excludes
-         * device nodes with major and minor of 0), but that's fine, in that case we use an AF_UNIX file node instead,
-         * which is not the same, but close enough for most uses. And most importantly, the kernel allows bind mounts
-         * from socket nodes to any non-directory file nodes, and that's the most important thing that matters. */
+int mode_to_inaccessible_node(
+                const char *runtime_dir,
+                mode_t mode,
+                char **ret) {
+
+        /* This function maps a node type to a corresponding inaccessible file node. These nodes are created
+         * during early boot by PID 1. In some cases we lacked the privs to create the character and block
+         * devices (maybe because we run in an userns environment, or miss CAP_SYS_MKNOD, or run with a
+         * devices policy that excludes device nodes with major and minor of 0), but that's fine, in that
+         * case we use an AF_UNIX file node instead, which is not the same, but close enough for most
+         * uses. And most importantly, the kernel allows bind mounts from socket nodes to any non-directory
+         * file nodes, and that's the most important thing that matters.
+         *
+         * Note that the runtime directory argument shall be the top-level runtime directory, i.e. /run/ if
+         * we operate in system context and $XDG_RUNTIME_DIR if we operate in user context. */
+
         _cleanup_free_ char *d = NULL;
         const char *node = NULL;
-        char *tmp;
+        bool fallback = false;
+
+        assert(ret);
 
-        assert(dest);
+        if (!runtime_dir)
+                runtime_dir = "/run";
 
         switch(mode & S_IFMT) {
                 case S_IFREG:
-                        node = "/inaccessible/reg";
+                        node = "/systemd/inaccessible/reg";
                         break;
 
                 case S_IFDIR:
-                        node = "/inaccessible/dir";
+                        node = "/systemd/inaccessible/dir";
                         break;
 
                 case S_IFCHR:
-                        d = path_join(runtime_dir, "/inaccessible/chr");
-                        if (!d)
-                                return log_oom();
-
-                        if (access(d, F_OK) == 0) {
-                                *dest = TAKE_PTR(d);
-                                return 0;
-                        }
-
-                        node = "/inaccessible/sock";
+                        node = "/systemd/inaccessible/chr";
+                        fallback = true;
                         break;
 
                 case S_IFBLK:
-                        d = path_join(runtime_dir, "/inaccessible/blk");
-                        if (!d)
-                                return log_oom();
-
-                        if (access(d, F_OK) == 0) {
-                                *dest = TAKE_PTR(d);
-                                return 0;
-                        }
-
-                        node = "/inaccessible/sock";
+                        node = "/systemd/inaccessible/blk";
+                        fallback = true;
                         break;
 
                 case S_IFIFO:
-                        node = "/inaccessible/fifo";
+                        node = "/systemd/inaccessible/fifo";
                         break;
 
                 case S_IFSOCK:
-                        node = "/inaccessible/sock";
+                        node = "/systemd/inaccessible/sock";
                         break;
         }
-
         if (!node)
                 return -EINVAL;
 
-        tmp = path_join(runtime_dir, node);
-        if (!tmp)
-                return log_oom();
+        d = path_join(runtime_dir, node);
+        if (!d)
+                return -ENOMEM;
+
+        if (fallback && access(d, F_OK) < 0) {
+                free(d);
+                d = path_join(runtime_dir, "/systemd/inaccessible/sock");
+                if (!d)
+                        return -ENOMEM;
+        }
 
-        *dest = tmp;
+        *ret = TAKE_PTR(d);
         return 0;
 }
 
index 06fddacf16d03b60350abcc1cfefa1aa3528d658..8fb597e7c039de33ba61a04b5ee5692d61c91153 100644 (file)
@@ -3,13 +3,39 @@
 
 #include <mntent.h>
 #include <stdio.h>
+#include <unistd.h>
 
+#include "errno-util.h"
 #include "macro.h"
 
+/* 4MB for contents of regular files, 64k inodes for directories, symbolic links and device specials, using
+ * large storage array systems as a baseline */
+#define TMPFS_LIMITS_DEV             ",size=4m,nr_inodes=64k"
+
+/* Very little, if any use expected */
+#define TMPFS_LIMITS_EMPTY_OR_ALMOST ",size=4m,nr_inodes=1k"
+#define TMPFS_LIMITS_SYS             TMPFS_LIMITS_EMPTY_OR_ALMOST
+#define TMPFS_LIMITS_SYS_FS_CGROUP   TMPFS_LIMITS_EMPTY_OR_ALMOST
+
+/* On an extremely small device with only 256MB of RAM, 20% of RAM should be enough for the re-execution of
+ * PID1 because 16MB of free space is required. */
+#define TMPFS_LIMITS_RUN             ",size=20%,nr_inodes=800k"
+
+/* The limit used for various nested tmpfs mounts, in paricular for guests started by systemd-nspawn.
+ * 10% of RAM (using 16GB of RAM as a baseline) translates to 400k inodes (assuming 4k each) and 25%
+ * translates to 1M inodes.
+ * (On the host, /tmp is configured through a .mount unit file.) */
+#define NESTED_TMPFS_LIMITS          ",size=10%,nr_inodes=400k"
+
+/* More space for volatile root and /var */
+#define TMPFS_LIMITS_VAR             ",size=25%,nr_inodes=1m"
+#define TMPFS_LIMITS_ROOTFS          TMPFS_LIMITS_VAR
+#define TMPFS_LIMITS_VOLATILE_STATE  TMPFS_LIMITS_VAR
+
 int repeat_unmount(const char *path, int flags);
 int umount_recursive(const char *target, int flags);
-int bind_remount_recursive(const char *prefix, unsigned long new_flags, unsigned long flags_mask, char **blacklist);
-int bind_remount_recursive_with_mountinfo(const char *prefix, unsigned long new_flags, unsigned long flags_mask, char **blacklist, FILE *proc_self_mountinfo);
+int bind_remount_recursive(const char *prefix, unsigned long new_flags, unsigned long flags_mask, char **deny_list);
+int bind_remount_recursive_with_mountinfo(const char *prefix, unsigned long new_flags, unsigned long flags_mask, char **deny_list, FILE *proc_self_mountinfo);
 int bind_remount_one_with_mountinfo(const char *path, unsigned long new_flags, unsigned long flags_mask, FILE *proc_self_mountinfo);
 
 int mount_move_root(const char *path);
@@ -33,3 +59,12 @@ int mount_option_mangle(
                 char **ret_remaining_options);
 
 int mode_to_inaccessible_node(const char *runtime_dir, mode_t mode, char **dest);
+
+/* Useful for usage with _cleanup_(), unmounts, removes a directory and frees the pointer */
+static inline void umount_and_rmdir_and_free(char *p) {
+        PROTECT_ERRNO;
+        (void) umount_recursive(p, 0);
+        (void) rmdir(p);
+        free(p);
+}
+DEFINE_TRIVIAL_CLEANUP_FUNC(char*, umount_and_rmdir_and_free);
index 33a06a010c43bcc80f09bf65eb07f5a10b3008f5..10fdbaf326d06f24bd80e89a024efd1a6f34a270 100644 (file)
@@ -134,7 +134,7 @@ int nscd_flush_cache(char **databases) {
         int r = 0;
         char **i;
 
-        /* Tries to invalidate the specified database in nscd. We do this carefully, with a 5s time-out, so that we
+        /* Tries to invalidate the specified database in nscd. We do this carefully, with a 5s timeout, so that we
          * don't block indefinitely on another service. */
 
         end = usec_add(now(CLOCK_MONOTONIC), NSCD_FLUSH_CACHE_TIMEOUT_USEC);
diff --git a/src/shared/offline-passwd.c b/src/shared/offline-passwd.c
new file mode 100644 (file)
index 0000000..26a1b9c
--- /dev/null
@@ -0,0 +1,164 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "fd-util.h"
+#include "fs-util.h"
+#include "offline-passwd.h"
+#include "path-util.h"
+#include "user-util.h"
+
+DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(uid_gid_hash_ops, char, string_hash_func, string_compare_func, free);
+
+static int open_passwd_file(const char *root, const char *fname, FILE **ret_file) {
+        _cleanup_free_ char *p = NULL;
+        _cleanup_close_ int fd = -1;
+
+        fd = chase_symlinks_and_open(fname, root, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC, &p);
+        if (fd < 0)
+                return fd;
+
+        FILE *f = fdopen(fd, "r");
+        if (!f)
+                return -errno;
+
+        TAKE_FD(fd);
+
+        log_debug("Reading %s entries from %s...", basename(fname), p);
+
+        *ret_file = f;
+        return 0;
+}
+
+static int populate_uid_cache(const char *root, Hashmap **ret) {
+        _cleanup_(hashmap_freep) Hashmap *cache = NULL;
+        int r;
+
+        cache = hashmap_new(&uid_gid_hash_ops);
+        if (!cache)
+                return -ENOMEM;
+
+        /* The directory list is harcoded here: /etc is the standard, and rpm-ostree uses /usr/lib. This
+         * could be made configurable, but I don't see the point right now. */
+
+        const char *fname;
+        FOREACH_STRING(fname, "/etc/passwd", "/usr/lib/passwd") {
+                _cleanup_fclose_ FILE *f = NULL;
+
+                r = open_passwd_file(root, fname, &f);
+                if (r == -ENOENT)
+                        continue;
+                if (r < 0)
+                        return r;
+
+                struct passwd *pw;
+                while ((r = fgetpwent_sane(f, &pw)) > 0) {
+                        _cleanup_free_ char *n = NULL;
+
+                        n = strdup(pw->pw_name);
+                        if (!n)
+                                return -ENOMEM;
+
+                        r = hashmap_put(cache, n, UID_TO_PTR(pw->pw_uid));
+                        if (IN_SET(r, 0 -EEXIST))
+                                continue;
+                        if (r < 0)
+                                return r;
+                        TAKE_PTR(n);
+                }
+        }
+
+        *ret = TAKE_PTR(cache);
+        return 0;
+}
+
+static int populate_gid_cache(const char *root, Hashmap **ret) {
+        _cleanup_(hashmap_freep) Hashmap *cache = NULL;
+        int r;
+
+        cache = hashmap_new(&uid_gid_hash_ops);
+        if (!cache)
+                return -ENOMEM;
+
+        const char *fname;
+        FOREACH_STRING(fname, "/etc/group", "/usr/lib/group") {
+                _cleanup_fclose_ FILE *f = NULL;
+
+                r = open_passwd_file(root, fname, &f);
+                if (r == -ENOENT)
+                        continue;
+                if (r < 0)
+                        return r;
+
+                struct group *gr;
+                while ((r = fgetgrent_sane(f, &gr)) > 0) {
+                        _cleanup_free_ char *n = NULL;
+
+                        n = strdup(gr->gr_name);
+                        if (!n)
+                                return -ENOMEM;
+
+                        r = hashmap_put(cache, n, GID_TO_PTR(gr->gr_gid));
+                        if (IN_SET(r, 0, -EEXIST))
+                                continue;
+                        if (r < 0)
+                                return r;
+                        TAKE_PTR(n);
+                }
+        }
+
+        *ret = TAKE_PTR(cache);
+        return 0;
+}
+
+int name_to_uid_offline(
+                const char *root,
+                const char *user,
+                uid_t *ret_uid,
+                Hashmap **cache) {
+
+        void *found;
+        int r;
+
+        assert(user);
+        assert(ret_uid);
+        assert(cache);
+
+        if (!*cache) {
+                r = populate_uid_cache(root, cache);
+                if (r < 0)
+                        return r;
+        }
+
+        found = hashmap_get(*cache, user);
+        if (!found)
+                return -ESRCH;
+
+        *ret_uid = PTR_TO_UID(found);
+        return 0;
+}
+
+int name_to_gid_offline(
+                const char *root,
+                const char *group,
+                gid_t *ret_gid,
+                Hashmap **cache) {
+
+        void *found;
+        int r;
+
+        assert(group);
+        assert(ret_gid);
+        assert(cache);
+
+        if (!*cache) {
+                r = populate_gid_cache(root, cache);
+                if (r < 0)
+                        return r;
+        }
+
+        found = hashmap_get(*cache, group);
+        if (!found)
+                return -ESRCH;
+
+        *ret_gid = PTR_TO_GID(found);
+        return 0;
+}
diff --git a/src/shared/offline-passwd.h b/src/shared/offline-passwd.h
new file mode 100644 (file)
index 0000000..90bdfc7
--- /dev/null
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <sys/types.h>
+
+#include "hashmap.h"
+
+int name_to_uid_offline(const char *root, const char *user, uid_t *ret_uid, Hashmap **cache);
+int name_to_gid_offline(const char *root, const char *group, gid_t *ret_gid, Hashmap **cache);
index b2af8535f9c5a2cab62af09850dfd713db33fd0c..042e77c8c7f758ec605a7bbf60ac419f7bff5fbd 100644 (file)
@@ -3,6 +3,7 @@
 #include "alloc-util.h"
 #include "env-file.h"
 #include "fd-util.h"
+#include "fileio.h"
 #include "fs-util.h"
 #include "macro.h"
 #include "os-util.h"
@@ -76,10 +77,9 @@ int fopen_os_release(const char *root, char **ret_path, FILE **ret_file) {
         if (r < 0)
                 return r;
 
-        f = fdopen(fd, "r");
+        f = take_fdopen(&fd, "r");
         if (!f)
                 return -errno;
-        fd = -1;
 
         *ret_file = f;
 
@@ -117,3 +117,33 @@ int load_os_release_pairs(const char *root, char ***ret) {
 
         return load_env_file_pairs(f, p, ret);
 }
+
+int load_os_release_pairs_with_prefix(const char *root, const char *prefix, char ***ret) {
+        _cleanup_strv_free_ char **os_release_pairs = NULL, **os_release_pairs_prefixed = NULL;
+        char **p, **q;
+        int r;
+
+        r = load_os_release_pairs(root, &os_release_pairs);
+        if (r < 0)
+                return r;
+
+        STRV_FOREACH_PAIR(p, q, os_release_pairs) {
+                char *line;
+
+                /* We strictly return only the four main ID fields and ignore the rest */
+                if (!STR_IN_SET(*p, "ID", "VERSION_ID", "BUILD_ID", "VARIANT_ID"))
+                        continue;
+
+                ascii_strlower(*p);
+                line = strjoin(prefix, *p, "=", *q);
+                if (!line)
+                        return -ENOMEM;
+                r = strv_consume(&os_release_pairs_prefixed, line);
+                if (r < 0)
+                        return r;
+        }
+
+        *ret = TAKE_PTR(os_release_pairs_prefixed);
+
+        return 0;
+}
index 27ec7ac8d7774d48c9ca62485382c1ffa19560b1..b54bb0916dbf3e5e038c1c1723c7d8509ab1c0b0 100644 (file)
@@ -10,3 +10,4 @@ int fopen_os_release(const char *root, char **ret_path, FILE **ret_file);
 
 int parse_os_release(const char *root, ...) _sentinel_;
 int load_os_release_pairs(const char *root, char ***ret);
+int load_os_release_pairs_with_prefix(const char *root, const char *prefix, char ***ret);
diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c
deleted file mode 100644 (file)
index 48e0eec..0000000
+++ /dev/null
@@ -1,840 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
-
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "alloc-util.h"
-#include "fs-util.h"
-#include "install.h"
-#include "log.h"
-#include "macro.h"
-#include "mkdir.h"
-#include "path-lookup.h"
-#include "path-util.h"
-#include "rm-rf.h"
-#include "stat-util.h"
-#include "string-util.h"
-#include "strv.h"
-#include "tmpfile-util.h"
-#include "user-util.h"
-#include "util.h"
-
-int xdg_user_runtime_dir(char **ret, const char *suffix) {
-        const char *e;
-        char *j;
-
-        assert(ret);
-        assert(suffix);
-
-        e = getenv("XDG_RUNTIME_DIR");
-        if (!e)
-                return -ENXIO;
-
-        j = strjoin(e, suffix);
-        if (!j)
-                return -ENOMEM;
-
-        *ret = j;
-        return 0;
-}
-
-int xdg_user_config_dir(char **ret, const char *suffix) {
-        const char *e;
-        char *j;
-        int r;
-
-        assert(ret);
-
-        e = getenv("XDG_CONFIG_HOME");
-        if (e)
-                j = strjoin(e, suffix);
-        else {
-                _cleanup_free_ char *home = NULL;
-
-                r = get_home_dir(&home);
-                if (r < 0)
-                        return r;
-
-                j = strjoin(home, "/.config", suffix);
-        }
-
-        if (!j)
-                return -ENOMEM;
-
-        *ret = j;
-        return 0;
-}
-
-int xdg_user_data_dir(char **ret, const char *suffix) {
-        const char *e;
-        char *j;
-        int r;
-
-        assert(ret);
-        assert(suffix);
-
-        /* We don't treat /etc/xdg/systemd here as the spec
-         * suggests because we assume that is a link to
-         * /etc/systemd/ anyway. */
-
-        e = getenv("XDG_DATA_HOME");
-        if (e)
-                j = strjoin(e, suffix);
-        else {
-                _cleanup_free_ char *home = NULL;
-
-                r = get_home_dir(&home);
-                if (r < 0)
-                        return r;
-
-                j = strjoin(home, "/.local/share", suffix);
-        }
-        if (!j)
-                return -ENOMEM;
-
-        *ret = j;
-        return 1;
-}
-
-static const char* const user_data_unit_paths[] = {
-        "/usr/local/lib/systemd/user",
-        "/usr/local/share/systemd/user",
-        USER_DATA_UNIT_PATH,
-        "/usr/lib/systemd/user",
-        "/usr/share/systemd/user",
-        NULL
-};
-
-static const char* const user_config_unit_paths[] = {
-        USER_CONFIG_UNIT_PATH,
-        "/etc/systemd/user",
-        NULL
-};
-
-int xdg_user_dirs(char ***ret_config_dirs, char ***ret_data_dirs) {
-        /* Implement the mechanisms defined in
-         *
-         * http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
-         *
-         * We look in both the config and the data dirs because we
-         * want to encourage that distributors ship their unit files
-         * as data, and allow overriding as configuration.
-         */
-        const char *e;
-        _cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
-
-        e = getenv("XDG_CONFIG_DIRS");
-        if (e)
-                config_dirs = strv_split(e, ":");
-        else
-                config_dirs = strv_new("/etc/xdg");
-        if (!config_dirs)
-                return -ENOMEM;
-
-        e = getenv("XDG_DATA_DIRS");
-        if (e)
-                data_dirs = strv_split(e, ":");
-        else
-                data_dirs = strv_new("/usr/local/share",
-                                     "/usr/share");
-        if (!data_dirs)
-                return -ENOMEM;
-
-        *ret_config_dirs = TAKE_PTR(config_dirs);
-        *ret_data_dirs = TAKE_PTR(data_dirs);
-
-        return 0;
-}
-
-static char** user_dirs(
-                const char *persistent_config,
-                const char *runtime_config,
-                const char *global_persistent_config,
-                const char *global_runtime_config,
-                const char *generator,
-                const char *generator_early,
-                const char *generator_late,
-                const char *transient,
-                const char *persistent_control,
-                const char *runtime_control) {
-
-        _cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
-        _cleanup_free_ char *data_home = NULL;
-        _cleanup_strv_free_ char **res = NULL;
-        int r;
-
-        r = xdg_user_dirs(&config_dirs, &data_dirs);
-        if (r < 0)
-                return NULL;
-
-        r = xdg_user_data_dir(&data_home, "/systemd/user");
-        if (r < 0 && r != -ENXIO)
-                return NULL;
-
-        /* Now merge everything we found. */
-        if (strv_extend(&res, persistent_control) < 0)
-                return NULL;
-
-        if (strv_extend(&res, runtime_control) < 0)
-                return NULL;
-
-        if (strv_extend(&res, transient) < 0)
-                return NULL;
-
-        if (strv_extend(&res, generator_early) < 0)
-                return NULL;
-
-        if (strv_extend_strv_concat(&res, config_dirs, "/systemd/user") < 0)
-                return NULL;
-
-        if (strv_extend(&res, persistent_config) < 0)
-                return NULL;
-
-        /* global config has lower priority than the user config of the same type */
-        if (strv_extend(&res, global_persistent_config) < 0)
-                return NULL;
-
-        if (strv_extend_strv(&res, (char**) user_config_unit_paths, false) < 0)
-                return NULL;
-
-        if (strv_extend(&res, runtime_config) < 0)
-                return NULL;
-
-        if (strv_extend(&res, global_runtime_config) < 0)
-                return NULL;
-
-        if (strv_extend(&res, generator) < 0)
-                return NULL;
-
-        if (strv_extend(&res, data_home) < 0)
-                return NULL;
-
-        if (strv_extend_strv_concat(&res, data_dirs, "/systemd/user") < 0)
-                return NULL;
-
-        if (strv_extend_strv(&res, (char**) user_data_unit_paths, false) < 0)
-                return NULL;
-
-        if (strv_extend(&res, generator_late) < 0)
-                return NULL;
-
-        if (path_strv_make_absolute_cwd(res) < 0)
-                return NULL;
-
-        return TAKE_PTR(res);
-}
-
-bool path_is_user_data_dir(const char *path) {
-        assert(path);
-
-        return strv_contains((char**) user_data_unit_paths, path);
-}
-
-bool path_is_user_config_dir(const char *path) {
-        assert(path);
-
-        return strv_contains((char**) user_config_unit_paths, path);
-}
-
-static int acquire_generator_dirs(
-                UnitFileScope scope,
-                const char *tempdir,
-                char **generator,
-                char **generator_early,
-                char **generator_late) {
-
-        _cleanup_free_ char *x = NULL, *y = NULL, *z = NULL;
-        const char *prefix;
-
-        assert(generator);
-        assert(generator_early);
-        assert(generator_late);
-        assert(IN_SET(scope, UNIT_FILE_SYSTEM, UNIT_FILE_USER, UNIT_FILE_GLOBAL));
-
-        if (scope == UNIT_FILE_GLOBAL)
-                return -EOPNOTSUPP;
-
-        if (tempdir)
-                prefix = tempdir;
-        else if (scope == UNIT_FILE_SYSTEM)
-                prefix = "/run/systemd";
-        else {
-                /* UNIT_FILE_USER */
-                const char *e;
-
-                e = getenv("XDG_RUNTIME_DIR");
-                if (!e)
-                        return -ENXIO;
-
-                prefix = strjoina(e, "/systemd");
-        }
-
-        x = path_join(prefix, "generator");
-        if (!x)
-                return -ENOMEM;
-
-        y = path_join(prefix, "generator.early");
-        if (!y)
-                return -ENOMEM;
-
-        z = path_join(prefix, "generator.late");
-        if (!z)
-                return -ENOMEM;
-
-        *generator = TAKE_PTR(x);
-        *generator_early = TAKE_PTR(y);
-        *generator_late = TAKE_PTR(z);
-
-        return 0;
-}
-
-static int acquire_transient_dir(
-                UnitFileScope scope,
-                const char *tempdir,
-                char **ret) {
-
-        char *transient;
-
-        assert(ret);
-        assert(IN_SET(scope, UNIT_FILE_SYSTEM, UNIT_FILE_USER, UNIT_FILE_GLOBAL));
-
-        if (scope == UNIT_FILE_GLOBAL)
-                return -EOPNOTSUPP;
-
-        if (tempdir)
-                transient = path_join(tempdir, "transient");
-        else if (scope == UNIT_FILE_SYSTEM)
-                transient = strdup("/run/systemd/transient");
-        else
-                return xdg_user_runtime_dir(ret, "/systemd/transient");
-
-        if (!transient)
-                return -ENOMEM;
-        *ret = transient;
-        return 0;
-}
-
-static int acquire_config_dirs(UnitFileScope scope, char **persistent, char **runtime) {
-        _cleanup_free_ char *a = NULL, *b = NULL;
-        int r;
-
-        assert(persistent);
-        assert(runtime);
-
-        switch (scope) {
-
-        case UNIT_FILE_SYSTEM:
-                a = strdup(SYSTEM_CONFIG_UNIT_PATH);
-                b = strdup("/run/systemd/system");
-                break;
-
-        case UNIT_FILE_GLOBAL:
-                a = strdup(USER_CONFIG_UNIT_PATH);
-                b = strdup("/run/systemd/user");
-                break;
-
-        case UNIT_FILE_USER:
-                r = xdg_user_config_dir(&a, "/systemd/user");
-                if (r < 0 && r != -ENXIO)
-                        return r;
-
-                r = xdg_user_runtime_dir(runtime, "/systemd/user");
-                if (r < 0) {
-                        if (r != -ENXIO)
-                                return r;
-
-                        /* If XDG_RUNTIME_DIR is not set, don't consider that fatal, simply initialize the runtime
-                         * directory to NULL */
-                        *runtime = NULL;
-                }
-
-                *persistent = TAKE_PTR(a);
-
-                return 0;
-
-        default:
-                assert_not_reached("Hmm, unexpected scope value.");
-        }
-
-        if (!a || !b)
-                return -ENOMEM;
-
-        *persistent = TAKE_PTR(a);
-        *runtime = TAKE_PTR(b);
-
-        return 0;
-}
-
-static int acquire_control_dirs(UnitFileScope scope, char **persistent, char **runtime) {
-        _cleanup_free_ char *a = NULL;
-        int r;
-
-        assert(persistent);
-        assert(runtime);
-
-        switch (scope) {
-
-        case UNIT_FILE_SYSTEM:  {
-                _cleanup_free_ char *b = NULL;
-
-                a = strdup("/etc/systemd/system.control");
-                if (!a)
-                        return -ENOMEM;
-
-                b = strdup("/run/systemd/system.control");
-                if (!b)
-                        return -ENOMEM;
-
-                *runtime = TAKE_PTR(b);
-
-                break;
-        }
-
-        case UNIT_FILE_USER:
-                r = xdg_user_config_dir(&a, "/systemd/user.control");
-                if (r < 0 && r != -ENXIO)
-                        return r;
-
-                r = xdg_user_runtime_dir(runtime, "/systemd/user.control");
-                if (r < 0) {
-                        if (r != -ENXIO)
-                                return r;
-
-                        /* If XDG_RUNTIME_DIR is not set, don't consider this fatal, simply initialize the directory to
-                         * NULL */
-                        *runtime = NULL;
-                }
-
-                break;
-
-        case UNIT_FILE_GLOBAL:
-                return -EOPNOTSUPP;
-
-        default:
-                assert_not_reached("Hmm, unexpected scope value.");
-        }
-
-        *persistent = TAKE_PTR(a);
-
-        return 0;
-}
-
-static int acquire_attached_dirs(
-                UnitFileScope scope,
-                char **ret_persistent,
-                char **ret_runtime) {
-
-        _cleanup_free_ char *a = NULL, *b = NULL;
-
-        assert(ret_persistent);
-        assert(ret_runtime);
-
-        /* Portable services are not available to regular users for now. */
-        if (scope != UNIT_FILE_SYSTEM)
-                return -EOPNOTSUPP;
-
-        a = strdup("/etc/systemd/system.attached");
-        if (!a)
-                return -ENOMEM;
-
-        b = strdup("/run/systemd/system.attached");
-        if (!b)
-                return -ENOMEM;
-
-        *ret_persistent = TAKE_PTR(a);
-        *ret_runtime = TAKE_PTR(b);
-
-        return 0;
-}
-
-static int patch_root_prefix(char **p, const char *root_dir) {
-        char *c;
-
-        assert(p);
-
-        if (!*p)
-                return 0;
-
-        c = path_join(root_dir, *p);
-        if (!c)
-                return -ENOMEM;
-
-        free_and_replace(*p, c);
-        return 0;
-}
-
-static int patch_root_prefix_strv(char **l, const char *root_dir) {
-        char **i;
-        int r;
-
-        if (!root_dir)
-                return 0;
-
-        STRV_FOREACH(i, l) {
-                r = patch_root_prefix(i, root_dir);
-                if (r < 0)
-                        return r;
-        }
-
-        return 0;
-}
-
-int lookup_paths_init(
-                LookupPaths *p,
-                UnitFileScope scope,
-                LookupPathsFlags flags,
-                const char *root_dir) {
-
-        _cleanup_(rmdir_and_freep) char *tempdir = NULL;
-        _cleanup_free_ char
-                *root = NULL,
-                *persistent_config = NULL, *runtime_config = NULL,
-                *global_persistent_config = NULL, *global_runtime_config = NULL,
-                *generator = NULL, *generator_early = NULL, *generator_late = NULL,
-                *transient = NULL,
-                *persistent_control = NULL, *runtime_control = NULL,
-                *persistent_attached = NULL, *runtime_attached = NULL;
-        bool append = false; /* Add items from SYSTEMD_UNIT_PATH before normal directories */
-        _cleanup_strv_free_ char **paths = NULL;
-        const char *e;
-        int r;
-
-        assert(p);
-        assert(scope >= 0);
-        assert(scope < _UNIT_FILE_SCOPE_MAX);
-
-#if HAVE_SPLIT_USR
-        flags |= LOOKUP_PATHS_SPLIT_USR;
-#endif
-
-        if (!empty_or_root(root_dir)) {
-                if (scope == UNIT_FILE_USER)
-                        return -EINVAL;
-
-                r = is_dir(root_dir, true);
-                if (r < 0)
-                        return r;
-                if (r == 0)
-                        return -ENOTDIR;
-
-                root = strdup(root_dir);
-                if (!root)
-                        return -ENOMEM;
-        }
-
-        if (flags & LOOKUP_PATHS_TEMPORARY_GENERATED) {
-                r = mkdtemp_malloc("/tmp/systemd-temporary-XXXXXX", &tempdir);
-                if (r < 0)
-                        return log_debug_errno(r, "Failed to create temporary directory: %m");
-        }
-
-        /* Note: when XDG_RUNTIME_DIR is not set this will not return -ENXIO, but simply set runtime_config to NULL */
-        r = acquire_config_dirs(scope, &persistent_config, &runtime_config);
-        if (r < 0)
-                return r;
-
-        if (scope == UNIT_FILE_USER) {
-                r = acquire_config_dirs(UNIT_FILE_GLOBAL, &global_persistent_config, &global_runtime_config);
-                if (r < 0)
-                        return r;
-        }
-
-        if ((flags & LOOKUP_PATHS_EXCLUDE_GENERATED) == 0) {
-                /* Note: if XDG_RUNTIME_DIR is not set, this will fail completely with ENXIO */
-                r = acquire_generator_dirs(scope, tempdir,
-                                           &generator, &generator_early, &generator_late);
-                if (r < 0 && !IN_SET(r, -EOPNOTSUPP, -ENXIO))
-                        return r;
-        }
-
-        /* Note: if XDG_RUNTIME_DIR is not set, this will fail completely with ENXIO */
-        r = acquire_transient_dir(scope, tempdir, &transient);
-        if (r < 0 && !IN_SET(r, -EOPNOTSUPP, -ENXIO))
-                return r;
-
-        /* Note: when XDG_RUNTIME_DIR is not set this will not return -ENXIO, but simply set runtime_control to NULL */
-        r = acquire_control_dirs(scope, &persistent_control, &runtime_control);
-        if (r < 0 && r != -EOPNOTSUPP)
-                return r;
-
-        r = acquire_attached_dirs(scope, &persistent_attached, &runtime_attached);
-        if (r < 0 && r != -EOPNOTSUPP)
-                return r;
-
-        /* First priority is whatever has been passed to us via env vars */
-        e = getenv("SYSTEMD_UNIT_PATH");
-        if (e) {
-                const char *k;
-
-                k = endswith(e, ":");
-                if (k) {
-                        e = strndupa(e, k - e);
-                        append = true;
-                }
-
-                /* FIXME: empty components in other places should be rejected. */
-
-                r = path_split_and_make_absolute(e, &paths);
-                if (r < 0)
-                        return r;
-        }
-
-        if (!paths || append) {
-                /* Let's figure something out. */
-
-                _cleanup_strv_free_ char **add = NULL;
-
-                /* For the user units we include share/ in the search
-                 * path in order to comply with the XDG basedir spec.
-                 * For the system stuff we avoid such nonsense. OTOH
-                 * we include /lib in the search path for the system
-                 * stuff but avoid it for user stuff. */
-
-                switch (scope) {
-
-                case UNIT_FILE_SYSTEM:
-                        add = strv_new(
-                                        /* If you modify this you also want to modify
-                                         * systemdsystemunitpath= in systemd.pc.in! */
-                                        STRV_IFNOTNULL(persistent_control),
-                                        STRV_IFNOTNULL(runtime_control),
-                                        STRV_IFNOTNULL(transient),
-                                        STRV_IFNOTNULL(generator_early),
-                                        persistent_config,
-                                        SYSTEM_CONFIG_UNIT_PATH,
-                                        "/etc/systemd/system",
-                                        STRV_IFNOTNULL(persistent_attached),
-                                        runtime_config,
-                                        "/run/systemd/system",
-                                        STRV_IFNOTNULL(runtime_attached),
-                                        STRV_IFNOTNULL(generator),
-                                        "/usr/local/lib/systemd/system",
-                                        SYSTEM_DATA_UNIT_PATH,
-                                        "/usr/lib/systemd/system",
-                                        STRV_IFNOTNULL(flags & LOOKUP_PATHS_SPLIT_USR ? "/lib/systemd/system" : NULL),
-                                        STRV_IFNOTNULL(generator_late));
-                        break;
-
-                case UNIT_FILE_GLOBAL:
-                        add = strv_new(
-                                        /* If you modify this you also want to modify
-                                         * systemduserunitpath= in systemd.pc.in, and
-                                         * the arrays in user_dirs() above! */
-                                        STRV_IFNOTNULL(persistent_control),
-                                        STRV_IFNOTNULL(runtime_control),
-                                        STRV_IFNOTNULL(transient),
-                                        STRV_IFNOTNULL(generator_early),
-                                        persistent_config,
-                                        USER_CONFIG_UNIT_PATH,
-                                        "/etc/systemd/user",
-                                        runtime_config,
-                                        "/run/systemd/user",
-                                        STRV_IFNOTNULL(generator),
-                                        "/usr/local/share/systemd/user",
-                                        "/usr/share/systemd/user",
-                                        "/usr/local/lib/systemd/user",
-                                        USER_DATA_UNIT_PATH,
-                                        "/usr/lib/systemd/user",
-                                        STRV_IFNOTNULL(generator_late));
-                        break;
-
-                case UNIT_FILE_USER:
-                        add = user_dirs(persistent_config, runtime_config,
-                                        global_persistent_config, global_runtime_config,
-                                        generator, generator_early, generator_late,
-                                        transient,
-                                        persistent_control, runtime_control);
-                        break;
-
-                default:
-                        assert_not_reached("Hmm, unexpected scope?");
-                }
-
-                if (!add)
-                        return -ENOMEM;
-
-                if (paths) {
-                        r = strv_extend_strv(&paths, add, true);
-                        if (r < 0)
-                                return r;
-                } else
-                        /* Small optimization: if paths is NULL (and it usually is), we can simply assign 'add' to it,
-                         * and don't have to copy anything */
-                        paths = TAKE_PTR(add);
-        }
-
-        r = patch_root_prefix(&persistent_config, root);
-        if (r < 0)
-                return r;
-        r = patch_root_prefix(&runtime_config, root);
-        if (r < 0)
-                return r;
-
-        r = patch_root_prefix(&generator, root);
-        if (r < 0)
-                return r;
-        r = patch_root_prefix(&generator_early, root);
-        if (r < 0)
-                return r;
-        r = patch_root_prefix(&generator_late, root);
-        if (r < 0)
-                return r;
-
-        r = patch_root_prefix(&transient, root);
-        if (r < 0)
-                return r;
-
-        r = patch_root_prefix(&persistent_control, root);
-        if (r < 0)
-                return r;
-        r = patch_root_prefix(&runtime_control, root);
-        if (r < 0)
-                return r;
-
-        r = patch_root_prefix(&persistent_attached, root);
-        if (r < 0)
-                return r;
-        r = patch_root_prefix(&runtime_attached, root);
-        if (r < 0)
-                return r;
-
-        r = patch_root_prefix_strv(paths, root);
-        if (r < 0)
-                return -ENOMEM;
-
-        *p = (LookupPaths) {
-                .search_path = strv_uniq(TAKE_PTR(paths)),
-
-                .persistent_config = TAKE_PTR(persistent_config),
-                .runtime_config = TAKE_PTR(runtime_config),
-
-                .generator = TAKE_PTR(generator),
-                .generator_early = TAKE_PTR(generator_early),
-                .generator_late = TAKE_PTR(generator_late),
-
-                .transient = TAKE_PTR(transient),
-
-                .persistent_control = TAKE_PTR(persistent_control),
-                .runtime_control = TAKE_PTR(runtime_control),
-
-                .persistent_attached = TAKE_PTR(persistent_attached),
-                .runtime_attached = TAKE_PTR(runtime_attached),
-
-                .root_dir = TAKE_PTR(root),
-                .temporary_dir = TAKE_PTR(tempdir),
-        };
-
-        return 0;
-}
-
-void lookup_paths_free(LookupPaths *p) {
-        if (!p)
-                return;
-
-        p->search_path = strv_free(p->search_path);
-
-        p->persistent_config = mfree(p->persistent_config);
-        p->runtime_config = mfree(p->runtime_config);
-
-        p->persistent_attached = mfree(p->persistent_attached);
-        p->runtime_attached = mfree(p->runtime_attached);
-
-        p->generator = mfree(p->generator);
-        p->generator_early = mfree(p->generator_early);
-        p->generator_late = mfree(p->generator_late);
-
-        p->transient = mfree(p->transient);
-
-        p->persistent_control = mfree(p->persistent_control);
-        p->runtime_control = mfree(p->runtime_control);
-
-        p->root_dir = mfree(p->root_dir);
-        p->temporary_dir = mfree(p->temporary_dir);
-}
-
-void lookup_paths_log(LookupPaths *p) {
-        assert(p);
-
-        if (strv_isempty(p->search_path)) {
-                log_debug("Ignoring unit files.");
-                p->search_path = strv_free(p->search_path);
-        } else {
-                _cleanup_free_ char *t;
-
-                t = strv_join(p->search_path, "\n\t");
-                log_debug("Looking for unit files in (higher priority first):\n\t%s", strna(t));
-        }
-}
-
-int lookup_paths_mkdir_generator(LookupPaths *p) {
-        int r, q;
-
-        assert(p);
-
-        if (!p->generator || !p->generator_early || !p->generator_late)
-                return -EINVAL;
-
-        r = mkdir_p_label(p->generator, 0755);
-
-        q = mkdir_p_label(p->generator_early, 0755);
-        if (q < 0 && r >= 0)
-                r = q;
-
-        q = mkdir_p_label(p->generator_late, 0755);
-        if (q < 0 && r >= 0)
-                r = q;
-
-        return r;
-}
-
-void lookup_paths_trim_generator(LookupPaths *p) {
-        assert(p);
-
-        /* Trim empty dirs */
-
-        if (p->generator)
-                (void) rmdir(p->generator);
-        if (p->generator_early)
-                (void) rmdir(p->generator_early);
-        if (p->generator_late)
-                (void) rmdir(p->generator_late);
-}
-
-void lookup_paths_flush_generator(LookupPaths *p) {
-        assert(p);
-
-        /* Flush the generated unit files in full */
-
-        if (p->generator)
-                (void) rm_rf(p->generator, REMOVE_ROOT|REMOVE_PHYSICAL);
-        if (p->generator_early)
-                (void) rm_rf(p->generator_early, REMOVE_ROOT|REMOVE_PHYSICAL);
-        if (p->generator_late)
-                (void) rm_rf(p->generator_late, REMOVE_ROOT|REMOVE_PHYSICAL);
-
-        if (p->temporary_dir)
-                (void) rm_rf(p->temporary_dir, REMOVE_ROOT|REMOVE_PHYSICAL);
-}
-
-char **generator_binary_paths(UnitFileScope scope) {
-
-        switch (scope) {
-
-        case UNIT_FILE_SYSTEM:
-                return strv_new("/run/systemd/system-generators",
-                                "/etc/systemd/system-generators",
-                                "/usr/local/lib/systemd/system-generators",
-                                SYSTEM_GENERATOR_PATH);
-
-        case UNIT_FILE_GLOBAL:
-        case UNIT_FILE_USER:
-                return strv_new("/run/systemd/user-generators",
-                                "/etc/systemd/user-generators",
-                                "/usr/local/lib/systemd/user-generators",
-                                USER_GENERATOR_PATH);
-
-        default:
-                assert_not_reached("Hmm, unexpected scope.");
-        }
-}
diff --git a/src/shared/path-lookup.h b/src/shared/path-lookup.h
deleted file mode 100644 (file)
index f0762d2..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1+ */
-#pragma once
-
-#include <stdbool.h>
-
-typedef struct LookupPaths LookupPaths;
-
-#include "unit-file.h"
-#include "macro.h"
-
-typedef enum LookupPathsFlags {
-        LOOKUP_PATHS_EXCLUDE_GENERATED   = 1 << 0,
-        LOOKUP_PATHS_TEMPORARY_GENERATED = 1 << 1,
-        LOOKUP_PATHS_SPLIT_USR           = 1 << 2,
-} LookupPathsFlags;
-
-struct LookupPaths {
-        /* Where we look for unit files. This includes the individual special paths below, but also any vendor
-         * supplied, static unit file paths. */
-        char **search_path;
-
-        /* Where we shall create or remove our installation symlinks, aka "configuration", and where the user/admin
-         * shall place his own unit files. */
-        char *persistent_config;
-        char *runtime_config;
-
-        /* Where units from a portable service image shall be placed. */
-        char *persistent_attached;
-        char *runtime_attached;
-
-        /* Where to place generated unit files (i.e. those a "generator" tool generated). Note the special semantics of
-         * this directory: the generators are flushed each time a "systemctl daemon-reload" is issued. The user should
-         * not alter these directories directly. */
-        char *generator;
-        char *generator_early;
-        char *generator_late;
-
-        /* Where to place transient unit files (i.e. those created dynamically via the bus API). Note the special
-         * semantics of this directory: all units created transiently have their unit files removed as the transient
-         * unit is unloaded. The user should not alter this directory directly. */
-        char *transient;
-
-        /* Where the snippets created by "systemctl set-property" are placed. Note that for transient units, the
-         * snippets are placed in the transient directory though (see above). The user should not alter this directory
-         * directly. */
-        char *persistent_control;
-        char *runtime_control;
-
-        /* The root directory prepended to all items above, or NULL */
-        char *root_dir;
-
-        /* A temporary directory when running in test mode, to be nuked */
-        char *temporary_dir;
-};
-
-int lookup_paths_init(LookupPaths *p, UnitFileScope scope, LookupPathsFlags flags, const char *root_dir);
-
-int xdg_user_dirs(char ***ret_config_dirs, char ***ret_data_dirs);
-int xdg_user_runtime_dir(char **ret, const char *suffix);
-int xdg_user_config_dir(char **ret, const char *suffix);
-int xdg_user_data_dir(char **ret, const char *suffix);
-
-bool path_is_user_data_dir(const char *path);
-bool path_is_user_config_dir(const char *path);
-
-void lookup_paths_log(LookupPaths *p);
-
-int lookup_paths_mkdir_generator(LookupPaths *p);
-void lookup_paths_trim_generator(LookupPaths *p);
-void lookup_paths_flush_generator(LookupPaths *p);
-
-void lookup_paths_free(LookupPaths *p);
-
-char **generator_binary_paths(UnitFileScope scope);
index 3fcd7630db6bd429c2fd154168e7d728b84db61f..632964df4491c81de264cb1112eae0c3676b02f2 100644 (file)
@@ -151,6 +151,28 @@ char *pkcs11_token_label(const CK_TOKEN_INFO *token_info) {
         return t;
 }
 
+char *pkcs11_token_manufacturer_id(const CK_TOKEN_INFO *token_info) {
+        char *t;
+
+        t = strndup((char*) token_info->manufacturerID, sizeof(token_info->manufacturerID));
+        if (!t)
+                return NULL;
+
+        strstrip(t);
+        return t;
+}
+
+char *pkcs11_token_model(const CK_TOKEN_INFO *token_info) {
+        char *t;
+
+        t = strndup((char*) token_info->model, sizeof(token_info->model));
+        if (!t)
+                return NULL;
+
+        strstrip(t);
+        return t;
+}
+
 int pkcs11_token_login(
                 CK_FUNCTION_LIST *m,
                 CK_SESSION_HANDLE session,
@@ -165,9 +187,8 @@ int pkcs11_token_login(
         _cleanup_free_ char *token_uri_string = NULL, *token_uri_escaped = NULL, *id = NULL, *token_label = NULL;
         _cleanup_(p11_kit_uri_freep) P11KitUri *token_uri = NULL;
         CK_TOKEN_INFO updated_token_info;
-        int uri_result;
+        int uri_result, r;
         CK_RV rv;
-        int r;
 
         assert(m);
         assert(token_info);
@@ -190,7 +211,7 @@ int pkcs11_token_login(
                         return log_error_errno(SYNTHETIC_ERRNO(EIO),
                                                "Failed to log into security token '%s': %s", token_label, p11_kit_strerror(rv));
 
-                log_info("Successully logged into security token '%s' via protected authentication path.", token_label);
+                log_info("Successfully logged into security token '%s' via protected authentication path.", token_label);
                 *ret_used_pin = NULL;
                 return 0;
         }
@@ -211,28 +232,8 @@ int pkcs11_token_login(
 
         for (unsigned tries = 0; tries < 3; tries++) {
                 _cleanup_strv_free_erase_ char **passwords = NULL;
-                _cleanup_free_ char *text = NULL;
                 char **i, *e;
 
-                if (FLAGS_SET(token_info->flags, CKF_USER_PIN_FINAL_TRY))
-                        r = asprintf(&text,
-                                     "Please enter correct PIN for security token '%s' in order to unlock %s (final try):",
-                                     token_label, friendly_name);
-                else if (FLAGS_SET(token_info->flags, CKF_USER_PIN_COUNT_LOW))
-                        r = asprintf(&text,
-                                     "PIN has been entered incorrectly previously, please enter correct PIN for security token '%s' in order to unlock %s:",
-                                     token_label, friendly_name);
-                else if (tries == 0)
-                        r = asprintf(&text,
-                                     "Please enter PIN for security token '%s' in order to unlock %s:",
-                                     token_label, friendly_name);
-                else
-                        r = asprintf(&text,
-                                     "Please enter PIN for security token '%s' in order to unlock %s (try #%u):",
-                                     token_label, friendly_name, tries+1);
-                if (r < 0)
-                        return log_oom();
-
                 e = getenv("PIN");
                 if (e) {
                         passwords = strv_new(e);
@@ -243,6 +244,27 @@ int pkcs11_token_login(
                         if (unsetenv("PIN") < 0)
                                 return log_error_errno(errno, "Failed to unset $PIN: %m");
                 } else {
+                        _cleanup_free_ char *text = NULL;
+
+                        if (FLAGS_SET(token_info->flags, CKF_USER_PIN_FINAL_TRY))
+                                r = asprintf(&text,
+                                             "Please enter correct PIN for security token '%s' in order to unlock %s (final try):",
+                                             token_label, friendly_name);
+                        else if (FLAGS_SET(token_info->flags, CKF_USER_PIN_COUNT_LOW))
+                                r = asprintf(&text,
+                                             "PIN has been entered incorrectly previously, please enter correct PIN for security token '%s' in order to unlock %s:",
+                                             token_label, friendly_name);
+                        else if (tries == 0)
+                                r = asprintf(&text,
+                                             "Please enter PIN for security token '%s' in order to unlock %s:",
+                                             token_label, friendly_name);
+                        else
+                                r = asprintf(&text,
+                                             "Please enter PIN for security token '%s' in order to unlock %s (try #%u):",
+                                             token_label, friendly_name, tries+1);
+                        if (r < 0)
+                                return log_oom();
+
                         /* We never cache PINs, simply because it's fatal if we use wrong PINs, since usually there are only 3 tries */
                         r = ask_password_auto(text, icon_name, id, keyname, until, 0, &passwords);
                         if (r < 0)
@@ -702,7 +724,6 @@ static int token_process(
         assert(m);
         assert(slot_info);
         assert(token_info);
-        assert(search_uri);
 
         token_label = pkcs11_token_label(token_info);
         if (!token_label)
@@ -740,7 +761,6 @@ static int slot_process(
         CK_RV rv;
 
         assert(m);
-        assert(search_uri);
 
         /* We return -EAGAIN for all failures we can attribute to a specific slot in some way, so that the
          * caller might try other slots before giving up. */
@@ -786,7 +806,7 @@ static int slot_process(
                 return -EAGAIN;
         }
 
-        if (!p11_kit_uri_match_token_info(search_uri, &token_info)) {
+        if (search_uri && !p11_kit_uri_match_token_info(search_uri, &token_info)) {
                 log_debug("Found non-matching token with URI %s.", token_uri_string);
                 return -EAGAIN;
         }
@@ -820,7 +840,6 @@ static int module_process(
         int r;
 
         assert(m);
-        assert(search_uri);
 
         /* We ignore most errors from modules here, in order to skip over faulty modules: one faulty module
          * should not have the effect that we don't try the others anymore. We indicate such per-module
@@ -883,14 +902,14 @@ int pkcs11_find_token(
         _cleanup_(p11_kit_uri_freep) P11KitUri *search_uri = NULL;
         int r;
 
-        assert(pkcs11_uri);
-
         /* Execute the specified callback for each matching token found. If nothing is found returns
          * -EAGAIN. Logs about all errors, except for EAGAIN, which the caller has to log about. */
 
-        r = uri_from_string(pkcs11_uri, &search_uri);
-        if (r < 0)
-                return log_error_errno(r, "Failed to parse PKCS#11 URI '%s': %m", pkcs11_uri);
+        if (pkcs11_uri) {
+                r = uri_from_string(pkcs11_uri, &search_uri);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to parse PKCS#11 URI '%s': %m", pkcs11_uri);
+        }
 
         modules = p11_kit_modules_load_and_initialize(0);
         if (!modules)
index 46791eb23b74c1a627282e9da3c63ad179d343ca..959e7c3e0d9c90f63760ab1898aae978beb81dcc 100644 (file)
@@ -27,6 +27,8 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(CK_FUNCTION_LIST**, p11_kit_modules_finalize_and_rel
 CK_RV pkcs11_get_slot_list_malloc(CK_FUNCTION_LIST *m, CK_SLOT_ID **ret_slotids, CK_ULONG *ret_n_slotids);
 
 char *pkcs11_token_label(const CK_TOKEN_INFO *token_info);
+char *pkcs11_token_manufacturer_id(const CK_TOKEN_INFO *token_info);
+char *pkcs11_token_model(const CK_TOKEN_INFO *token_info);
 
 int pkcs11_token_login(CK_FUNCTION_LIST *m, CK_SESSION_HANDLE session, CK_SLOT_ID slotid, const CK_TOKEN_INFO *token_info, const char *friendly_name, const char *icon_name, const char *keyname, usec_t until, char **ret_used_pin);
 
index 5772918c374b21ae825d69d03910006b74d51d45..69bae6883c26032a8a952a32abb98a3a1a8cba87 100644 (file)
@@ -18,7 +18,7 @@
 #include "terminal-util.h"
 #include "util.h"
 
-static bool urlify_enabled(void) {
+bool urlify_enabled(void) {
         static int cached_urlify_enabled = -1;
 
         /* Unfortunately 'less' doesn't support links like this yet 😭, hence let's disable this as long as there's a
index 12ab9acf58ba41fcb3a039d2fbc98d31ab2af590..b3057ae6b8868fb5f98c6177f32d93bc0c5f69d1 100644 (file)
@@ -5,6 +5,8 @@ void print_separator(void);
 
 int file_url_from_path(const char *path, char **ret);
 
+bool urlify_enabled(void);
+
 int terminal_urlify(const char *url, const char *text, char **ret);
 int terminal_urlify_path(const char *path, const char *text, char **ret);
 int terminal_urlify_man(const char *page, const char *section, char **ret);
index 5d76299179b9248d06b8b00dc60efc8120439643..888f685aed9ee678fa7f2774fa70265a382ac2a5 100644 (file)
@@ -55,12 +55,12 @@ int read_reboot_parameter(char **parameter) {
 int reboot_with_parameter(RebootFlags flags) {
         int r;
 
-        /* Reboots the system with a parameter that is read from /run/systemd/reboot-param. Returns 0 if REBOOT_DRY_RUN
-         * was set and the actual reboot operation was hence skipped. If REBOOT_FALLBACK is set and the reboot with
-         * parameter doesn't work out a fallback to classic reboot() is attempted. If REBOOT_FALLBACK is not set, 0 is
-         * returned instead, which should be considered indication for the caller to fall back to reboot() on its own,
-         * or somehow else deal with this. If REBOOT_LOG is specified will log about what it is going to do, as well as
-         * all errors. */
+        /* Reboots the system with a parameter that is read from /run/systemd/reboot-param. Returns 0 if
+         * REBOOT_DRY_RUN was set and the actual reboot operation was hence skipped. If REBOOT_FALLBACK is
+         * set and the reboot with parameter doesn't work out a fallback to classic reboot() is attempted. If
+         * REBOOT_FALLBACK is not set, 0 is returned instead, which should be considered indication for the
+         * caller to fall back to reboot() on its own, or somehow else deal with this. If REBOOT_LOG is
+         * specified will log about what it is going to do, as well as all errors. */
 
         if (detect_container() == 0) {
                 _cleanup_free_ char *parameter = NULL;
@@ -71,7 +71,6 @@ int reboot_with_parameter(RebootFlags flags) {
                                        "Failed to read reboot parameter file, ignoring: %m");
 
                 if (!isempty(parameter)) {
-
                         log_full(flags & REBOOT_LOG ? LOG_INFO : LOG_DEBUG,
                                  "Rebooting with argument '%s'.", parameter);
 
index 0abd289fecf9cf50dd3bf9b1601fa4848ac7569b..24a17a2ffef8873aae63155a1ba43c0cd7a1a22f 100644 (file)
@@ -41,7 +41,6 @@ int resize_fs(int fd, uint64_t sz, uint64_t *ret_size) {
                         *ret_size = u * sfs.f_bsize;
 
         } else if (is_fs_type(&sfs, BTRFS_SUPER_MAGIC)) {
-                _unused_ int r;
                 struct btrfs_ioctl_vol_args args = {};
 
                 /* 256M is the minimize size enforced by the btrfs kernel code when resizing (which is
@@ -54,8 +53,7 @@ int resize_fs(int fd, uint64_t sz, uint64_t *ret_size) {
 
                 sz -= sz % sfs.f_bsize;
 
-                r = snprintf(args.name, sizeof(args.name), "%" PRIu64, sz);
-                assert((size_t) r < sizeof(args.name));
+                xsprintf(args.name, "%" PRIu64, sz);
 
                 if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0)
                         return -errno;
index acf1f3dade9a2d20aa275c5ca89489d424deea49..0524f1a74f1868680d390658e3b212a8a062b53e 100644 (file)
@@ -81,3 +81,12 @@ bool dns_server_address_valid(int family, const union in_addr_union *sa);
 
 const char* dns_cache_mode_to_string(DnsCacheMode p) _const_;
 DnsCacheMode dns_cache_mode_from_string(const char *s) _pure_;
+
+/* A resolv.conf file containing the DNS server and domain data we learnt from uplink, i.e. the full uplink data */
+#define PRIVATE_UPLINK_RESOLV_CONF "/run/systemd/resolve/resolv.conf"
+
+/* A resolv.conf file containing the domain data we learnt from uplink, but our own DNS server address. */
+#define PRIVATE_STUB_RESOLV_CONF "/run/systemd/resolve/stub-resolv.conf"
+
+/* A static resolv.conf file containing no domains, but only our own DNS server address */
+#define PRIVATE_STATIC_RESOLV_CONF ROOTLIBEXECDIR "/resolv.conf"
index 320b1767c566a950c7a102fb7e0946d02cf368e8..a8dd069a758dbb82edd9266bddb63e1cbadcee49 100644 (file)
@@ -24,7 +24,7 @@
 
 const uint32_t seccomp_local_archs[] = {
 
-        /* Note: always list the native arch we are compiled as last, so that users can blacklist seccomp(), but our own calls to it still succeed */
+        /* Note: always list the native arch we are compiled as last, so that users can deny-list seccomp(), but our own calls to it still succeed */
 
 #if defined(__x86_64__) && defined(__ILP32__)
                 SCMP_ARCH_X86,
@@ -1112,7 +1112,7 @@ int seccomp_parse_syscall_filter(
 
                 /* If we previously wanted to forbid a syscall and now
                  * we want to allow it, then remove it from the list. */
-                if (!(flags & SECCOMP_PARSE_INVERT) == !!(flags & SECCOMP_PARSE_WHITELIST)) {
+                if (!(flags & SECCOMP_PARSE_INVERT) == !!(flags & SECCOMP_PARSE_ALLOW_LIST)) {
                         r = hashmap_put(filter, INT_TO_PTR(id + 1), INT_TO_PTR(errno_num));
                         if (r < 0)
                                 switch (r) {
@@ -1315,7 +1315,7 @@ int seccomp_protect_syslog(void) {
         return 0;
 }
 
-int seccomp_restrict_address_families(Set *address_families, bool whitelist) {
+int seccomp_restrict_address_families(Set *address_families, bool allow_list) {
         uint32_t arch;
         int r;
 
@@ -1362,13 +1362,13 @@ int seccomp_restrict_address_families(Set *address_families, bool whitelist) {
                 if (r < 0)
                         return r;
 
-                if (whitelist) {
+                if (allow_list) {
                         int af, first = 0, last = 0;
                         void *afp;
 
-                        /* If this is a whitelist, we first block the address families that are out of range and then
-                         * everything that is not in the set. First, we find the lowest and highest address family in
-                         * the set. */
+                        /* If this is an allow list, we first block the address families that are out of
+                         * range and then everything that is not in the set. First, we find the lowest and
+                         * highest address family in the set. */
 
                         SET_FOREACH(afp, address_families, i) {
                                 af = PTR_TO_INT(afp);
@@ -1448,9 +1448,8 @@ int seccomp_restrict_address_families(Set *address_families, bool whitelist) {
                 } else {
                         void *af;
 
-                        /* If this is a blacklist, then generate one rule for
-                         * each address family that are then combined in OR
-                         * checks. */
+                        /* If this is a deny list, then generate one rule for each address family that are
+                         * then combined in OR checks. */
 
                         SET_FOREACH(af, address_families, i) {
 
@@ -1506,11 +1505,11 @@ int seccomp_restrict_realtime(void) {
                         return r;
 
                 /* Go through all policies with lower values than that, and block them -- unless they appear in the
-                 * whitelist. */
+                 * allow list. */
                 for (p = 0; p < max_policy; p++) {
                         bool good = false;
 
-                        /* Check if this is in the whitelist. */
+                        /* Check if this is in the allow list. */
                         for (i = 0; i < ELEMENTSOF(permitted_policies); i++)
                                 if (permitted_policies[i] == p) {
                                         good = true;
@@ -1533,8 +1532,8 @@ int seccomp_restrict_realtime(void) {
                         }
                 }
 
-                /* Blacklist all other policies, i.e. the ones with higher values. Note that all comparisons are
-                 * unsigned here, hence no need no check for < 0 values. */
+                /* Deny-list all other policies, i.e. the ones with higher values. Note that all comparisons
+                 * are unsigned here, hence no need no check for < 0 values. */
                 r = seccomp_rule_add_exact(
                                 seccomp,
                                 SCMP_ACT_ERRNO(EPERM),
@@ -1742,17 +1741,13 @@ int seccomp_restrict_archs(Set *archs) {
         return 0;
 }
 
-int parse_syscall_archs(char **l, Set **archs) {
-        _cleanup_set_free_ Set *_archs = NULL;
+int parse_syscall_archs(char **l, Set **ret_archs) {
+        _cleanup_set_free_ Set *archs = NULL;
         char **s;
         int r;
 
         assert(l);
-        assert(archs);
-
-        r = set_ensure_allocated(&_archs, NULL);
-        if (r < 0)
-                return r;
+        assert(ret_archs);
 
         STRV_FOREACH(s, l) {
                 uint32_t a;
@@ -1761,13 +1756,12 @@ int parse_syscall_archs(char **l, Set **archs) {
                 if (r < 0)
                         return -EINVAL;
 
-                r = set_put(_archs, UINT32_TO_PTR(a + 1));
+                r = set_ensure_put(&archs, NULL, UINT32_TO_PTR(a + 1));
                 if (r < 0)
                         return -ENOMEM;
         }
 
-        *archs = TAKE_PTR(_archs);
-
+        *ret_archs = TAKE_PTR(archs);
         return 0;
 }
 
@@ -2002,6 +1996,22 @@ static int seccomp_restrict_sxid(scmp_filter_ctx seccomp, mode_t m) {
         else
                 any = true;
 
+#if defined(__SNR_openat2)
+        /* The new openat2() system call can't be filtered sensibly, since it moves the flags parameter into
+         * an indirect structure. Let's block it entirely for now. That should be a reasonably OK thing to do
+         * for now, since openat2() is very new and code generally needs fallback logic anyway to be
+         * compatible with kernels that are not absolutely recent. */
+        r = seccomp_rule_add_exact(
+                        seccomp,
+                        SCMP_ACT_ERRNO(EPERM),
+                        SCMP_SYS(openat2),
+                        0);
+        if (r < 0)
+                log_debug_errno(r, "Failed to add filter for openat2: %m");
+        else
+                any = true;
+#endif
+
         r = seccomp_rule_add_exact(
                         seccomp,
                         SCMP_ACT_ERRNO(EPERM),
index 0b48e74a87fe15e5abca7adb8a30d6ce26b0f4d4..ef970434c6b0ab9fc100f585bf6255a77eb33f52 100644 (file)
@@ -66,7 +66,7 @@ int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Hashmap* set, u
 
 typedef enum SeccompParseFlags {
         SECCOMP_PARSE_INVERT     = 1 << 0,
-        SECCOMP_PARSE_WHITELIST  = 1 << 1,
+        SECCOMP_PARSE_ALLOW_LIST = 1 << 1,
         SECCOMP_PARSE_LOG        = 1 << 2,
         SECCOMP_PARSE_PERMISSIVE = 1 << 3,
 } SeccompParseFlags;
@@ -83,7 +83,7 @@ int seccomp_restrict_archs(Set *archs);
 int seccomp_restrict_namespaces(unsigned long retain);
 int seccomp_protect_sysctl(void);
 int seccomp_protect_syslog(void);
-int seccomp_restrict_address_families(Set *address_families, bool whitelist);
+int seccomp_restrict_address_families(Set *address_families, bool allow_list);
 int seccomp_restrict_realtime(void);
 int seccomp_memory_deny_write_execute(void);
 int seccomp_lock_personality(unsigned long personality);
@@ -105,6 +105,6 @@ extern const uint32_t seccomp_local_archs[];
 
 DEFINE_TRIVIAL_CLEANUP_FUNC(scmp_filter_ctx, seccomp_release);
 
-int parse_syscall_archs(char **l, Set **archs);
+int parse_syscall_archs(char **l, Set **ret_archs);
 
 uint32_t scmp_act_kill_process(void);
diff --git a/src/shared/service-util.c b/src/shared/service-util.c
new file mode 100644 (file)
index 0000000..c9b684f
--- /dev/null
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <getopt.h>
+#include <stdio.h>
+
+#include "alloc-util.h"
+#include "pretty-print.h"
+#include "service-util.h"
+#include "terminal-util.h"
+#include "util.h"
+
+static int help(const char *program_path, const char *service, const char *description, bool bus_introspect) {
+        _cleanup_free_ char *link = NULL;
+        int r;
+
+        r = terminal_urlify_man(service, "8", &link);
+        if (r < 0)
+                return log_oom();
+
+        printf("%s [OPTIONS...]\n\n"
+               "%s%s%s\n\n"
+               "This program takes no positional arguments.\n\n"
+               "%sOptions%s:\n"
+               "  -h --help                 Show this help\n"
+               "     --version              Show package version\n"
+               "     --bus-introspect=PATH  Write D-Bus XML introspection data\n"
+               "\nSee the %s for details.\n"
+               , program_path
+               , ansi_highlight(), description, ansi_normal()
+               , ansi_underline(), ansi_normal()
+               , link
+        );
+
+        return 0; /* No further action */
+}
+
+int service_parse_argv(
+                const char *service,
+                const char *description,
+                const BusObjectImplementation* const* bus_objects,
+                int argc, char *argv[]) {
+
+        enum {
+                ARG_VERSION = 0x100,
+                ARG_BUS_INTROSPECT,
+        };
+
+        static const struct option options[] = {
+                { "help",           no_argument,       NULL, 'h'                },
+                { "version",        no_argument,       NULL, ARG_VERSION        },
+                { "bus-introspect", required_argument, NULL, ARG_BUS_INTROSPECT },
+                {}
+        };
+
+        int c;
+
+        assert(argc >= 0);
+        assert(argv);
+
+        while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
+                switch(c) {
+
+                case 'h':
+                        return help(argv[0], service, description, bus_objects);
+
+                case ARG_VERSION:
+                        return version();
+
+                case ARG_BUS_INTROSPECT:
+                        return bus_introspect_implementations(
+                                        stdout,
+                                        optarg,
+                                        bus_objects);
+
+                case '?':
+                        return -EINVAL;
+
+                default:
+                        assert_not_reached("Unknown option code.");
+                }
+
+        if (optind < argc)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "This program takes no arguments.");
+
+        return 1; /* Further action */
+}
diff --git a/src/shared/service-util.h b/src/shared/service-util.h
new file mode 100644 (file)
index 0000000..928c596
--- /dev/null
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "bus-object.h"
+
+int service_parse_argv(
+                const char *service,
+                const char *description,
+                const BusObjectImplementation* const* bus_objects,
+                int argc, char *argv[]);
index f8e23f953bbe5f06b127266b06f9cc10983a8f3e..0dccc8f9700491e5d7b1705065e75ca66a1a09ac 100644 (file)
@@ -59,10 +59,14 @@ int parse_sleep_config(SleepConfig **ret_sleep_config) {
                 {}
         };
 
-        (void) config_parse_many_nulstr(PKGSYSCONFDIR "/sleep.conf",
-                                        CONF_PATHS_NULSTR("systemd/sleep.conf.d"),
-                                        "Sleep\0", config_item_table_lookup, items,
-                                        CONFIG_PARSE_WARN, NULL);
+        (void) config_parse_many_nulstr(
+                        PKGSYSCONFDIR "/sleep.conf",
+                        CONF_PATHS_NULSTR("systemd/sleep.conf.d"),
+                        "Sleep\0",
+                        config_item_table_lookup, items,
+                        CONFIG_PARSE_WARN,
+                        NULL,
+                        NULL);
 
         /* use default values unless set */
         sc->allow_suspend = allow_suspend != 0;
@@ -121,10 +125,16 @@ int can_sleep_state(char **types) {
 
                 k = strlen(*type);
                 FOREACH_WORD_SEPARATOR(word, l, p, WHITESPACE, state)
-                        if (l == k && memcmp(word, *type, l) == 0)
+                        if (l == k && memcmp(word, *type, l) == 0) {
+                                log_debug("Sleep mode \"%s\" is supported by the kernel.", *type);
                                 return true;
+                        }
         }
 
+        if (DEBUG_LOGGING) {
+                _cleanup_free_ char *t = strv_join(types, "/");
+                log_debug("Sleep mode %s not supported by the kernel, sorry.", strnull(t));
+        }
         return false;
 }
 
@@ -213,7 +223,7 @@ static int swap_device_to_device_id(const SwapEntry *swap, dev_t *ret_dev) {
 }
 
 /*
- * Attempt to calculate the swap file offset on supported filesystems. On unsuported
+ * Attempt to calculate the swap file offset on supported filesystems. On unsupported
  * filesystems, a debug message is logged and ret_offset is set to UINT64_MAX.
  */
 static int calculate_swap_file_offset(const SwapEntry *swap, uint64_t *ret_offset) {
index 5177137b995d0744e6d4e6b37670a45041cb1773..d72a70503a94efaaa91c39ed1828fc153a40336c 100644 (file)
@@ -243,8 +243,9 @@ int socket_address_parse_netlink(SocketAddress *a, const char *s) {
         assert(a);
         assert(s);
 
-        zero(*a);
-        a->type = SOCK_RAW;
+        *a = (SocketAddress) {
+                .type = SOCK_RAW,
+        };
 
         r = extract_first_word(&s, &word, NULL, 0);
         if (r < 0)
@@ -326,38 +327,197 @@ int make_socket_fd(int log_level, const char* address, int type, int flags) {
         return fd;
 }
 
-int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret_addr, int *ret_ifindex) {
-        _cleanup_free_ char *buf = NULL;
-        const char *suffix;
-        int r, ifindex = 0;
+int in_addr_port_ifindex_name_from_string_auto(
+                const char *s,
+                int *ret_family,
+                union in_addr_union *ret_address,
+                uint16_t *ret_port,
+                int *ret_ifindex,
+                char **ret_server_name) {
+
+        _cleanup_free_ char *buf1 = NULL, *buf2 = NULL, *name = NULL;
+        int family, ifindex = 0, r;
+        union in_addr_union a;
+        uint16_t port = 0;
+        const char *m;
 
         assert(s);
-        assert(family);
-        assert(ret_addr);
 
-        /* Similar to in_addr_from_string_auto() but also parses an optionally appended IPv6 zone suffix ("scope id")
-         * if one is found. */
+        /* This accepts the following:
+         * 192.168.0.1:53#example.com
+         * [2001:4860:4860::8888]:53%eth0#example.com */
+
+        /* if ret_port is NULL, then strings with port cannot be specified.
+         * Also, if ret_server_name is NULL, then server_name cannot be specified. */
+
+        m = strchr(s, '#');
+        if (m) {
+                if (!ret_server_name)
+                        return -EINVAL;
+
+                if (isempty(m + 1))
+                        return -EINVAL;
+
+                name = strdup(m + 1);
+                if (!name)
+                        return -ENOMEM;
+
+                s = buf1 = strndup(s, m - s);
+                if (!buf1)
+                        return -ENOMEM;
+        }
+
+        m = strchr(s, '%');
+        if (m) {
+                if (isempty(m + 1))
+                        return -EINVAL;
 
-        suffix = strchr(s, '%');
-        if (suffix) {
                 if (ret_ifindex) {
                         /* If we shall return the interface index, try to parse it */
-                        ifindex = resolve_interface(NULL, suffix + 1);
+                        ifindex = resolve_interface(NULL, m + 1);
                         if (ifindex < 0)
                                 return ifindex;
                 }
 
-                s = buf = strndup(s, suffix - s);
-                if (!buf)
+                s = buf2 = strndup(s, m - s);
+                if (!buf2)
                         return -ENOMEM;
         }
 
-        r = in_addr_from_string_auto(s, family, ret_addr);
-        if (r < 0)
-                return r;
+        m = strrchr(s, ':');
+        if (m) {
+                if (*s == '[') {
+                        _cleanup_free_ char *ip_str = NULL;
+
+                        if (!ret_port)
+                                return -EINVAL;
+
+                        if (*(m - 1) != ']')
+                                return -EINVAL;
+
+                        family = AF_INET6;
+
+                        r = parse_ip_port(m + 1, &port);
+                        if (r < 0)
+                                return r;
+
+                        ip_str = strndup(s + 1, m - s - 2);
+                        if (!ip_str)
+                                return -ENOMEM;
+
+                        r = in_addr_from_string(family, ip_str, &a);
+                        if (r < 0)
+                                return r;
+                } else {
+                        /* First try to parse the string as IPv6 address without port number */
+                        r = in_addr_from_string(AF_INET6, s, &a);
+                        if (r < 0) {
+                                /* Then the input should be IPv4 address with port number */
+                                _cleanup_free_ char *ip_str = NULL;
+
+                                if (!ret_port)
+                                        return -EINVAL;
+
+                                family = AF_INET;
+
+                                ip_str = strndup(s, m - s);
+                                if (!ip_str)
+                                        return -ENOMEM;
+
+                                r = in_addr_from_string(family, ip_str, &a);
+                                if (r < 0)
+                                        return r;
+
+                                r = parse_ip_port(m + 1, &port);
+                                if (r < 0)
+                                        return r;
+                        } else
+                                family = AF_INET6;
+                }
+        } else {
+                family = AF_INET;
+                r = in_addr_from_string(family, s, &a);
+                if (r < 0)
+                        return r;
+        }
 
+        if (ret_family)
+                *ret_family = family;
+        if (ret_address)
+                *ret_address = a;
+        if (ret_port)
+                *ret_port = port;
         if (ret_ifindex)
                 *ret_ifindex = ifindex;
+        if (ret_server_name)
+                *ret_server_name = TAKE_PTR(name);
 
         return r;
 }
+
+struct in_addr_full *in_addr_full_free(struct in_addr_full *a) {
+        if (!a)
+                return NULL;
+
+        free(a->server_name);
+        free(a->cached_server_string);
+        return mfree(a);
+}
+
+int in_addr_full_new(int family, union in_addr_union *a, uint16_t port, int ifindex, const char *server_name, struct in_addr_full **ret) {
+        _cleanup_free_ char *name = NULL;
+        struct in_addr_full *x;
+
+        assert(ret);
+
+        if (!isempty(server_name)) {
+                name = strdup(server_name);
+                if (!name)
+                        return -ENOMEM;
+        }
+
+        x = new(struct in_addr_full, 1);
+        if (!x)
+                return -ENOMEM;
+
+        *x = (struct in_addr_full) {
+                .family = family,
+                .address = *a,
+                .port = port,
+                .ifindex = ifindex,
+                .server_name = TAKE_PTR(name),
+        };
+
+        *ret = x;
+        return 0;
+}
+
+int in_addr_full_new_from_string(const char *s, struct in_addr_full **ret) {
+        _cleanup_free_ char *server_name = NULL;
+        int family, ifindex, r;
+        union in_addr_union a;
+        uint16_t port;
+
+        assert(s);
+
+        r = in_addr_port_ifindex_name_from_string_auto(s, &family, &a, &port, &ifindex, &server_name);
+        if (r < 0)
+                return r;
+
+        return in_addr_full_new(family, &a, port, ifindex, server_name, ret);
+}
+
+const char *in_addr_full_to_string(struct in_addr_full *a) {
+        assert(a);
+
+        if (!a->cached_server_string)
+                (void) in_addr_port_ifindex_name_to_string(
+                                a->family,
+                                &a->address,
+                                a->port,
+                                a->ifindex,
+                                a->server_name,
+                                &a->cached_server_string);
+
+        return a->cached_server_string;
+}
index fa58409d6182f77c937cf52f149d2574d969faa7..9517f6dd6d14ff5e879b903c1228dcb325c02a64 100644 (file)
@@ -20,4 +20,31 @@ int socket_address_parse_netlink(SocketAddress *a, const char *s);
 bool socket_address_is(const SocketAddress *a, const char *s, int type);
 bool socket_address_is_netlink(const SocketAddress *a, const char *s);
 
-int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex);
+int in_addr_port_ifindex_name_from_string_auto(
+                const char *s,
+                int *ret_family,
+                union in_addr_union *ret_address,
+                uint16_t *ret_port,
+                int *ret_ifindex,
+                char **ret_server_name);
+static inline int in_addr_ifindex_name_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex, char **server_name) {
+        return in_addr_port_ifindex_name_from_string_auto(s, family, ret, NULL, ifindex, server_name);
+}
+static inline int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex) {
+        return in_addr_ifindex_name_from_string_auto(s, family, ret, ifindex, NULL);
+}
+
+struct in_addr_full {
+        int family;
+        union in_addr_union address;
+        uint16_t port;
+        int ifindex;
+        char *server_name;
+        char *cached_server_string; /* Should not be handled directly, but through in_addr_full_to_string(). */
+};
+
+struct in_addr_full *in_addr_full_free(struct in_addr_full *a);
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct in_addr_full*, in_addr_full_free);
+int in_addr_full_new(int family, union in_addr_union *a, uint16_t port, int ifindex, const char *server_name, struct in_addr_full **ret);
+int in_addr_full_new_from_string(const char *s, struct in_addr_full **ret);
+const char *in_addr_full_to_string(struct in_addr_full *a);
index b036ff67ddbdf3690a2f4a8961416fba8b84c49e..112cf6f8fb524dae0a7e10590145604694d92134 100644 (file)
@@ -9,10 +9,12 @@
 #include "sd-id128.h"
 
 #include "alloc-util.h"
+#include "architecture.h"
 #include "format-util.h"
 #include "fs-util.h"
 #include "hostname-util.h"
 #include "macro.h"
+#include "os-util.h"
 #include "specifier.h"
 #include "string-util.h"
 #include "strv.h"
@@ -158,6 +160,17 @@ int specifier_host_name(char specifier, const void *data, const void *userdata,
         return 0;
 }
 
+int specifier_short_host_name(char specifier, const void *data, const void *userdata, char **ret) {
+        char *n;
+
+        n = gethostname_short_malloc();
+        if (!n)
+                return -ENOMEM;
+
+        *ret = n;
+        return 0;
+}
+
 int specifier_kernel_release(char specifier, const void *data, const void *userdata, char **ret) {
         struct utsname uts;
         char *n;
@@ -175,6 +188,52 @@ int specifier_kernel_release(char specifier, const void *data, const void *userd
         return 0;
 }
 
+int specifier_architecture(char specifier, const void *data, const void *userdata, char **ret) {
+        char *t;
+
+        t = strdup(architecture_to_string(uname_architecture()));
+        if (!t)
+                return -ENOMEM;
+
+        *ret = t;
+        return 0;
+}
+
+static int specifier_os_release_common(const char *field, char **ret) {
+        char *t = NULL;
+        int r;
+
+        r = parse_os_release(NULL, field, &t, NULL);
+        if (r < 0)
+                return r;
+        if (!t) {
+                /* fields in /etc/os-release might quite possibly be missing, even if everything is entirely
+                 * valid otherwise. Let's hence return "" in that case. */
+                t = strdup("");
+                if (!t)
+                        return -ENOMEM;
+        }
+
+        *ret = t;
+        return 0;
+}
+
+int specifier_os_id(char specifier, const void *data, const void *userdata, char **ret) {
+        return specifier_os_release_common("ID", ret);
+}
+
+int specifier_os_version_id(char specifier, const void *data, const void *userdata, char **ret) {
+        return specifier_os_release_common("VERSION_ID", ret);
+}
+
+int specifier_os_build_id(char specifier, const void *data, const void *userdata, char **ret) {
+        return specifier_os_release_common("BUILD_ID", ret);
+}
+
+int specifier_os_variant_id(char specifier, const void *data, const void *userdata, char **ret) {
+        return specifier_os_release_common("VARIANT_ID", ret);
+}
+
 int specifier_group_name(char specifier, const void *data, const void *userdata, char **ret) {
         char *t;
 
index d0221ef714a794def0f846c91af12d78e1479b4e..50c6cbd6ab466644dcf59f58a9f6c92be9a1ec9d 100644 (file)
@@ -18,7 +18,13 @@ int specifier_string(char specifier, const void *data, const void *userdata, cha
 int specifier_machine_id(char specifier, const void *data, const void *userdata, char **ret);
 int specifier_boot_id(char specifier, const void *data, const void *userdata, char **ret);
 int specifier_host_name(char specifier, const void *data, const void *userdata, char **ret);
+int specifier_short_host_name(char specifier, const void *data, const void *userdata, char **ret);
 int specifier_kernel_release(char specifier, const void *data, const void *userdata, char **ret);
+int specifier_architecture(char specifier, const void *data, const void *userdata, char **ret);
+int specifier_os_id(char specifier, const void *data, const void *userdata, char **ret);
+int specifier_os_version_id(char specifier, const void *data, const void *userdata, char **ret);
+int specifier_os_build_id(char specifier, const void *data, const void *userdata, char **ret);
+int specifier_os_variant_id(char specifier, const void *data, const void *userdata, char **ret);
 
 int specifier_group_name(char specifier, const void *data, const void *userdata, char **ret);
 int specifier_group_id(char specifier, const void *data, const void *userdata, char **ret);
index dce015151956f4627cd73f9b533dabb42a13807c..316cfcccf9d555d09612cc788a63d8667d3896c2 100644 (file)
@@ -11,7 +11,7 @@
 char *sysctl_normalize(char *s);
 int sysctl_read(const char *property, char **value);
 int sysctl_write(const char *property, const char *value);
-int sysctl_writef(const char *propery, const char *format, ...) _printf_(2, 3);
+int sysctl_writef(const char *property, const char *format, ...) _printf_(2, 3);
 
 int sysctl_read_ip_property(int af, const char *ifname, const char *property, char **ret);
 int sysctl_write_ip_property(int af, const char *ifname, const char *property, const char *value);
index 96b5b805a9678e514383924a4ef113a3d6f9da96..ff662ecfe0cdc27393234470e82da99ae8b2cf4c 100644 (file)
@@ -21,6 +21,7 @@
 #include "env-util.h"
 #include "fs-util.h"
 #include "log.h"
+#include "namespace-util.h"
 #include "path-util.h"
 #include "random-util.h"
 #include "strv.h"
@@ -58,21 +59,25 @@ static void load_testdata_env(void) {
                 setenv(*k, *v, 0);
 }
 
-const char* get_testdata_dir(void) {
-        const char *env;
+int get_testdata_dir(const char *suffix, char **ret) {
+        const char *dir;
+        char *p;
 
         load_testdata_env();
 
         /* if the env var is set, use that */
-        env = getenv("SYSTEMD_TEST_DATA");
-        if (!env)
-                env = SYSTEMD_TEST_DATA;
-        if (access(env, F_OK) < 0) {
-                fprintf(stderr, "ERROR: $SYSTEMD_TEST_DATA directory [%s] does not exist\n", env);
-                exit(EXIT_FAILURE);
-        }
+        dir = getenv("SYSTEMD_TEST_DATA");
+        if (!dir)
+                dir = SYSTEMD_TEST_DATA;
+        if (access(dir, F_OK) < 0)
+                return log_error_errno(errno, "ERROR: $SYSTEMD_TEST_DATA directory [%s] not accessible: %m", dir);
 
-        return env;
+        p = path_join(dir, suffix);
+        if (!p)
+                return log_oom();
+
+        *ret = p;
+        return 0;
 }
 
 const char* get_catalog_dir(void) {
@@ -133,10 +138,7 @@ bool have_namespaces(void) {
 
         if (pid == 0) {
                 /* child */
-                if (unshare(CLONE_NEWNS) < 0)
-                        _exit(EXIT_FAILURE);
-
-                if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0)
+                if (detach_mount_namespace() < 0)
                         _exit(EXIT_FAILURE);
 
                 _exit(EXIT_SUCCESS);
index 5a6fd53f36b978f5725e5177d7a2b8acda65e3c1..6817ef4860653c98403cdc04ec5a8ae79aea5087 100644 (file)
@@ -20,7 +20,7 @@ static inline bool manager_errno_skip_test(int r) {
 
 char* setup_fake_runtime_dir(void);
 int enter_cgroup_subroot(char **ret_cgroup);
-const char* get_testdata_dir(void);
+int get_testdata_dir(const char *suffix, char **ret);
 const char* get_catalog_dir(void);
 bool slow_tests_enabled(void);
 void test_setup_logging(int level);
index ea96f5b49dccc428de07869156fb8e6c78fad35e..419c73318fbbcac35ffca3e483f6c736a94d9af2 100644 (file)
@@ -7,6 +7,7 @@
 #include "env-file.h"
 #include "log.h"
 #include "parse-util.h"
+#include "signal-util.h"
 #include "string-table.h"
 #include "string-util.h"
 #include "udev-util.h"
@@ -23,9 +24,10 @@ int udev_parse_config_full(
                 unsigned *ret_children_max,
                 usec_t *ret_exec_delay_usec,
                 usec_t *ret_event_timeout_usec,
-                ResolveNameTiming *ret_resolve_name_timing) {
+                ResolveNameTiming *ret_resolve_name_timing,
+                int *ret_timeout_signal) {
 
-        _cleanup_free_ char *log_val = NULL, *children_max = NULL, *exec_delay = NULL, *event_timeout = NULL, *resolve_names = NULL;
+        _cleanup_free_ char *log_val = NULL, *children_max = NULL, *exec_delay = NULL, *event_timeout = NULL, *resolve_names = NULL, *timeout_signal = NULL;
         int r;
 
         r = parse_env_file(NULL, "/etc/udev/udev.conf",
@@ -33,7 +35,8 @@ int udev_parse_config_full(
                            "children_max", &children_max,
                            "exec_delay", &exec_delay,
                            "event_timeout", &event_timeout,
-                           "resolve_names", &resolve_names);
+                           "resolve_names", &resolve_names,
+                           "timeout_signal", &timeout_signal);
         if (r == -ENOENT)
                 return 0;
         if (r < 0)
@@ -65,21 +68,21 @@ int udev_parse_config_full(
                 r = safe_atou(children_max, ret_children_max);
                 if (r < 0)
                         log_syntax(NULL, LOG_WARNING, "/etc/udev/udev.conf", 0, r,
-                                   "failed to set parse children_max=%s, ignoring: %m", children_max);
+                                   "failed to parse children_max=%s, ignoring: %m", children_max);
         }
 
         if (ret_exec_delay_usec && exec_delay) {
                 r = parse_sec(exec_delay, ret_exec_delay_usec);
                 if (r < 0)
                         log_syntax(NULL, LOG_WARNING, "/etc/udev/udev.conf", 0, r,
-                                   "failed to set parse exec_delay=%s, ignoring: %m", exec_delay);
+                                   "failed to parse exec_delay=%s, ignoring: %m", exec_delay);
         }
 
         if (ret_event_timeout_usec && event_timeout) {
                 r = parse_sec(event_timeout, ret_event_timeout_usec);
                 if (r < 0)
                         log_syntax(NULL, LOG_WARNING, "/etc/udev/udev.conf", 0, r,
-                                   "failed to set parse event_timeout=%s, ignoring: %m", event_timeout);
+                                   "failed to parse event_timeout=%s, ignoring: %m", event_timeout);
         }
 
         if (ret_resolve_name_timing && resolve_names) {
@@ -88,11 +91,20 @@ int udev_parse_config_full(
                 t = resolve_name_timing_from_string(resolve_names);
                 if (t < 0)
                         log_syntax(NULL, LOG_WARNING, "/etc/udev/udev.conf", 0, r,
-                                   "failed to set parse resolve_names=%s, ignoring.", resolve_names);
+                                   "failed to parse resolve_names=%s, ignoring.", resolve_names);
                 else
                         *ret_resolve_name_timing = t;
         }
 
+        if (ret_timeout_signal && timeout_signal) {
+                r = signal_from_string(timeout_signal);
+                if (r < 0)
+                        log_syntax(NULL, LOG_WARNING, "/etc/udev/udev.conf", 0, r,
+                                   "failed to parse timeout_signal=%s, ignoring: %m", timeout_signal);
+                else
+                        *ret_timeout_signal = r;
+        }
+
         return 0;
 }
 
index f62c4c56e394b5007db4f9c0e64a3cb514a12fc8..c35d29344f731b39ecb7fedffaae7d6c4fbb203e 100644 (file)
@@ -21,10 +21,11 @@ int udev_parse_config_full(
                 unsigned *ret_children_max,
                 usec_t *ret_exec_delay_usec,
                 usec_t *ret_event_timeout_usec,
-                ResolveNameTiming *ret_resolve_name_timing);
+                ResolveNameTiming *ret_resolve_name_timing,
+                int *ret_timeout_signal);
 
 static inline int udev_parse_config(void) {
-        return udev_parse_config_full(NULL, NULL, NULL, NULL);
+        return udev_parse_config_full(NULL, NULL, NULL, NULL, NULL);
 }
 
 int device_wait_for_initialization(sd_device *device, const char *subsystem, usec_t timeout, sd_device **ret);
index 7b64bbf7f1b68ea282dd8ddc70536a2aa6ebf3f3..ed4affd6683543ea5c2d1f97f63344fe99233e92 100644 (file)
@@ -199,7 +199,7 @@ static bool lookup_paths_mtime_exclude(const LookupPaths *lp, const char *path)
                streq_ptr(path, lp->runtime_control);
 }
 
-static bool lookup_paths_mtime_good(const LookupPaths *lp, usec_t mtime) {
+bool lookup_paths_mtime_good(const LookupPaths *lp, usec_t mtime) {
         char **dir;
 
         STRV_FOREACH(dir, (char**) lp->search_path) {
@@ -229,9 +229,9 @@ static bool lookup_paths_mtime_good(const LookupPaths *lp, usec_t mtime) {
 int unit_file_build_name_map(
                 const LookupPaths *lp,
                 usec_t *cache_mtime,
-                Hashmap **ret_unit_ids_map,
-                Hashmap **ret_unit_names_map,
-                Set **ret_path_cache) {
+                Hashmap **unit_ids_map,
+                Hashmap **unit_names_map,
+                Set **path_cache) {
 
         /* Build two mappings: any name → main unit (i.e. the end result of symlink resolution), unit name →
          * all aliases (i.e. the entry for a given key is a a list of all names which point to this key). The
@@ -239,7 +239,8 @@ int unit_file_build_name_map(
          * have a key, but it is not present in the value for itself, there was an alias pointing to it, but
          * the unit itself is not loadable.
          *
-         * At the same, build a cache of paths where to find units.
+         * At the same, build a cache of paths where to find units. The non-const parameters are for input
+         * and output. Existing contents will be freed before the new contents are stored.
          */
 
         _cleanup_hashmap_free_ Hashmap *ids = NULL, *names = NULL;
@@ -253,8 +254,8 @@ int unit_file_build_name_map(
         if (cache_mtime && *cache_mtime > 0 && lookup_paths_mtime_good(lp, *cache_mtime))
                 return 0;
 
-        if (ret_path_cache) {
-                paths = set_new(&path_hash_ops);
+        if (path_cache) {
+                paths = set_new(&path_hash_ops_free);
                 if (!paths)
                         return log_oom();
         }
@@ -296,7 +297,7 @@ int unit_file_build_name_map(
                         if (!filename)
                                 return log_oom();
 
-                        if (ret_path_cache) {
+                        if (paths) {
                                 r = set_consume(paths, filename);
                                 if (r < 0)
                                         return log_oom();
@@ -418,10 +419,11 @@ int unit_file_build_name_map(
 
         if (cache_mtime)
                 *cache_mtime = mtime;
-        *ret_unit_ids_map = TAKE_PTR(ids);
-        *ret_unit_names_map = TAKE_PTR(names);
-        if (ret_path_cache)
-                *ret_path_cache = TAKE_PTR(paths);
+
+        hashmap_free_and_replace(*unit_ids_map, ids);
+        hashmap_free_and_replace(*unit_names_map, names);
+        if (path_cache)
+                set_free_and_replace(*path_cache, paths);
 
         return 1;
 }
@@ -463,7 +465,7 @@ int unit_file_find_fragment(
 
         /* The unit always has its own name if it's not a template. */
         if (IN_SET(name_type, UNIT_NAME_PLAIN, UNIT_NAME_INSTANCE)) {
-                r = set_put_strdup(names, unit_name);
+                r = set_put_strdup(&names, unit_name);
                 if (r < 0)
                         return r;
         }
@@ -493,7 +495,7 @@ int unit_file_find_fragment(
                                 if (!streq(unit_name, *t))
                                         log_debug("%s: %s has alias %s", __func__, unit_name, *t);
 
-                                r = set_put_strdup(names, *t);
+                                r = set_put_strdup(&names, *t);
                         }
                         if (r < 0)
                                 return r;
index a44ba5b05182030f54b4e5e904581df1320a4011..d6d041d714bc60f443ebc45aa90c82d0533bc72d 100644 (file)
@@ -16,6 +16,7 @@ enum UnitFileState {
         UNIT_FILE_ENABLED_RUNTIME,
         UNIT_FILE_LINKED,
         UNIT_FILE_LINKED_RUNTIME,
+        UNIT_FILE_ALIAS,
         UNIT_FILE_MASKED,
         UNIT_FILE_MASKED_RUNTIME,
         UNIT_FILE_STATIC,
@@ -42,6 +43,7 @@ bool unit_type_may_template(UnitType type) _const_;
 int unit_symlink_name_compatible(const char *symlink, const char *target, bool instance_propagation);
 int unit_validate_alias_symlink_and_warn(const char *filename, const char *target);
 
+bool lookup_paths_mtime_good(const LookupPaths *lp, usec_t mtime);
 int unit_file_build_name_map(
                 const LookupPaths *lp,
                 usec_t *ret_time,
index 0ff6d1711753d189bcac907c199da1846979a5b6..f265a2af9333e24457cd634b77cbb77ab0840090 100644 (file)
@@ -161,12 +161,16 @@ int nss_spwd_for_passwd(const struct passwd *pwd, struct spwd *ret_spwd, char **
         }
 }
 
-int nss_user_record_by_name(const char *name, UserRecord **ret) {
+int nss_user_record_by_name(
+                const char *name,
+                bool with_shadow,
+                UserRecord **ret) {
+
         _cleanup_free_ char *buf = NULL, *sbuf = NULL;
         struct passwd pwd, *result;
         bool incomplete = false;
         size_t buflen = 4096;
-        struct spwd spwd;
+        struct spwd spwd, *sresult = NULL;
         int r;
 
         assert(name);
@@ -197,13 +201,17 @@ int nss_user_record_by_name(const char *name, UserRecord **ret) {
                 buf = mfree(buf);
         }
 
-        r = nss_spwd_for_passwd(result, &spwd, &sbuf);
-        if (r < 0) {
-                log_debug_errno(r, "Failed to do shadow lookup for user %s, ignoring: %m", name);
-                incomplete = ERRNO_IS_PRIVILEGE(r);
-        }
+        if (with_shadow) {
+                r = nss_spwd_for_passwd(result, &spwd, &sbuf);
+                if (r < 0) {
+                        log_debug_errno(r, "Failed to do shadow lookup for user %s, ignoring: %m", name);
+                        incomplete = ERRNO_IS_PRIVILEGE(r);
+                } else
+                        sresult = &spwd;
+        } else
+                incomplete = true;
 
-        r = nss_passwd_to_user_record(result, r >= 0 ? &spwd : NULL, ret);
+        r = nss_passwd_to_user_record(result, sresult, ret);
         if (r < 0)
                 return r;
 
@@ -211,12 +219,16 @@ int nss_user_record_by_name(const char *name, UserRecord **ret) {
         return 0;
 }
 
-int nss_user_record_by_uid(uid_t uid, UserRecord **ret) {
+int nss_user_record_by_uid(
+                uid_t uid,
+                bool with_shadow,
+                UserRecord **ret) {
+
         _cleanup_free_ char *buf = NULL, *sbuf = NULL;
         struct passwd pwd, *result;
         bool incomplete = false;
         size_t buflen = 4096;
-        struct spwd spwd;
+        struct spwd spwd, *sresult = NULL;
         int r;
 
         assert(ret);
@@ -245,13 +257,17 @@ int nss_user_record_by_uid(uid_t uid, UserRecord **ret) {
                 buf = mfree(buf);
         }
 
-        r = nss_spwd_for_passwd(result, &spwd, &sbuf);
-        if (r < 0) {
-                log_debug_errno(r, "Failed to do shadow lookup for UID " UID_FMT ", ignoring: %m", uid);
-                incomplete = ERRNO_IS_PRIVILEGE(r);
-        }
+        if (with_shadow)  {
+                r = nss_spwd_for_passwd(result, &spwd, &sbuf);
+                if (r < 0) {
+                        log_debug_errno(r, "Failed to do shadow lookup for UID " UID_FMT ", ignoring: %m", uid);
+                        incomplete = ERRNO_IS_PRIVILEGE(r);
+                } else
+                        sresult = &spwd;
+        } else
+                incomplete = true;
 
-        r = nss_passwd_to_user_record(result, r >= 0 ? &spwd : NULL, ret);
+        r = nss_passwd_to_user_record(result, sresult, ret);
         if (r < 0)
                 return r;
 
index d5fb23ad2a294f66e3a468be2e08254f959c954b..0eb78d5b52a374a0f89e7363f90759be57e88af4 100644 (file)
@@ -11,5 +11,5 @@
 int nss_passwd_to_user_record(const struct passwd *pwd, const struct spwd *spwd, UserRecord **ret);
 int nss_spwd_for_passwd(const struct passwd *pwd, struct spwd *ret_spwd, char **ret_buffer);
 
-int nss_user_record_by_name(const char *name, UserRecord **ret);
-int nss_user_record_by_uid(uid_t uid, UserRecord **ret);
+int nss_user_record_by_name(const char *name, bool with_shadow, UserRecord **ret);
+int nss_user_record_by_uid(uid_t uid, bool with_shadow, UserRecord **ret);
index e5eff50840115eeecdeb4cadcbb06d47c87eddef..84ededd86e95b719dd211b8a1908af23399fe8a1 100644 (file)
@@ -279,7 +279,7 @@ void user_record_show(UserRecord *hr, bool show_full_group_info) {
                 printf(" Access Mode: 0%03oo\n", user_record_access_mode(hr));
 
         if (storage == USER_LUKS) {
-                printf("LUKS Discard: %s\n", yes_no(user_record_luks_discard(hr)));
+                printf("LUKS Discard: online=%s offline=%s\n", yes_no(user_record_luks_discard(hr)), yes_no(user_record_luks_offline_discard(hr)));
 
                 if (!sd_id128_is_null(hr->luks_uuid))
                         printf("   LUKS UUID: " SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(hr->luks_uuid));
@@ -340,12 +340,48 @@ void user_record_show(UserRecord *hr, bool show_full_group_info) {
 
         if (hr->disk_usage != UINT64_MAX) {
                 char buf[FORMAT_BYTES_MAX];
-                printf("  Disk Usage: %s\n", format_bytes(buf, sizeof(buf), hr->disk_usage));
+
+                if (hr->disk_size != UINT64_MAX) {
+                        unsigned permille;
+
+                        permille = (unsigned) DIV_ROUND_UP(hr->disk_usage * 1000U, hr->disk_size); /* Round up! */
+                        printf("  Disk Usage: %s (= %u.%01u%%)\n",
+                               format_bytes(buf, sizeof(buf), hr->disk_usage),
+                               permille / 10, permille % 10);
+                } else
+                        printf("  Disk Usage: %s\n", format_bytes(buf, sizeof(buf), hr->disk_usage));
         }
 
         if (hr->disk_free != UINT64_MAX) {
                 char buf[FORMAT_BYTES_MAX];
-                printf("   Disk Free: %s\n", format_bytes(buf, sizeof(buf), hr->disk_free));
+
+                if (hr->disk_size != UINT64_MAX) {
+                        const char *color_on, *color_off;
+                        unsigned permille;
+
+                        permille = (unsigned) ((hr->disk_free * 1000U) / hr->disk_size); /* Round down! */
+
+                        /* Color the output red or yellow if we are below 10% resp. 25% free. Because 10% and
+                         * 25% can be a lot of space still, let's additionally make some absolute
+                         * restrictions: 1G and 2G */
+                        if (permille <= 100U &&
+                            hr->disk_free < 1024U*1024U*1024U /* 1G */) {
+                                color_on = ansi_highlight_red();
+                                color_off = ansi_normal();
+                        } else if (permille <= 250U &&
+                                   hr->disk_free < 2U*1024U*1024U*1024U /* 2G */) {
+                                color_on = ansi_highlight_yellow();
+                                color_off = ansi_normal();
+                        } else
+                                color_on = color_off = "";
+
+                        printf("   Disk Free: %s%s (= %u.%01u%%)%s\n",
+                               color_on,
+                               format_bytes(buf, sizeof(buf), hr->disk_free),
+                               permille / 10, permille % 10,
+                               color_off);
+                } else
+                        printf("   Disk Free: %s\n", format_bytes(buf, sizeof(buf), hr->disk_free));
         }
 
         if (hr->disk_floor != UINT64_MAX) {
@@ -436,10 +472,13 @@ void user_record_show(UserRecord *hr, bool show_full_group_info) {
 
                 STRV_FOREACH(i, hr->pkcs11_token_uri)
                         printf(i == hr->pkcs11_token_uri ?
-                               "  Sec. Token: %s\n" :
+                               "PKCS11 Token: %s\n" :
                                "              %s\n", *i);
         }
 
+        if (hr->n_fido2_hmac_credential > 0)
+                printf(" FIDO2 Token: %zu\n", hr->n_fido2_hmac_credential);
+
         k = strv_length(hr->hashed_password);
         if (k == 0)
                 printf("   Passwords: %snone%s\n",
index 080c2a140d725d6151846d7bc77a6f0dc171330b..16edaa45face3bb66a17f196efa47d54ba3873c3 100644 (file)
@@ -52,6 +52,7 @@ UserRecord* user_record_new(void) {
                 .nodev = true,
                 .nosuid = true,
                 .luks_discard = -1,
+                .luks_offline_discard = -1,
                 .luks_volume_key_size = UINT64_MAX,
                 .luks_pbkdf_time_cost_usec = UINT64_MAX,
                 .luks_pbkdf_memory_cost = UINT64_MAX,
@@ -80,6 +81,7 @@ UserRecord* user_record_new(void) {
                 .password_change_inactive_usec = UINT64_MAX,
                 .password_change_now = -1,
                 .pkcs11_protected_authentication_path_permitted = -1,
+                .fido2_user_presence_permitted = -1,
         };
 
         return h;
@@ -94,6 +96,22 @@ static void pkcs11_encrypted_key_done(Pkcs11EncryptedKey *k) {
         erase_and_free(k->hashed_password);
 }
 
+static void fido2_hmac_credential_done(Fido2HmacCredential *c) {
+        if (!c)
+                return;
+
+        free(c->id);
+}
+
+static void fido2_hmac_salt_done(Fido2HmacSalt *s) {
+        if (!s)
+                return;
+
+        fido2_hmac_credential_done(&s->credential);
+        erase_and_free(s->salt);
+        erase_and_free(s->hashed_password);
+}
+
 static UserRecord* user_record_free(UserRecord *h) {
         if (!h)
                 return NULL;
@@ -119,7 +137,7 @@ static UserRecord* user_record_free(UserRecord *h) {
         strv_free_erase(h->hashed_password);
         strv_free_erase(h->ssh_authorized_keys);
         strv_free_erase(h->password);
-        strv_free_erase(h->pkcs11_pin);
+        strv_free_erase(h->token_pin);
 
         free(h->cifs_service);
         free(h->cifs_user_name);
@@ -146,6 +164,11 @@ static UserRecord* user_record_free(UserRecord *h) {
                 pkcs11_encrypted_key_done(h->pkcs11_encrypted_key + i);
         free(h->pkcs11_encrypted_key);
 
+        for (size_t i = 0; i < h->n_fido2_hmac_credential; i++)
+                fido2_hmac_credential_done(h->fido2_hmac_credential + i);
+        for (size_t i = 0; i < h->n_fido2_hmac_salt; i++)
+                fido2_hmac_salt_done(h->fido2_hmac_salt + i);
+
         json_variant_unref(h->json);
 
         return mfree(h);
@@ -619,8 +642,10 @@ static int dispatch_secret(const char *name, JsonVariant *variant, JsonDispatchF
 
         static const JsonDispatch secret_dispatch_table[] = {
                 { "password",                                   _JSON_VARIANT_TYPE_INVALID, json_dispatch_strv,     offsetof(UserRecord, password),                                       0 },
-                { "pkcs11Pin",                                  _JSON_VARIANT_TYPE_INVALID, json_dispatch_strv,     offsetof(UserRecord, pkcs11_pin),                                     0 },
+                { "tokenPin",                                   _JSON_VARIANT_TYPE_INVALID, json_dispatch_strv,     offsetof(UserRecord, token_pin),                                      0 },
+                { "pkcs11Pin",   /* legacy alias */             _JSON_VARIANT_TYPE_INVALID, json_dispatch_strv,     offsetof(UserRecord, token_pin),                                      0 },
                 { "pkcs11ProtectedAuthenticationPathPermitted", JSON_VARIANT_BOOLEAN,       json_dispatch_tristate, offsetof(UserRecord, pkcs11_protected_authentication_path_permitted), 0 },
+                { "fido2UserPresencePermitted",                 JSON_VARIANT_BOOLEAN,       json_dispatch_tristate, offsetof(UserRecord, fido2_user_presence_permitted),                  0 },
                 {},
         };
 
@@ -705,7 +730,7 @@ static int dispatch_pkcs11_key_data(const char *name, JsonVariant *variant, Json
         int r;
 
         if (json_variant_is_null(variant)) {
-                k->data = mfree(k->data);
+                k->data = erase_and_free(k->data);
                 k->size = 0;
                 return 0;
         }
@@ -765,13 +790,141 @@ static int dispatch_pkcs11_key(const char *name, JsonVariant *variant, JsonDispa
         return 0;
 }
 
+static int dispatch_fido2_hmac_credential(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+        Fido2HmacCredential *k = userdata;
+        size_t l;
+        void *b;
+        int r;
+
+        if (json_variant_is_null(variant)) {
+                k->id = mfree(k->id);
+                k->size = 0;
+                return 0;
+        }
+
+        if (!json_variant_is_string(variant))
+                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
+
+        r = unbase64mem(json_variant_string(variant), (size_t) -1, &b, &l);
+        if (r < 0)
+                return json_log(variant, flags, r, "Failed to decode FIDO2 credential ID: %m");
+
+        free_and_replace(k->id, b);
+        k->size = l;
+
+        return 0;
+}
+
+static int dispatch_fido2_hmac_credential_array(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+        UserRecord *h = userdata;
+        JsonVariant *e;
+        int r;
+
+        if (!json_variant_is_array(variant))
+                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of strings.", strna(name));
+
+        JSON_VARIANT_ARRAY_FOREACH(e, variant) {
+                Fido2HmacCredential *array;
+                size_t l;
+                void *b;
+
+                if (!json_variant_is_string(e))
+                        return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), "JSON array element is not a string.");
+
+                array = reallocarray(h->fido2_hmac_credential, h->n_fido2_hmac_credential + 1, sizeof(Fido2HmacCredential));
+                if (!array)
+                        return log_oom();
+
+                r = unbase64mem(json_variant_string(e), (size_t) -1, &b, &l);
+                if (r < 0)
+                        return json_log(variant, flags, r, "Failed to decode FIDO2 credential ID: %m");
+
+                h->fido2_hmac_credential = array;
+
+                h->fido2_hmac_credential[h->n_fido2_hmac_credential++] = (Fido2HmacCredential) {
+                        .id = b,
+                        .size = l,
+                };
+        }
+
+        return 0;
+}
+
+static int dispatch_fido2_hmac_salt_value(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+        Fido2HmacSalt *k = userdata;
+        size_t l;
+        void *b;
+        int r;
+
+        if (json_variant_is_null(variant)) {
+                k->salt = erase_and_free(k->salt);
+                k->salt_size = 0;
+                return 0;
+        }
+
+        if (!json_variant_is_string(variant))
+                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
+
+        r = unbase64mem(json_variant_string(variant), (size_t) -1, &b, &l);
+        if (r < 0)
+                return json_log(variant, flags, r, "Failed to decode FIDO2 salt: %m");
+
+        erase_and_free(k->salt);
+        k->salt = b;
+        k->salt_size = l;
+
+        return 0;
+}
+
+static int dispatch_fido2_hmac_salt(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
+        UserRecord *h = userdata;
+        JsonVariant *e;
+        int r;
+
+        if (!json_variant_is_array(variant))
+                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of objects.", strna(name));
+
+        JSON_VARIANT_ARRAY_FOREACH(e, variant) {
+                Fido2HmacSalt *array, *k;
+
+                static const JsonDispatch fido2_hmac_salt_dispatch_table[] = {
+                        { "credential",     JSON_VARIANT_STRING, dispatch_fido2_hmac_credential, offsetof(Fido2HmacSalt, credential),      JSON_MANDATORY },
+                        { "salt",           JSON_VARIANT_STRING, dispatch_fido2_hmac_salt_value, 0,                                        JSON_MANDATORY },
+                        { "hashedPassword", JSON_VARIANT_STRING, json_dispatch_string,           offsetof(Fido2HmacSalt, hashed_password), JSON_MANDATORY },
+                        {},
+                };
+
+                if (!json_variant_is_object(e))
+                        return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), "JSON array element is not an object.");
+
+                array = reallocarray(h->fido2_hmac_salt, h->n_fido2_hmac_salt + 1, sizeof(Fido2HmacSalt));
+                if (!array)
+                        return log_oom();
+
+                h->fido2_hmac_salt = array;
+                k = h->fido2_hmac_salt + h->n_fido2_hmac_salt;
+                *k = (Fido2HmacSalt) {};
+
+                r = json_dispatch(e, fido2_hmac_salt_dispatch_table, NULL, flags, k);
+                if (r < 0) {
+                        fido2_hmac_salt_done(k);
+                        return r;
+                }
+
+                h->n_fido2_hmac_salt++;
+        }
+
+        return 0;
+}
+
 static int dispatch_privileged(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
 
         static const JsonDispatch privileged_dispatch_table[] = {
-                { "passwordHint",       JSON_VARIANT_STRING,        json_dispatch_string, offsetof(UserRecord, password_hint),        0         },
-                { "hashedPassword",     _JSON_VARIANT_TYPE_INVALID, json_dispatch_strv,   offsetof(UserRecord, hashed_password),      JSON_SAFE },
-                { "sshAuthorizedKeys",  _JSON_VARIANT_TYPE_INVALID, json_dispatch_strv,   offsetof(UserRecord, ssh_authorized_keys),  0         },
-                { "pkcs11EncryptedKey", JSON_VARIANT_ARRAY,         dispatch_pkcs11_key,  0,                                          0         },
+                { "passwordHint",       JSON_VARIANT_STRING,        json_dispatch_string,     offsetof(UserRecord, password_hint),        0         },
+                { "hashedPassword",     _JSON_VARIANT_TYPE_INVALID, json_dispatch_strv,       offsetof(UserRecord, hashed_password),      JSON_SAFE },
+                { "sshAuthorizedKeys",  _JSON_VARIANT_TYPE_INVALID, json_dispatch_strv,       offsetof(UserRecord, ssh_authorized_keys),  0         },
+                { "pkcs11EncryptedKey", JSON_VARIANT_ARRAY,         dispatch_pkcs11_key,      0,                                          0         },
+                { "fido2HmacSalt",      JSON_VARIANT_ARRAY,         dispatch_fido2_hmac_salt, 0,                                          0         },
                 {},
         };
 
@@ -905,65 +1058,67 @@ int per_machine_hostname_match(JsonVariant *hns, JsonDispatchFlags flags) {
 static int dispatch_per_machine(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
 
         static const JsonDispatch per_machine_dispatch_table[] = {
-                { "matchMachineId",             _JSON_VARIANT_TYPE_INVALID, NULL,                              0,                                                   0         },
-                { "matchHostname",              _JSON_VARIANT_TYPE_INVALID, NULL,                              0,                                                   0         },
-                { "iconName",                   JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, icon_name),                     JSON_SAFE },
-                { "location",                   JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, location),                      0         },
-                { "shell",                      JSON_VARIANT_STRING,        json_dispatch_filename_or_path,    offsetof(UserRecord, shell),                         0         },
-                { "umask",                      JSON_VARIANT_UNSIGNED,      json_dispatch_umask,               offsetof(UserRecord, umask),                         0         },
-                { "environment",                JSON_VARIANT_ARRAY,         json_dispatch_environment,         offsetof(UserRecord, environment),                   0         },
-                { "timeZone",                   JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, time_zone),                     JSON_SAFE },
-                { "preferredLanguage",          JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, preferred_language),            JSON_SAFE },
-                { "niceLevel",                  _JSON_VARIANT_TYPE_INVALID, json_dispatch_nice,                offsetof(UserRecord, nice_level),                    0         },
-                { "resourceLimits",             _JSON_VARIANT_TYPE_INVALID, json_dispatch_rlimits,             offsetof(UserRecord, rlimits),                       0         },
-                { "locked",                     JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,            offsetof(UserRecord, locked),                        0         },
-                { "notBeforeUSec",              JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, not_before_usec),               0         },
-                { "notAfterUSec",               JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, not_after_usec),                0         },
-                { "storage",                    JSON_VARIANT_STRING,        json_dispatch_storage,             offsetof(UserRecord, storage),                       0         },
-                { "diskSize",                   JSON_VARIANT_UNSIGNED,      json_dispatch_disk_size,           offsetof(UserRecord, disk_size),                     0         },
-                { "diskSizeRelative",           JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, disk_size_relative),            0         },
-                { "skeletonDirectory",          JSON_VARIANT_STRING,        json_dispatch_path,                offsetof(UserRecord, skeleton_directory),            0         },
-                { "accessMode",                 JSON_VARIANT_UNSIGNED,      json_dispatch_access_mode,         offsetof(UserRecord, access_mode),                   0         },
-                { "tasksMax",                   JSON_VARIANT_UNSIGNED,      json_dispatch_tasks_or_memory_max, offsetof(UserRecord, tasks_max),                     0         },
-                { "memoryHigh",                 JSON_VARIANT_UNSIGNED,      json_dispatch_tasks_or_memory_max, offsetof(UserRecord, memory_high),                   0         },
-                { "memoryMax",                  JSON_VARIANT_UNSIGNED,      json_dispatch_tasks_or_memory_max, offsetof(UserRecord, memory_max),                    0         },
-                { "cpuWeight",                  JSON_VARIANT_UNSIGNED,      json_dispatch_weight,              offsetof(UserRecord, cpu_weight),                    0         },
-                { "ioWeight",                   JSON_VARIANT_UNSIGNED,      json_dispatch_weight,              offsetof(UserRecord, io_weight),                     0         },
-                { "mountNoDevices",             JSON_VARIANT_BOOLEAN,       json_dispatch_boolean,             offsetof(UserRecord, nodev),                         0         },
-                { "mountNoSuid",                JSON_VARIANT_BOOLEAN,       json_dispatch_boolean,             offsetof(UserRecord, nosuid),                        0         },
-                { "mountNoExecute",             JSON_VARIANT_BOOLEAN,       json_dispatch_boolean,             offsetof(UserRecord, noexec),                        0         },
-                { "cifsDomain",                 JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, cifs_domain),                   JSON_SAFE },
-                { "cifsUserName",               JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, cifs_user_name),                JSON_SAFE },
-                { "cifsService",                JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, cifs_service),                  JSON_SAFE },
-                { "imagePath",                  JSON_VARIANT_STRING,        json_dispatch_path,                offsetof(UserRecord, image_path),                    0         },
-                { "uid",                        JSON_VARIANT_UNSIGNED,      json_dispatch_uid_gid,             offsetof(UserRecord, uid),                           0         },
-                { "gid",                        JSON_VARIANT_UNSIGNED,      json_dispatch_uid_gid,             offsetof(UserRecord, gid),                           0         },
-                { "memberOf",                   JSON_VARIANT_ARRAY,         json_dispatch_user_group_list,     offsetof(UserRecord, member_of),                     JSON_RELAX},
-                { "fileSystemType",             JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, file_system_type),              JSON_SAFE },
-                { "partitionUuid",              JSON_VARIANT_STRING,        json_dispatch_id128,               offsetof(UserRecord, partition_uuid),                0         },
-                { "luksUuid",                   JSON_VARIANT_STRING,        json_dispatch_id128,               offsetof(UserRecord, luks_uuid),                     0         },
-                { "fileSystemUuid",             JSON_VARIANT_STRING,        json_dispatch_id128,               offsetof(UserRecord, file_system_uuid),              0         },
-                { "luksDiscard",                _JSON_VARIANT_TYPE_INVALID, json_dispatch_tristate,            offsetof(UserRecord, luks_discard),                  0,        },
-                { "luksCipher",                 JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, luks_cipher),                   JSON_SAFE },
-                { "luksCipherMode",             JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, luks_cipher_mode),              JSON_SAFE },
-                { "luksVolumeKeySize",          JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, luks_volume_key_size),          0         },
-                { "luksPbkdfHashAlgorithm",     JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, luks_pbkdf_hash_algorithm),     JSON_SAFE },
-                { "luksPbkdfType",              JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, luks_pbkdf_type),               JSON_SAFE },
-                { "luksPbkdfTimeCostUSec",      JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, luks_pbkdf_time_cost_usec),     0         },
-                { "luksPbkdfMemoryCost",        JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, luks_pbkdf_memory_cost),        0         },
-                { "luksPbkdfParallelThreads",   JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, luks_pbkdf_parallel_threads),   0         },
-                { "rateLimitIntervalUSec",      JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, ratelimit_interval_usec),       0         },
-                { "rateLimitBurst",             JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, ratelimit_burst),               0         },
-                { "enforcePasswordPolicy",      JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,            offsetof(UserRecord, enforce_password_policy),       0         },
-                { "autoLogin",                  JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,            offsetof(UserRecord, auto_login),                    0         },
-                { "stopDelayUSec",              JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, stop_delay_usec),               0         },
-                { "killProcesses",              JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,            offsetof(UserRecord, kill_processes),                0         },
-                { "passwordChangeMinUSec",      JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, password_change_min_usec),      0         },
-                { "passwordChangeMaxUSec",      JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, password_change_max_usec),      0         },
-                { "passwordChangeWarnUSec",     JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, password_change_warn_usec),     0         },
-                { "passwordChangeInactiveUSec", JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, password_change_inactive_usec), 0         },
-                { "passwordChangeNow",          JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,            offsetof(UserRecord, password_change_now),           0         },
-                { "pkcs11TokenUri",             JSON_VARIANT_ARRAY,         dispatch_pkcs11_uri_array,         offsetof(UserRecord, pkcs11_token_uri),              0         },
+                { "matchMachineId",             _JSON_VARIANT_TYPE_INVALID, NULL,                                 0,                                                   0         },
+                { "matchHostname",              _JSON_VARIANT_TYPE_INVALID, NULL,                                 0,                                                   0         },
+                { "iconName",                   JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, icon_name),                     JSON_SAFE },
+                { "location",                   JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, location),                      0         },
+                { "shell",                      JSON_VARIANT_STRING,        json_dispatch_filename_or_path,       offsetof(UserRecord, shell),                         0         },
+                { "umask",                      JSON_VARIANT_UNSIGNED,      json_dispatch_umask,                  offsetof(UserRecord, umask),                         0         },
+                { "environment",                JSON_VARIANT_ARRAY,         json_dispatch_environment,            offsetof(UserRecord, environment),                   0         },
+                { "timeZone",                   JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, time_zone),                     JSON_SAFE },
+                { "preferredLanguage",          JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, preferred_language),            JSON_SAFE },
+                { "niceLevel",                  _JSON_VARIANT_TYPE_INVALID, json_dispatch_nice,                   offsetof(UserRecord, nice_level),                    0         },
+                { "resourceLimits",             _JSON_VARIANT_TYPE_INVALID, json_dispatch_rlimits,                offsetof(UserRecord, rlimits),                       0         },
+                { "locked",                     JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,               offsetof(UserRecord, locked),                        0         },
+                { "notBeforeUSec",              JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, not_before_usec),               0         },
+                { "notAfterUSec",               JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, not_after_usec),                0         },
+                { "storage",                    JSON_VARIANT_STRING,        json_dispatch_storage,                offsetof(UserRecord, storage),                       0         },
+                { "diskSize",                   JSON_VARIANT_UNSIGNED,      json_dispatch_disk_size,              offsetof(UserRecord, disk_size),                     0         },
+                { "diskSizeRelative",           JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, disk_size_relative),            0         },
+                { "skeletonDirectory",          JSON_VARIANT_STRING,        json_dispatch_path,                   offsetof(UserRecord, skeleton_directory),            0         },
+                { "accessMode",                 JSON_VARIANT_UNSIGNED,      json_dispatch_access_mode,            offsetof(UserRecord, access_mode),                   0         },
+                { "tasksMax",                   JSON_VARIANT_UNSIGNED,      json_dispatch_tasks_or_memory_max,    offsetof(UserRecord, tasks_max),                     0         },
+                { "memoryHigh",                 JSON_VARIANT_UNSIGNED,      json_dispatch_tasks_or_memory_max,    offsetof(UserRecord, memory_high),                   0         },
+                { "memoryMax",                  JSON_VARIANT_UNSIGNED,      json_dispatch_tasks_or_memory_max,    offsetof(UserRecord, memory_max),                    0         },
+                { "cpuWeight",                  JSON_VARIANT_UNSIGNED,      json_dispatch_weight,                 offsetof(UserRecord, cpu_weight),                    0         },
+                { "ioWeight",                   JSON_VARIANT_UNSIGNED,      json_dispatch_weight,                 offsetof(UserRecord, io_weight),                     0         },
+                { "mountNoDevices",             JSON_VARIANT_BOOLEAN,       json_dispatch_boolean,                offsetof(UserRecord, nodev),                         0         },
+                { "mountNoSuid",                JSON_VARIANT_BOOLEAN,       json_dispatch_boolean,                offsetof(UserRecord, nosuid),                        0         },
+                { "mountNoExecute",             JSON_VARIANT_BOOLEAN,       json_dispatch_boolean,                offsetof(UserRecord, noexec),                        0         },
+                { "cifsDomain",                 JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, cifs_domain),                   JSON_SAFE },
+                { "cifsUserName",               JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, cifs_user_name),                JSON_SAFE },
+                { "cifsService",                JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, cifs_service),                  JSON_SAFE },
+                { "imagePath",                  JSON_VARIANT_STRING,        json_dispatch_path,                   offsetof(UserRecord, image_path),                    0         },
+                { "uid",                        JSON_VARIANT_UNSIGNED,      json_dispatch_uid_gid,                offsetof(UserRecord, uid),                           0         },
+                { "gid",                        JSON_VARIANT_UNSIGNED,      json_dispatch_uid_gid,                offsetof(UserRecord, gid),                           0         },
+                { "memberOf",                   JSON_VARIANT_ARRAY,         json_dispatch_user_group_list,        offsetof(UserRecord, member_of),                     JSON_RELAX},
+                { "fileSystemType",             JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, file_system_type),              JSON_SAFE },
+                { "partitionUuid",              JSON_VARIANT_STRING,        json_dispatch_id128,                  offsetof(UserRecord, partition_uuid),                0         },
+                { "luksUuid",                   JSON_VARIANT_STRING,        json_dispatch_id128,                  offsetof(UserRecord, luks_uuid),                     0         },
+                { "fileSystemUuid",             JSON_VARIANT_STRING,        json_dispatch_id128,                  offsetof(UserRecord, file_system_uuid),              0         },
+                { "luksDiscard",                _JSON_VARIANT_TYPE_INVALID, json_dispatch_tristate,               offsetof(UserRecord, luks_discard),                  0,        },
+                { "luksOfflineDiscard",         _JSON_VARIANT_TYPE_INVALID, json_dispatch_tristate,               offsetof(UserRecord, luks_offline_discard),          0,        },
+                { "luksCipher",                 JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, luks_cipher),                   JSON_SAFE },
+                { "luksCipherMode",             JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, luks_cipher_mode),              JSON_SAFE },
+                { "luksVolumeKeySize",          JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, luks_volume_key_size),          0         },
+                { "luksPbkdfHashAlgorithm",     JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, luks_pbkdf_hash_algorithm),     JSON_SAFE },
+                { "luksPbkdfType",              JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, luks_pbkdf_type),               JSON_SAFE },
+                { "luksPbkdfTimeCostUSec",      JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, luks_pbkdf_time_cost_usec),     0         },
+                { "luksPbkdfMemoryCost",        JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, luks_pbkdf_memory_cost),        0         },
+                { "luksPbkdfParallelThreads",   JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, luks_pbkdf_parallel_threads),   0         },
+                { "rateLimitIntervalUSec",      JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, ratelimit_interval_usec),       0         },
+                { "rateLimitBurst",             JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, ratelimit_burst),               0         },
+                { "enforcePasswordPolicy",      JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,               offsetof(UserRecord, enforce_password_policy),       0         },
+                { "autoLogin",                  JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,               offsetof(UserRecord, auto_login),                    0         },
+                { "stopDelayUSec",              JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, stop_delay_usec),               0         },
+                { "killProcesses",              JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,               offsetof(UserRecord, kill_processes),                0         },
+                { "passwordChangeMinUSec",      JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, password_change_min_usec),      0         },
+                { "passwordChangeMaxUSec",      JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, password_change_max_usec),      0         },
+                { "passwordChangeWarnUSec",     JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, password_change_warn_usec),     0         },
+                { "passwordChangeInactiveUSec", JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, password_change_inactive_usec), 0         },
+                { "passwordChangeNow",          JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,               offsetof(UserRecord, password_change_now),           0         },
+                { "pkcs11TokenUri",             JSON_VARIANT_ARRAY,         dispatch_pkcs11_uri_array,            offsetof(UserRecord, pkcs11_token_uri),              0         },
+                { "fido2HmacCredential",        JSON_VARIANT_ARRAY,         dispatch_fido2_hmac_credential_array, 0,                                                   0         },
                 {},
         };
 
@@ -1057,7 +1212,34 @@ static int dispatch_status(const char *name, JsonVariant *variant, JsonDispatchF
         return json_dispatch(m, status_dispatch_table, NULL, flags, userdata);
 }
 
+int user_record_build_image_path(UserStorage storage, const char *user_name_and_realm, char **ret) {
+        const char *suffix;
+        char *z;
+
+        assert(storage >= 0);
+        assert(user_name_and_realm);
+        assert(ret);
+
+        if (storage == USER_LUKS)
+                suffix = ".home";
+        else if (IN_SET(storage, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT))
+                suffix = ".homedir";
+        else {
+                *ret = NULL;
+                return 0;
+        }
+
+        z = strjoin("/home/", user_name_and_realm, suffix);
+        if (!z)
+                return -ENOMEM;
+
+        *ret = z;
+        return 1;
+}
+
 static int user_record_augment(UserRecord *h, JsonDispatchFlags json_flags) {
+        int r;
+
         assert(h);
 
         if (!FLAGS_SET(h->mask, USER_RECORD_REGULAR))
@@ -1071,7 +1253,7 @@ static int user_record_augment(UserRecord *h, JsonDispatchFlags json_flags) {
                         return json_log_oom(h->json, json_flags);
         }
 
-        /* Let's add in the following automatisms only for regular users, they dont make sense for any others */
+        /* Let's add in the following automatisms only for regular users, they don't make sense for any others */
         if (user_record_disposition(h) != USER_REGULAR)
                 return 0;
 
@@ -1082,22 +1264,9 @@ static int user_record_augment(UserRecord *h, JsonDispatchFlags json_flags) {
         }
 
         if (!h->image_path && !h->image_path_auto) {
-                const char *suffix;
-                UserStorage storage;
-
-                storage = user_record_storage(h);
-                if (storage == USER_LUKS)
-                        suffix = ".home";
-                else if (IN_SET(storage, USER_DIRECTORY, USER_SUBVOLUME, USER_FSCRYPT))
-                        suffix = ".homedir";
-                else
-                        suffix = NULL;
-
-                if (suffix) {
-                        h->image_path_auto = strjoin("/home/", user_record_user_name_and_realm(h), suffix);
-                        if (!h->image_path_auto)
-                                return json_log_oom(h->json, json_flags);
-                }
+                r = user_record_build_image_path(user_record_storage(h), user_record_user_name_and_realm(h), &h->image_path_auto);
+                if (r < 0)
+                        return json_log(h->json, json_flags, r, "Failed to determine default image path: %m");
         }
 
         return 0;
@@ -1231,83 +1400,85 @@ int user_group_record_mangle(
 int user_record_load(UserRecord *h, JsonVariant *v, UserRecordLoadFlags load_flags) {
 
         static const JsonDispatch user_dispatch_table[] = {
-                { "userName",                   JSON_VARIANT_STRING,        json_dispatch_user_group_name,     offsetof(UserRecord, user_name),                     JSON_RELAX},
-                { "realm",                      JSON_VARIANT_STRING,        json_dispatch_realm,               offsetof(UserRecord, realm),                         0         },
-                { "realName",                   JSON_VARIANT_STRING,        json_dispatch_gecos,               offsetof(UserRecord, real_name),                     0         },
-                { "emailAddress",               JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, email_address),                 JSON_SAFE },
-                { "iconName",                   JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, icon_name),                     JSON_SAFE },
-                { "location",                   JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, location),                      0         },
-                { "disposition",                JSON_VARIANT_STRING,        json_dispatch_user_disposition,    offsetof(UserRecord, disposition),                   0         },
-                { "lastChangeUSec",             JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, last_change_usec),              0         },
-                { "lastPasswordChangeUSec",     JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, last_password_change_usec),     0         },
-                { "shell",                      JSON_VARIANT_STRING,        json_dispatch_filename_or_path,    offsetof(UserRecord, shell),                         0         },
-                { "umask",                      JSON_VARIANT_UNSIGNED,      json_dispatch_umask,               offsetof(UserRecord, umask),                         0         },
-                { "environment",                JSON_VARIANT_ARRAY,         json_dispatch_environment,         offsetof(UserRecord, environment),                   0         },
-                { "timeZone",                   JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, time_zone),                     JSON_SAFE },
-                { "preferredLanguage",          JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, preferred_language),            JSON_SAFE },
-                { "niceLevel",                  _JSON_VARIANT_TYPE_INVALID, json_dispatch_nice,                offsetof(UserRecord, nice_level),                    0         },
-                { "resourceLimits",             _JSON_VARIANT_TYPE_INVALID, json_dispatch_rlimits,             offsetof(UserRecord, rlimits),                       0         },
-                { "locked",                     JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,            offsetof(UserRecord, locked),                        0         },
-                { "notBeforeUSec",              JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, not_before_usec),               0         },
-                { "notAfterUSec",               JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, not_after_usec),                0         },
-                { "storage",                    JSON_VARIANT_STRING,        json_dispatch_storage,             offsetof(UserRecord, storage),                       0         },
-                { "diskSize",                   JSON_VARIANT_UNSIGNED,      json_dispatch_disk_size,           offsetof(UserRecord, disk_size),                     0         },
-                { "diskSizeRelative",           JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, disk_size_relative),            0         },
-                { "skeletonDirectory",          JSON_VARIANT_STRING,        json_dispatch_path,                offsetof(UserRecord, skeleton_directory),            0         },
-                { "accessMode",                 JSON_VARIANT_UNSIGNED,      json_dispatch_access_mode,         offsetof(UserRecord, access_mode),                   0         },
-                { "tasksMax",                   JSON_VARIANT_UNSIGNED,      json_dispatch_tasks_or_memory_max, offsetof(UserRecord, tasks_max),                     0         },
-                { "memoryHigh",                 JSON_VARIANT_UNSIGNED,      json_dispatch_tasks_or_memory_max, offsetof(UserRecord, memory_high),                   0         },
-                { "memoryMax",                  JSON_VARIANT_UNSIGNED,      json_dispatch_tasks_or_memory_max, offsetof(UserRecord, memory_max),                    0         },
-                { "cpuWeight",                  JSON_VARIANT_UNSIGNED,      json_dispatch_weight,              offsetof(UserRecord, cpu_weight),                    0         },
-                { "ioWeight",                   JSON_VARIANT_UNSIGNED,      json_dispatch_weight,              offsetof(UserRecord, io_weight),                     0         },
-                { "mountNoDevices",             JSON_VARIANT_BOOLEAN,       json_dispatch_boolean,             offsetof(UserRecord, nodev),                         0         },
-                { "mountNoSuid",                JSON_VARIANT_BOOLEAN,       json_dispatch_boolean,             offsetof(UserRecord, nosuid),                        0         },
-                { "mountNoExecute",             JSON_VARIANT_BOOLEAN,       json_dispatch_boolean,             offsetof(UserRecord, noexec),                        0         },
-                { "cifsDomain",                 JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, cifs_domain),                   JSON_SAFE },
-                { "cifsUserName",               JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, cifs_user_name),                JSON_SAFE },
-                { "cifsService",                JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, cifs_service),                  JSON_SAFE },
-                { "imagePath",                  JSON_VARIANT_STRING,        json_dispatch_path,                offsetof(UserRecord, image_path),                    0         },
-                { "homeDirectory",              JSON_VARIANT_STRING,        json_dispatch_home_directory,      offsetof(UserRecord, home_directory),                0         },
-                { "uid",                        JSON_VARIANT_UNSIGNED,      json_dispatch_uid_gid,             offsetof(UserRecord, uid),                           0         },
-                { "gid",                        JSON_VARIANT_UNSIGNED,      json_dispatch_uid_gid,             offsetof(UserRecord, gid),                           0         },
-                { "memberOf",                   JSON_VARIANT_ARRAY,         json_dispatch_user_group_list,     offsetof(UserRecord, member_of),                     JSON_RELAX},
-                { "fileSystemType",             JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, file_system_type),              JSON_SAFE },
-                { "partitionUuid",              JSON_VARIANT_STRING,        json_dispatch_id128,               offsetof(UserRecord, partition_uuid),                0         },
-                { "luksUuid",                   JSON_VARIANT_STRING,        json_dispatch_id128,               offsetof(UserRecord, luks_uuid),                     0         },
-                { "fileSystemUuid",             JSON_VARIANT_STRING,        json_dispatch_id128,               offsetof(UserRecord, file_system_uuid),              0         },
-                { "luksDiscard",                _JSON_VARIANT_TYPE_INVALID, json_dispatch_tristate,            offsetof(UserRecord, luks_discard),                  0         },
-                { "luksCipher",                 JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, luks_cipher),                   JSON_SAFE },
-                { "luksCipherMode",             JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, luks_cipher_mode),              JSON_SAFE },
-                { "luksVolumeKeySize",          JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, luks_volume_key_size),          0         },
-                { "luksPbkdfHashAlgorithm",     JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, luks_pbkdf_hash_algorithm),     JSON_SAFE },
-                { "luksPbkdfType",              JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, luks_pbkdf_type),               JSON_SAFE },
-                { "luksPbkdfTimeCostUSec",      JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, luks_pbkdf_time_cost_usec),     0         },
-                { "luksPbkdfMemoryCost",        JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, luks_pbkdf_memory_cost),        0         },
-                { "luksPbkdfParallelThreads",   JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, luks_pbkdf_parallel_threads),   0         },
-                { "service",                    JSON_VARIANT_STRING,        json_dispatch_string,              offsetof(UserRecord, service),                       JSON_SAFE },
-                { "rateLimitIntervalUSec",      JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, ratelimit_interval_usec),       0         },
-                { "rateLimitBurst",             JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, ratelimit_burst),               0         },
-                { "enforcePasswordPolicy",      JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,            offsetof(UserRecord, enforce_password_policy),       0         },
-                { "autoLogin",                  JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,            offsetof(UserRecord, auto_login),                    0         },
-                { "stopDelayUSec",              JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, stop_delay_usec),               0         },
-                { "killProcesses",              JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,            offsetof(UserRecord, kill_processes),                0         },
-                { "passwordChangeMinUSec",      JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, password_change_min_usec),      0         },
-                { "passwordChangeMaxUSec",      JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, password_change_max_usec),      0         },
-                { "passwordChangeWarnUSec",     JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, password_change_warn_usec),     0         },
-                { "passwordChangeInactiveUSec", JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,              offsetof(UserRecord, password_change_inactive_usec), 0         },
-                { "passwordChangeNow",          JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,            offsetof(UserRecord, password_change_now),           0         },
-                { "pkcs11TokenUri",             JSON_VARIANT_ARRAY,         dispatch_pkcs11_uri_array,         offsetof(UserRecord, pkcs11_token_uri),              0         },
-
-                { "secret",                     JSON_VARIANT_OBJECT,        dispatch_secret,                   0,                                                   0         },
-                { "privileged",                 JSON_VARIANT_OBJECT,        dispatch_privileged,               0,                                                   0         },
+                { "userName",                   JSON_VARIANT_STRING,        json_dispatch_user_group_name,        offsetof(UserRecord, user_name),                     JSON_RELAX},
+                { "realm",                      JSON_VARIANT_STRING,        json_dispatch_realm,                  offsetof(UserRecord, realm),                         0         },
+                { "realName",                   JSON_VARIANT_STRING,        json_dispatch_gecos,                  offsetof(UserRecord, real_name),                     0         },
+                { "emailAddress",               JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, email_address),                 JSON_SAFE },
+                { "iconName",                   JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, icon_name),                     JSON_SAFE },
+                { "location",                   JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, location),                      0         },
+                { "disposition",                JSON_VARIANT_STRING,        json_dispatch_user_disposition,       offsetof(UserRecord, disposition),                   0         },
+                { "lastChangeUSec",             JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, last_change_usec),              0         },
+                { "lastPasswordChangeUSec",     JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, last_password_change_usec),     0         },
+                { "shell",                      JSON_VARIANT_STRING,        json_dispatch_filename_or_path,       offsetof(UserRecord, shell),                         0         },
+                { "umask",                      JSON_VARIANT_UNSIGNED,      json_dispatch_umask,                  offsetof(UserRecord, umask),                         0         },
+                { "environment",                JSON_VARIANT_ARRAY,         json_dispatch_environment,            offsetof(UserRecord, environment),                   0         },
+                { "timeZone",                   JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, time_zone),                     JSON_SAFE },
+                { "preferredLanguage",          JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, preferred_language),            JSON_SAFE },
+                { "niceLevel",                  _JSON_VARIANT_TYPE_INVALID, json_dispatch_nice,                   offsetof(UserRecord, nice_level),                    0         },
+                { "resourceLimits",             _JSON_VARIANT_TYPE_INVALID, json_dispatch_rlimits,                offsetof(UserRecord, rlimits),                       0         },
+                { "locked",                     JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,               offsetof(UserRecord, locked),                        0         },
+                { "notBeforeUSec",              JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, not_before_usec),               0         },
+                { "notAfterUSec",               JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, not_after_usec),                0         },
+                { "storage",                    JSON_VARIANT_STRING,        json_dispatch_storage,                offsetof(UserRecord, storage),                       0         },
+                { "diskSize",                   JSON_VARIANT_UNSIGNED,      json_dispatch_disk_size,              offsetof(UserRecord, disk_size),                     0         },
+                { "diskSizeRelative",           JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, disk_size_relative),            0         },
+                { "skeletonDirectory",          JSON_VARIANT_STRING,        json_dispatch_path,                   offsetof(UserRecord, skeleton_directory),            0         },
+                { "accessMode",                 JSON_VARIANT_UNSIGNED,      json_dispatch_access_mode,            offsetof(UserRecord, access_mode),                   0         },
+                { "tasksMax",                   JSON_VARIANT_UNSIGNED,      json_dispatch_tasks_or_memory_max,    offsetof(UserRecord, tasks_max),                     0         },
+                { "memoryHigh",                 JSON_VARIANT_UNSIGNED,      json_dispatch_tasks_or_memory_max,    offsetof(UserRecord, memory_high),                   0         },
+                { "memoryMax",                  JSON_VARIANT_UNSIGNED,      json_dispatch_tasks_or_memory_max,    offsetof(UserRecord, memory_max),                    0         },
+                { "cpuWeight",                  JSON_VARIANT_UNSIGNED,      json_dispatch_weight,                 offsetof(UserRecord, cpu_weight),                    0         },
+                { "ioWeight",                   JSON_VARIANT_UNSIGNED,      json_dispatch_weight,                 offsetof(UserRecord, io_weight),                     0         },
+                { "mountNoDevices",             JSON_VARIANT_BOOLEAN,       json_dispatch_boolean,                offsetof(UserRecord, nodev),                         0         },
+                { "mountNoSuid",                JSON_VARIANT_BOOLEAN,       json_dispatch_boolean,                offsetof(UserRecord, nosuid),                        0         },
+                { "mountNoExecute",             JSON_VARIANT_BOOLEAN,       json_dispatch_boolean,                offsetof(UserRecord, noexec),                        0         },
+                { "cifsDomain",                 JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, cifs_domain),                   JSON_SAFE },
+                { "cifsUserName",               JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, cifs_user_name),                JSON_SAFE },
+                { "cifsService",                JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, cifs_service),                  JSON_SAFE },
+                { "imagePath",                  JSON_VARIANT_STRING,        json_dispatch_path,                   offsetof(UserRecord, image_path),                    0         },
+                { "homeDirectory",              JSON_VARIANT_STRING,        json_dispatch_home_directory,         offsetof(UserRecord, home_directory),                0         },
+                { "uid",                        JSON_VARIANT_UNSIGNED,      json_dispatch_uid_gid,                offsetof(UserRecord, uid),                           0         },
+                { "gid",                        JSON_VARIANT_UNSIGNED,      json_dispatch_uid_gid,                offsetof(UserRecord, gid),                           0         },
+                { "memberOf",                   JSON_VARIANT_ARRAY,         json_dispatch_user_group_list,        offsetof(UserRecord, member_of),                     JSON_RELAX},
+                { "fileSystemType",             JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, file_system_type),              JSON_SAFE },
+                { "partitionUuid",              JSON_VARIANT_STRING,        json_dispatch_id128,                  offsetof(UserRecord, partition_uuid),                0         },
+                { "luksUuid",                   JSON_VARIANT_STRING,        json_dispatch_id128,                  offsetof(UserRecord, luks_uuid),                     0         },
+                { "fileSystemUuid",             JSON_VARIANT_STRING,        json_dispatch_id128,                  offsetof(UserRecord, file_system_uuid),              0         },
+                { "luksDiscard",                _JSON_VARIANT_TYPE_INVALID, json_dispatch_tristate,               offsetof(UserRecord, luks_discard),                  0         },
+                { "luksOfflineDiscard",         _JSON_VARIANT_TYPE_INVALID, json_dispatch_tristate,            offsetof(UserRecord, luks_offline_discard),          0         },
+                { "luksCipher",                 JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, luks_cipher),                   JSON_SAFE },
+                { "luksCipherMode",             JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, luks_cipher_mode),              JSON_SAFE },
+                { "luksVolumeKeySize",          JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, luks_volume_key_size),          0         },
+                { "luksPbkdfHashAlgorithm",     JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, luks_pbkdf_hash_algorithm),     JSON_SAFE },
+                { "luksPbkdfType",              JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, luks_pbkdf_type),               JSON_SAFE },
+                { "luksPbkdfTimeCostUSec",      JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, luks_pbkdf_time_cost_usec),     0         },
+                { "luksPbkdfMemoryCost",        JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, luks_pbkdf_memory_cost),        0         },
+                { "luksPbkdfParallelThreads",   JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, luks_pbkdf_parallel_threads),   0         },
+                { "service",                    JSON_VARIANT_STRING,        json_dispatch_string,                 offsetof(UserRecord, service),                       JSON_SAFE },
+                { "rateLimitIntervalUSec",      JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, ratelimit_interval_usec),       0         },
+                { "rateLimitBurst",             JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, ratelimit_burst),               0         },
+                { "enforcePasswordPolicy",      JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,               offsetof(UserRecord, enforce_password_policy),       0         },
+                { "autoLogin",                  JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,               offsetof(UserRecord, auto_login),                    0         },
+                { "stopDelayUSec",              JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, stop_delay_usec),               0         },
+                { "killProcesses",              JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,               offsetof(UserRecord, kill_processes),                0         },
+                { "passwordChangeMinUSec",      JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, password_change_min_usec),      0         },
+                { "passwordChangeMaxUSec",      JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, password_change_max_usec),      0         },
+                { "passwordChangeWarnUSec",     JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, password_change_warn_usec),     0         },
+                { "passwordChangeInactiveUSec", JSON_VARIANT_UNSIGNED,      json_dispatch_uint64,                 offsetof(UserRecord, password_change_inactive_usec), 0         },
+                { "passwordChangeNow",          JSON_VARIANT_BOOLEAN,       json_dispatch_tristate,               offsetof(UserRecord, password_change_now),           0         },
+                { "pkcs11TokenUri",             JSON_VARIANT_ARRAY,         dispatch_pkcs11_uri_array,            offsetof(UserRecord, pkcs11_token_uri),              0         },
+                { "fido2HmacCredential",        JSON_VARIANT_ARRAY,         dispatch_fido2_hmac_credential_array, 0,                                                   0         },
+
+                { "secret",                     JSON_VARIANT_OBJECT,        dispatch_secret,                      0,                                                   0         },
+                { "privileged",                 JSON_VARIANT_OBJECT,        dispatch_privileged,                  0,                                                   0         },
 
                 /* Ignore the perMachine, binding, status stuff here, and process it later, so that it overrides whatever is set above */
-                { "perMachine",                 JSON_VARIANT_ARRAY,         NULL,                              0,                                                   0         },
-                { "binding",                    JSON_VARIANT_OBJECT,        NULL,                              0,                                                   0         },
-                { "status",                     JSON_VARIANT_OBJECT,        NULL,                              0,                                                   0         },
+                { "perMachine",                 JSON_VARIANT_ARRAY,         NULL,                                 0,                                                   0         },
+                { "binding",                    JSON_VARIANT_OBJECT,        NULL,                                 0,                                                   0         },
+                { "status",                     JSON_VARIANT_OBJECT,        NULL,                                 0,                                                   0         },
 
                 /* Ignore 'signature', we check it with explicit accessors instead */
-                { "signature",                  JSON_VARIANT_ARRAY,         NULL,                              0,                                                   0         },
+                { "signature",                  JSON_VARIANT_ARRAY,         NULL,                                 0,                                                   0         },
                 {},
         };
 
@@ -1500,6 +1671,27 @@ bool user_record_luks_discard(UserRecord *h) {
         return path_startswith(ip, "/dev/");
 }
 
+bool user_record_luks_offline_discard(UserRecord *h) {
+        const char *ip;
+
+        assert(h);
+
+        if (h->luks_offline_discard >= 0)
+                return h->luks_offline_discard;
+
+        /* Discard while we are logged out should generally be a good idea, except when operating directly on
+         * physical media, where we should just bind it to the online discard mode. */
+
+        ip = user_record_image_path(h);
+        if (!ip)
+                return false;
+
+        if (path_startswith(ip, "/dev/"))
+                return user_record_luks_discard(h);
+
+        return true;
+}
+
 const char *user_record_luks_cipher(UserRecord *h) {
         assert(h);
 
@@ -1646,6 +1838,9 @@ bool user_record_can_authenticate(UserRecord *h) {
         if (h->n_pkcs11_encrypted_key > 0)
                 return true;
 
+        if (h->n_fido2_hmac_salt > 0)
+                return true;
+
         return !strv_isempty(h->hashed_password);
 }
 
@@ -1802,7 +1997,7 @@ int user_record_test_password_change_required(UserRecord *h) {
                        0: No password change required, but permitted
          */
 
-        /* If a pasword change request has been set explicitly, it overrides everything */
+        /* If a password change request has been set explicitly, it overrides everything */
         if (h->password_change_now > 0)
                 return -EKEYREVOKED;
 
index 5bac30476710373a39a183e92f42886964f9501d..1bfd095d27e18c1aed317428bebc78d7818de481 100644 (file)
@@ -189,6 +189,23 @@ typedef struct Pkcs11EncryptedKey {
         char *hashed_password;
 } Pkcs11EncryptedKey;
 
+typedef struct Fido2HmacCredential {
+        void *id;
+        size_t size;
+} Fido2HmacCredential;
+
+typedef struct Fido2HmacSalt {
+        /* The FIDO2 Cridential ID to use */
+        Fido2HmacCredential credential;
+
+        /* The FIDO2 salt value */
+        void *salt;
+        size_t salt_size;
+
+        /* What to test the hashed salt value against, usually UNIX password hash here. */
+        char *hashed_password;
+} Fido2HmacSalt;
+
 typedef struct UserRecord {
         /* The following three fields are not part of the JSON record */
         unsigned n_ref;
@@ -239,7 +256,7 @@ typedef struct UserRecord {
         char **hashed_password;
         char **ssh_authorized_keys;
         char **password;
-        char **pkcs11_pin;
+        char **token_pin;
 
         char *cifs_domain;
         char *cifs_user_name;
@@ -261,6 +278,7 @@ typedef struct UserRecord {
         sd_id128_t file_system_uuid;
 
         int luks_discard;
+        int luks_offline_discard;
         char *luks_cipher;
         char *luks_cipher_mode;
         uint64_t luks_volume_key_size;
@@ -308,6 +326,12 @@ typedef struct UserRecord {
         size_t n_pkcs11_encrypted_key;
         int pkcs11_protected_authentication_path_permitted;
 
+        Fido2HmacCredential *fido2_hmac_credential;
+        size_t n_fido2_hmac_credential;
+        Fido2HmacSalt *fido2_hmac_salt;
+        size_t n_fido2_hmac_salt;
+        int fido2_user_presence_permitted;
+
         JsonVariant *json;
 } UserRecord;
 
@@ -332,6 +356,7 @@ const char *user_record_cifs_user_name(UserRecord *h);
 const char *user_record_shell(UserRecord *h);
 const char *user_record_real_name(UserRecord *h);
 bool user_record_luks_discard(UserRecord *h);
+bool user_record_luks_offline_discard(UserRecord *h);
 const char *user_record_luks_cipher(UserRecord *h);
 const char *user_record_luks_cipher_mode(UserRecord *h);
 uint64_t user_record_luks_volume_key_size(UserRecord *h);
@@ -347,6 +372,8 @@ usec_t user_record_ratelimit_interval_usec(UserRecord *h);
 uint64_t user_record_ratelimit_burst(UserRecord *h);
 bool user_record_can_authenticate(UserRecord *h);
 
+int user_record_build_image_path(UserStorage storage, const char *user_name_and_realm, char **ret);
+
 bool user_record_equal(UserRecord *a, UserRecord *b);
 bool user_record_compatible(UserRecord *a, UserRecord *b);
 int user_record_compare_last_change(UserRecord *a, UserRecord *b);
index 0769a792c228b914981c16e32708b3a87e3eaf6a..94120862dfb3002760cee0b176570a31e36018b0 100644 (file)
@@ -3,6 +3,7 @@
 #include <sys/auxv.h>
 
 #include "dirent-util.h"
+#include "dlfcn-util.h"
 #include "errno-util.h"
 #include "fd-util.h"
 #include "group-record-nss.h"
@@ -32,8 +33,8 @@ struct UserDBIterator {
         bool nss_iterating:1;
         bool synthesize_root:1;
         bool synthesize_nobody:1;
+        bool nss_systemd_blocked:1;
         int error;
-        int nss_lock;
         unsigned n_found;
         sd_event *event;
         UserRecord *found_user;                   /* when .what == LOOKUP_USER */
@@ -85,7 +86,9 @@ UserDBIterator* userdb_iterator_free(UserDBIterator *iterator) {
         }
 
         sd_event_unref(iterator->event);
-        safe_close(iterator->nss_lock);
+
+        if (iterator->nss_systemd_blocked)
+                assert_se(userdb_block_nss_systemd(false) >= 0);
 
         return mfree(iterator);
 }
@@ -102,12 +105,27 @@ static UserDBIterator* userdb_iterator_new(LookupWhat what) {
 
         *i = (UserDBIterator) {
                 .what = what,
-                .nss_lock = -1,
         };
 
         return i;
 }
 
+static int userdb_iterator_block_nss_systemd(UserDBIterator *iterator) {
+        int r;
+
+        assert(iterator);
+
+        if (iterator->nss_systemd_blocked)
+                return 0;
+
+        r = userdb_block_nss_systemd(true);
+        if (r < 0)
+                return r;
+
+        iterator->nss_systemd_blocked = true;
+        return 1;
+}
+
 struct user_group_data {
         JsonVariant *record;
         bool incomplete;
@@ -138,6 +156,8 @@ static int userdb_on_query_reply(
                         r = -ESRCH;
                 else if (streq(error_id, "io.systemd.UserDatabase.ServiceNotAvailable"))
                         r = -EHOSTDOWN;
+                else if (streq(error_id, "io.systemd.UserDatabase.EnumerationNotSupported"))
+                        r = -EOPNOTSUPP;
                 else if (streq(error_id, VARLINK_ERROR_TIMEOUT))
                         r = -ETIMEDOUT;
                 else
@@ -359,15 +379,9 @@ static int userdb_connect(
         if (r < 0)
                 return log_debug_errno(r, "Failed to invoke varlink method: %m");
 
-        r = set_ensure_allocated(&iterator->links, &link_hash_ops);
-        if (r < 0)
-                return log_debug_errno(r, "Failed to allocate set: %m");
-
-        r = set_put(iterator->links, vl);
+        r = set_ensure_consume(&iterator->links, &link_hash_ops, TAKE_PTR(vl));
         if (r < 0)
                 return log_debug_errno(r, "Failed to add varlink connection to set: %m");
-
-        TAKE_PTR(vl);
         return r;
 }
 
@@ -606,15 +620,13 @@ int userdb_by_name(const char *name, UserDBFlags flags, UserRecord **ret) {
                         return r;
         }
 
-        if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && !(iterator && iterator->nss_covered)) {
-                /* Make sure the NSS lookup doesn't recurse back to us. (EBUSY is fine here, it just means we
-                 * already took the lock from our thread, which is totally OK.) */
-                r = userdb_nss_compat_disable();
-                if (r >= 0 || r == -EBUSY) {
-                        iterator->nss_lock = r;
+        if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && !iterator->nss_covered) {
+                /* Make sure the NSS lookup doesn't recurse back to us. */
 
+                r = userdb_iterator_block_nss_systemd(iterator);
+                if (r >= 0) {
                         /* Client-side NSS fallback */
-                        r = nss_user_record_by_name(name, ret);
+                        r = nss_user_record_by_name(name, !FLAGS_SET(flags, USERDB_AVOID_SHADOW), ret);
                         if (r >= 0)
                                 return r;
                 }
@@ -655,13 +667,11 @@ int userdb_by_uid(uid_t uid, UserDBFlags flags, UserRecord **ret) {
                         return r;
         }
 
-        if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && !(iterator && iterator->nss_covered)) {
-                r = userdb_nss_compat_disable();
-                if (r >= 0 || r == -EBUSY) {
-                        iterator->nss_lock = r;
-
+        if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && !iterator->nss_covered) {
+                r = userdb_iterator_block_nss_systemd(iterator);
+                if (r >= 0) {
                         /* Client-side NSS fallback */
-                        r = nss_user_record_by_uid(uid, ret);
+                        r = nss_user_record_by_uid(uid, !FLAGS_SET(flags, USERDB_AVOID_SHADOW), ret);
                         if (r >= 0)
                                 return r;
                 }
@@ -693,9 +703,9 @@ int userdb_all(UserDBFlags flags, UserDBIterator **ret) {
         r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetUserRecord", true, NULL, flags);
 
         if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && (r < 0 || !iterator->nss_covered)) {
-                iterator->nss_lock = userdb_nss_compat_disable();
-                if (iterator->nss_lock < 0 && iterator->nss_lock != -EBUSY)
-                        return iterator->nss_lock;
+                r = userdb_iterator_block_nss_systemd(iterator);
+                if (r < 0)
+                        return r;
 
                 setpwent();
                 iterator->nss_iterating = true;
@@ -815,11 +825,9 @@ int groupdb_by_name(const char *name, UserDBFlags flags, GroupRecord **ret) {
         }
 
         if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && !(iterator && iterator->nss_covered)) {
-                r = userdb_nss_compat_disable();
-                if (r >= 0 || r == -EBUSY) {
-                        iterator->nss_lock = r;
-
-                        r = nss_group_record_by_name(name, ret);
+                r = userdb_iterator_block_nss_systemd(iterator);
+                if (r >= 0) {
+                        r = nss_group_record_by_name(name, !FLAGS_SET(flags, USERDB_AVOID_SHADOW), ret);
                         if (r >= 0)
                                 return r;
                 }
@@ -861,11 +869,9 @@ int groupdb_by_gid(gid_t gid, UserDBFlags flags, GroupRecord **ret) {
         }
 
         if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && !(iterator && iterator->nss_covered)) {
-                r = userdb_nss_compat_disable();
-                if (r >= 0 || r == -EBUSY) {
-                        iterator->nss_lock = r;
-
-                        r = nss_group_record_by_gid(gid, ret);
+                r = userdb_iterator_block_nss_systemd(iterator);
+                if (r >= 0) {
+                        r = nss_group_record_by_gid(gid, !FLAGS_SET(flags, USERDB_AVOID_SHADOW), ret);
                         if (r >= 0)
                                 return r;
                 }
@@ -897,9 +903,9 @@ int groupdb_all(UserDBFlags flags, UserDBIterator **ret) {
         r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetGroupRecord", true, NULL, flags);
 
         if (!FLAGS_SET(flags, USERDB_AVOID_NSS) && (r < 0 || !iterator->nss_covered)) {
-                iterator->nss_lock = userdb_nss_compat_disable();
-                if (iterator->nss_lock < 0 && iterator->nss_lock != -EBUSY)
-                        return iterator->nss_lock;
+                r = userdb_iterator_block_nss_systemd(iterator);
+                if (r < 0)
+                        return r;
 
                 setgrent();
                 iterator->nss_iterating = true;
@@ -998,9 +1004,9 @@ int membershipdb_by_user(const char *name, UserDBFlags flags, UserDBIterator **r
         if ((r >= 0 && iterator->nss_covered) || FLAGS_SET(flags, USERDB_AVOID_NSS))
                 goto finish;
 
-        iterator->nss_lock = userdb_nss_compat_disable();
-        if (iterator->nss_lock < 0 && iterator->nss_lock != -EBUSY)
-                return iterator->nss_lock;
+        r = userdb_iterator_block_nss_systemd(iterator);
+        if (r < 0)
+                return r;
 
         iterator->filter_user_name = strdup(name);
         if (!iterator->filter_user_name)
@@ -1041,12 +1047,12 @@ int membershipdb_by_group(const char *name, UserDBFlags flags, UserDBIterator **
         if ((r >= 0 && iterator->nss_covered) || FLAGS_SET(flags, USERDB_AVOID_NSS))
                 goto finish;
 
-        iterator->nss_lock = userdb_nss_compat_disable();
-        if (iterator->nss_lock < 0 && iterator->nss_lock != -EBUSY)
-                return iterator->nss_lock;
+        r = userdb_iterator_block_nss_systemd(iterator);
+        if (r < 0)
+                return r;
 
         /* We ignore all errors here, since the group might be defined by a userdb native service, and we queried them already above. */
-        (void) nss_group_record_by_name(name, &gr);
+        (void) nss_group_record_by_name(name, false, &gr);
         if (gr) {
                 iterator->members_of_group = strv_copy(gr->members);
                 if (!iterator->members_of_group)
@@ -1082,9 +1088,9 @@ int membershipdb_all(UserDBFlags flags, UserDBIterator **ret) {
         if ((r >= 0 && iterator->nss_covered) || FLAGS_SET(flags, USERDB_AVOID_NSS))
                 goto finish;
 
-        iterator->nss_lock = userdb_nss_compat_disable();
-        if (iterator->nss_lock < 0 && iterator->nss_lock != -EBUSY)
-                return iterator->nss_lock;
+        r = userdb_iterator_block_nss_systemd(iterator);
+        if (r < 0)
+                return r;
 
         setgrent();
         iterator->nss_iterating = true;
@@ -1221,115 +1227,24 @@ int membershipdb_by_group_strv(const char *name, UserDBFlags flags, char ***ret)
         return 0;
 }
 
-static int userdb_thread_sockaddr(struct sockaddr_un *ret_sa, socklen_t *ret_salen) {
-        static const uint8_t
-                k1[16] = { 0x35, 0xc1, 0x1f, 0x41, 0x59, 0xc6, 0xa0, 0xf9, 0x33, 0x4b, 0x17, 0x3d, 0xb9, 0xf6, 0x14, 0xd9 },
-                k2[16] = { 0x6a, 0x11, 0x4c, 0x37, 0xe5, 0xa3, 0x8c, 0xa6, 0x93, 0x55, 0x64, 0x8c, 0x93, 0xee, 0xa1, 0x7b };
-
-        struct siphash sh;
-        uint64_t x, y;
-        pid_t tid;
-        void *p;
-
-        assert(ret_sa);
-        assert(ret_salen);
-
-        /* This calculates an AF_UNIX socket address in the abstract namespace whose existence works as an
-         * indicator whether to emulate NSS records for complex user records that are also available via the
-         * varlink protocol. The name of the socket is picked in a way so that:
-         *
-         *     → it is per-thread (by hashing from the TID)
-         *
-         *     → is not guessable for foreign processes (by hashing from the — hopefully secret — AT_RANDOM
-         *       value every process gets passed from the kernel
-         *
-         * By using a socket the NSS emulation can be nicely turned off for limited amounts of time only,
-         * simply controlled by the lifetime of the fd itself. By using an AF_UNIX socket in the abstract
-         * namespace the lock is automatically cleaned up when the process dies abnormally.
-         *
-         */
-
-        p = ULONG_TO_PTR(getauxval(AT_RANDOM));
-        if (!p)
-                return -EIO;
-
-        tid = gettid();
-
-        siphash24_init(&sh, k1);
-        siphash24_compress(p, 16, &sh);
-        siphash24_compress(&tid, sizeof(tid), &sh);
-        x = siphash24_finalize(&sh);
-
-        siphash24_init(&sh, k2);
-        siphash24_compress(p, 16, &sh);
-        siphash24_compress(&tid, sizeof(tid), &sh);
-        y = siphash24_finalize(&sh);
-
-        *ret_sa = (struct sockaddr_un) {
-                .sun_family = AF_UNIX,
-        };
-
-        sprintf(ret_sa->sun_path + 1, "userdb-%016" PRIx64 "%016" PRIx64, x, y);
-        *ret_salen = offsetof(struct sockaddr_un, sun_path) + 1 + 7 + 32;
-
-        return 0;
-}
-
-int userdb_nss_compat_is_enabled(void) {
-        _cleanup_close_ int fd = -1;
-        union sockaddr_union sa;
-        socklen_t salen;
-        int r;
-
-        /* Tests whether the NSS compatibility logic is currently turned on for the invoking thread. Returns
-         * true if NSS compatibility is turned on, i.e. whether NSS records shall be synthesized from complex
-         * user records. */
-
-        r = userdb_thread_sockaddr(&sa.un, &salen);
-        if (r < 0)
-                return r;
-
-        fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
-        if (fd < 0)
-                return -errno;
+int userdb_block_nss_systemd(int b) {
+        _cleanup_(dlclosep) void *dl = NULL;
+        int (*call)(bool b);
 
-        /* Try to connect(). This doesn't do anything really, except that it checks whether the socket
-         * address is bound at all. */
-        if (connect(fd, &sa.sa, salen) < 0) {
-                if (errno == ECONNREFUSED) /* the socket is not bound, hence NSS emulation shall be done */
-                        return true;
+        /* Note that we might be called from libnss_systemd.so.2 itself, but that should be fine, really. */
 
-                return -errno;
+        dl = dlopen(ROOTLIBDIR "/libnss_systemd.so.2", RTLD_LAZY|RTLD_NODELETE);
+        if (!dl) {
+                /* If the file isn't installed, don't complain loudly */
+                log_debug("Failed to dlopen(libnss_systemd.so.2), ignoring: %s", dlerror());
+                return 0;
         }
 
-        return false;
-}
-
-int userdb_nss_compat_disable(void) {
-        _cleanup_close_ int fd = -1;
-        union sockaddr_union sa;
-        socklen_t salen;
-        int r;
-
-        /* Turn off the NSS compatibility logic for the invoking thread. By default NSS records are
-         * synthesized for all complex user records looked up via NSS. If this call is invoked this is
-         * disabled for the invoking thread, but only for it. A caller that natively supports the varlink
-         * user record protocol may use that to turn off the compatibility for NSS lookups. */
-
-        r = userdb_thread_sockaddr(&sa.un, &salen);
-        if (r < 0)
-                return r;
-
-        fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
-        if (fd < 0)
-                return -errno;
-
-        if (bind(fd, &sa.sa, salen) < 0) {
-                if (errno == EADDRINUSE) /* lock already taken, convert this into a recognizable error */
-                        return -EBUSY;
-
-                return -errno;
-        }
+        call = (int (*)(bool b)) dlsym(dl, "_nss_systemd_block");
+        if (!call)
+                /* If the file is is installed but lacks the symbol we expect, things are weird, let's complain */
+                return log_debug_errno(SYNTHETIC_ERRNO(ELIBBAD),
+                                       "Unable to find symbol _nss_systemd_block in libnss_systemd.so.2: %s", dlerror());
 
-        return TAKE_FD(fd);
+        return call(b);
 }
index 4288b0ff95dfc85634039ee198af9d0544cdb856..2464f54c3e22e1dd2d79639d9d28c58e35d89293 100644 (file)
@@ -16,9 +16,10 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(UserDBIterator*, userdb_iterator_free);
 
 typedef enum UserDBFlags {
         USERDB_AVOID_NSS          = 1 << 0,  /* don't do client-side nor server-side NSS */
-        USERDB_AVOID_DYNAMIC_USER = 1 << 1,  /* exclude looking up in io.systemd.DynamicUser */
-        USERDB_AVOID_MULTIPLEXER  = 1 << 2,  /* exclude looking up via io.systemd.Multiplexer */
-        USERDB_DONT_SYNTHESIZE    = 1 << 3,  /* don't synthesize root/nobody */
+        USERDB_AVOID_SHADOW       = 1 << 1,  /* don't do client-side shadow calls (server side might happen though) */
+        USERDB_AVOID_DYNAMIC_USER = 1 << 2,  /* exclude looking up in io.systemd.DynamicUser */
+        USERDB_AVOID_MULTIPLEXER  = 1 << 3,  /* exclude looking up via io.systemd.Multiplexer */
+        USERDB_DONT_SYNTHESIZE    = 1 << 4,  /* don't synthesize root/nobody */
 } UserDBFlags;
 
 int userdb_by_name(const char *name, UserDBFlags flags, UserRecord **ret);
@@ -37,5 +38,4 @@ int membershipdb_all(UserDBFlags flags, UserDBIterator **ret);
 int membershipdb_iterator_get(UserDBIterator *iterator, char **user, char **group);
 int membershipdb_by_group_strv(const char *name, UserDBFlags flags, char ***ret);
 
-int userdb_nss_compat_is_enabled(void);
-int userdb_nss_compat_disable(void);
+int userdb_block_nss_systemd(int b);
index fa4f32f3538b29c40a16254fbf6310519fb2e7ae..9edcd8302ac79d204693a7600130d16af7491760 100644 (file)
@@ -14,6 +14,7 @@
 #include "alloc-util.h"
 #include "fd-util.h"
 #include "hostname-util.h"
+#include "io-util.h"
 #include "macro.h"
 #include "memory-util.h"
 #include "path-util.h"
@@ -24,8 +25,8 @@
 #include "utmp-wtmp.h"
 
 int utmp_get_runlevel(int *runlevel, int *previous) {
+        _cleanup_(utxent_cleanup) bool utmpx = false;
         struct utmpx *found, lookup = { .ut_type = RUN_LVL };
-        int r;
         const char *e;
 
         assert(runlevel);
@@ -34,8 +35,7 @@ int utmp_get_runlevel(int *runlevel, int *previous) {
          * precedence. Presumably, sysvinit does this to work around a
          * race condition that would otherwise exist where we'd always
          * go to disk and hence might read runlevel data that might be
-         * very new and does not apply to the current script being
-         * executed. */
+         * very new and not apply to the current script being executed. */
 
         e = getenv("RUNLEVEL");
         if (e && e[0] > 0) {
@@ -57,27 +57,17 @@ int utmp_get_runlevel(int *runlevel, int *previous) {
         if (utmpxname(_PATH_UTMPX) < 0)
                 return -errno;
 
-        setutxent();
+        utmpx = utxent_start();
 
         found = getutxid(&lookup);
         if (!found)
-                r = -errno;
-        else {
-                int a, b;
-
-                a = found->ut_pid & 0xFF;
-                b = (found->ut_pid >> 8) & 0xFF;
-
-                *runlevel = a;
-                if (previous)
-                        *previous = b;
-
-                r = 0;
-        }
+                return -errno;
 
-        endutxent();
+        *runlevel = found->ut_pid & 0xFF;
+        if (previous)
+                *previous = (found->ut_pid >> 8) & 0xFF;
 
-        return r;
+        return 0;
 }
 
 static void init_timestamp(struct utmpx *store, usec_t t) {
@@ -105,7 +95,7 @@ static void init_entry(struct utmpx *store, usec_t t) {
 }
 
 static int write_entry_utmp(const struct utmpx *store) {
-        int r;
+        _cleanup_(utxent_cleanup) bool utmpx = false;
 
         assert(store);
 
@@ -116,26 +106,35 @@ static int write_entry_utmp(const struct utmpx *store) {
         if (utmpxname(_PATH_UTMPX) < 0)
                 return -errno;
 
-        setutxent();
+        utmpx = utxent_start();
 
-        if (!pututxline(store))
-                r = -errno;
-        else
-                r = 0;
-
-        endutxent();
-
-        return r;
+        if (pututxline(store))
+                return 0;
+        if (errno == ENOENT) {
+                /* If utmp/wtmp have been disabled, that's a good thing, hence ignore the error. */
+                log_debug_errno(errno, "Not writing utmp: %m");
+                return 0;
+        }
+        return -errno;
 }
 
 static int write_entry_wtmp(const struct utmpx *store) {
         assert(store);
 
         /* wtmp is a simple append-only file where each entry is
-        simply appended to the end; i.e. basically a log. */
+         * simply appended to the end; i.e. basically a log. */
 
         errno = 0;
         updwtmpx(_PATH_WTMPX, store);
+        if (errno == ENOENT) {
+                /* If utmp/wtmp have been disabled, that's a good thing, hence ignore the error. */
+                log_debug_errno(errno, "Not writing wtmp: %m");
+                return 0;
+        }
+        if (errno == EROFS) {
+                log_warning_errno(errno, "Failed to write wtmp record, ignoring: %m");
+                return 0;
+        }
         return -errno;
 }
 
@@ -144,16 +143,7 @@ static int write_utmp_wtmp(const struct utmpx *store_utmp, const struct utmpx *s
 
         r = write_entry_utmp(store_utmp);
         s = write_entry_wtmp(store_wtmp);
-
-        if (r >= 0)
-                r = s;
-
-        /* If utmp/wtmp have been disabled, that's a good thing, hence
-         * ignore the errors */
-        if (r == -ENOENT)
-                r = 0;
-
-        return r;
+        return r < 0 ? r : s;
 }
 
 static int write_entry_both(const struct utmpx *store) {
@@ -297,7 +287,7 @@ int utmp_put_runlevel(int runlevel, int previous) {
         return write_entry_both(&store);
 }
 
-#define TIMEOUT_MSEC 50
+#define TIMEOUT_USEC (50 * USEC_PER_MSEC)
 
 static int write_to_terminal(const char *tty, const char *message) {
         _cleanup_close_ int fd = -1;
@@ -315,14 +305,10 @@ static int write_to_terminal(const char *tty, const char *message) {
         p = message;
         left = strlen(message);
 
-        end = now(CLOCK_MONOTONIC) + TIMEOUT_MSEC*USEC_PER_MSEC;
+        end = now(CLOCK_MONOTONIC) + TIMEOUT_USEC;
 
         while (left > 0) {
                 ssize_t n;
-                struct pollfd pollfd = {
-                        .fd = fd,
-                        .events = POLLOUT,
-                };
                 usec_t t;
                 int k;
 
@@ -331,10 +317,9 @@ static int write_to_terminal(const char *tty, const char *message) {
                 if (t >= end)
                         return -ETIME;
 
-                k = poll(&pollfd, 1, (end - t) / USEC_PER_MSEC);
+                k = fd_wait_for_event(fd, POLLOUT, end - t);
                 if (k < 0)
-                        return -errno;
-
+                        return k;
                 if (k == 0)
                         return -ETIME;
 
index 9e433cf73ec5a968ad5149b3768b3b1369c1f4c4..fe55bad12d632bd85b11938eca87a0fba780bbf2 100644 (file)
@@ -8,6 +8,8 @@
 #include "util.h"
 
 #if ENABLE_UTMP
+#include <utmpx.h>
+
 int utmp_get_runlevel(int *runlevel, int *previous);
 
 int utmp_put_shutdown(void);
@@ -24,6 +26,15 @@ int utmp_wall(
         bool (*match_tty)(const char *tty, void *userdata),
         void *userdata);
 
+static inline bool utxent_start(void) {
+        setutxent();
+        return true;
+}
+static inline void utxent_cleanup(bool *initialized) {
+        if (initialized)
+                endutxent();
+}
+
 #else /* ENABLE_UTMP */
 
 static inline int utmp_get_runlevel(int *runlevel, int *previous) {
index dff7d32535d5bc0dfba9ddc25ab4818c43e94610..be3559dc1037efec091ba4a1843daad3e0f0f919 100644 (file)
@@ -6,6 +6,7 @@
 #include "errno-util.h"
 #include "fd-util.h"
 #include "hashmap.h"
+#include "io-util.h"
 #include "list.h"
 #include "process-util.h"
 #include "set.h"
@@ -386,7 +387,7 @@ DEFINE_TRIVIAL_REF_UNREF_FUNC(Varlink, varlink, varlink_destroy);
 static int varlink_test_disconnect(Varlink *v) {
         assert(v);
 
-        /* Tests whether we the the connection has been terminated. We are careful to not stop processing it
+        /* Tests whether we the connection has been terminated. We are careful to not stop processing it
          * prematurely, since we want to handle half-open connections as well as possible and want to flush
          * out and read data before we close down if we can. */
 
@@ -1003,8 +1004,6 @@ static void handle_revents(Varlink *v, int revents) {
 }
 
 int varlink_wait(Varlink *v, usec_t timeout) {
-        struct timespec ts;
-        struct pollfd pfd;
         int r, fd, events;
         usec_t t;
 
@@ -1038,20 +1037,14 @@ int varlink_wait(Varlink *v, usec_t timeout) {
         if (events < 0)
                 return events;
 
-        pfd = (struct pollfd) {
-                .fd = fd,
-                .events = events,
-        };
-
-        r = ppoll(&pfd, 1,
-                  t == USEC_INFINITY ? NULL : timespec_store(&ts, t),
-                  NULL);
+        r = fd_wait_for_event(fd, events, t);
         if (r < 0)
-                return -errno;
-
-        handle_revents(v, pfd.revents);
+                return r;
+        if (r == 0)
+                return 0;
 
-        return r > 0 ? 1 : 0;
+        handle_revents(v, r);
+        return 1;
 }
 
 int varlink_get_fd(Varlink *v) {
@@ -1119,8 +1112,6 @@ int varlink_flush(Varlink *v) {
                 return -ENOTCONN;
 
         for (;;) {
-                struct pollfd pfd;
-
                 if (v->output_buffer_size == 0)
                         break;
                 if (v->write_disconnected)
@@ -1134,15 +1125,13 @@ int varlink_flush(Varlink *v) {
                         continue;
                 }
 
-                pfd = (struct pollfd) {
-                        .fd = v->fd,
-                        .events = POLLOUT,
-                };
+                r = fd_wait_for_event(v->fd, POLLOUT, USEC_INFINITY);
+                if (r < 0)
+                        return r;
 
-                if (poll(&pfd, 1, -1) < 0)
-                        return -errno;
+                assert(r != 0);
 
-                handle_revents(v, pfd.revents);
+                handle_revents(v, r);
         }
 
         return ret;
@@ -1334,7 +1323,7 @@ int varlink_invoke(Varlink *v, const char *method, JsonVariant *parameters) {
         if (v->state == VARLINK_DISCONNECTED)
                 return -ENOTCONN;
 
-        /* We allow enqueing multiple method calls at once! */
+        /* We allow enqueuing multiple method calls at once! */
         if (!IN_SET(v->state, VARLINK_IDLE_CLIENT, VARLINK_AWAITING_REPLY))
                 return -EBUSY;
 
index 06d89b4736375e45462144ee58db7f707e69c78a..d2744b6918f4c2ba86b5fe4f4419ff6532635256 100644 (file)
@@ -45,10 +45,19 @@ bool running_in_chroot_or_offline(void) {
         return r > 0;
 }
 
+const Verb* verbs_find_verb(const char *name, const Verb verbs[]) {
+        for (size_t i = 0; verbs[i].dispatch; i++)
+                if (streq_ptr(name, verbs[i].verb) ||
+                    (!name && FLAGS_SET(verbs[i].flags, VERB_DEFAULT)))
+                        return &verbs[i];
+
+        /* At the end of the list? */
+        return NULL;
+}
+
 int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) {
         const Verb *verb;
         const char *name;
-        unsigned i;
         int left;
 
         assert(verbs);
@@ -62,31 +71,16 @@ int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) {
         optind = 0;
         name = argv[0];
 
-        for (i = 0;; i++) {
-                bool found;
-
-                /* At the end of the list? */
-                if (!verbs[i].dispatch) {
-                        if (name)
-                                log_error("Unknown command verb %s.", name);
-                        else
-                                log_error("Command verb required.");
-                        return -EINVAL;
-                }
-
+        verb = verbs_find_verb(name, verbs);
+        if (!verb) {
                 if (name)
-                        found = streq(name, verbs[i].verb);
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                               "Unknown command verb %s.", name);
                 else
-                        found = verbs[i].flags & VERB_DEFAULT;
-
-                if (found) {
-                        verb = &verbs[i];
-                        break;
-                }
+                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                               "Command verb required.");
         }
 
-        assert(verb);
-
         if (!name)
                 left = 1;
 
@@ -101,10 +95,7 @@ int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) {
                                        "Too many arguments.");
 
         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.");
+                log_info("Running in chroot, ignoring command '%s'", name ?: verb->verb);
                 return 0;
         }
 
index c5fe6cc7c58b75c5065d96734b139ae2c76a4b2c..b6a1afcdee630de970a1acf205633a629e8427f4 100644 (file)
@@ -6,8 +6,8 @@
 #define VERB_ANY ((unsigned) -1)
 
 typedef enum VerbFlags {
-        VERB_DEFAULT      = 1 << 0,
-        VERB_ONLINE_ONLY  = 1 << 1,
+        VERB_DEFAULT      = 1 << 0,  /* The verb to run if no verb is specified */
+        VERB_ONLINE_ONLY  = 1 << 1,  /* Just do nothing when running in chroot or offline */
 } VerbFlags;
 
 typedef struct {
@@ -19,4 +19,5 @@ typedef struct {
 
 bool running_in_chroot_or_offline(void);
 
+const Verb* verbs_find_verb(const char *name, const Verb verbs[]);
 int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata);
index 98fefb39569b51fc2e750bb62d128a1fba0a43c8..4d3d8828f57d98f1bc0d9a573c96191ab00593aa 100644 (file)
@@ -16,6 +16,7 @@
 static int watchdog_fd = -1;
 static char *watchdog_device = NULL;
 static usec_t watchdog_timeout = USEC_INFINITY;
+static usec_t watchdog_last_ping = USEC_INFINITY;
 
 static int update_timeout(void) {
         int r;
@@ -57,6 +58,8 @@ static int update_timeout(void) {
                 r = ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0);
                 if (r < 0)
                         return log_warning_errno(errno, "Failed to ping hardware watchdog: %m");
+
+                watchdog_last_ping = now(clock_boottime_or_monotonic());
         }
 
         return 0;
@@ -114,9 +117,38 @@ int watchdog_set_timeout(usec_t *usec) {
         return r;
 }
 
+usec_t watchdog_runtime_wait(void) {
+        usec_t rtwait;
+        usec_t ntime;
+
+        if (!timestamp_is_set(watchdog_timeout))
+                return USEC_INFINITY;
+
+        /* Sleep half the watchdog timeout since the last successful ping at most */
+        if (timestamp_is_set(watchdog_last_ping)) {
+                ntime = now(clock_boottime_or_monotonic());
+                assert(ntime >= watchdog_last_ping);
+                rtwait = usec_sub_unsigned(watchdog_last_ping + (watchdog_timeout / 2), ntime);
+        } else
+                rtwait = watchdog_timeout / 2;
+
+        return rtwait;
+}
+
 int watchdog_ping(void) {
+        usec_t ntime;
         int r;
 
+        ntime = now(clock_boottime_or_monotonic());
+
+        /* Never ping earlier than watchdog_timeout/4 and try to ping
+         * by watchdog_timeout/2 plus scheduling latencies the latest */
+        if (timestamp_is_set(watchdog_last_ping)) {
+                assert(ntime >= watchdog_last_ping);
+                if ((ntime - watchdog_last_ping) < (watchdog_timeout / 4))
+                        return 0;
+        }
+
         if (watchdog_fd < 0) {
                 r = open_watchdog();
                 if (r < 0)
@@ -127,6 +159,8 @@ int watchdog_ping(void) {
         if (r < 0)
                 return log_warning_errno(errno, "Failed to ping hardware watchdog: %m");
 
+        watchdog_last_ping = ntime;
+
         return 0;
 }
 
index a345e4ba7d9d0df9019d1c87bbe21e8e2371f101..ce739fd8a38e4cccd5824dbd71b4d5875da93afd 100644 (file)
@@ -10,6 +10,7 @@ int watchdog_set_device(char *path);
 int watchdog_set_timeout(usec_t *usec);
 int watchdog_ping(void);
 void watchdog_close(bool disarm);
+usec_t watchdog_runtime_wait(void);
 
 static inline void watchdog_free_device(void) {
         (void) watchdog_set_device(NULL);
index 2717db6507584d4d88d573c3723602dab1cbb2a6..22bb3041fbbc1fa8ba52aeb86c3fa714bfd4466a 100644 (file)
@@ -14,6 +14,9 @@ int wifi_get_interface(sd_netlink *genl, int ifindex, enum nl80211_iftype *iftyp
         sd_genl_family family;
         int r;
 
+        assert(genl);
+        assert(ifindex > 0);
+
         r = sd_genl_message_new(genl, SD_GENL_NL80211, NL80211_CMD_GET_INTERFACE, &m);
         if (r < 0)
                 return log_debug_errno(r, "Failed to create generic netlink message: %m");
@@ -23,10 +26,17 @@ int wifi_get_interface(sd_netlink *genl, int ifindex, enum nl80211_iftype *iftyp
                 return log_debug_errno(r, "Could not append NL80211_ATTR_IFINDEX attribute: %m");
 
         r = sd_netlink_call(genl, m, 0, &reply);
+        if (r == -ENODEV) {
+                /* For obsolete WEXT driver. */
+                log_debug_errno(r, "Failed to request information about wifi interface %d. "
+                                "The device doesn't seem to have nl80211 interface. Ignoring.",
+                                ifindex);
+                goto nodata;
+        }
         if (r < 0)
                 return log_debug_errno(r, "Failed to request information about wifi interface %d: %m", ifindex);
         if (!reply)
-                return 0;
+                goto nodata;
 
         r = sd_netlink_message_get_errno(reply);
         if (r < 0)
@@ -37,7 +47,7 @@ int wifi_get_interface(sd_netlink *genl, int ifindex, enum nl80211_iftype *iftyp
                 return log_debug_errno(r, "Failed to determine genl family: %m");
         if (family != SD_GENL_NL80211) {
                 log_debug("Received message of unexpected genl family %u, ignoring.", family);
-                return 0;
+                goto nodata;
         }
 
         if (iftype) {
@@ -51,11 +61,20 @@ int wifi_get_interface(sd_netlink *genl, int ifindex, enum nl80211_iftype *iftyp
 
         if (ssid) {
                 r = sd_netlink_message_read_string_strdup(reply, NL80211_ATTR_SSID, ssid);
-                if (r < 0 && r != -ENODATA)
+                if (r == -ENODATA)
+                        goto nodata;
+                if (r < 0)
                         return log_debug_errno(r, "Failed to get NL80211_ATTR_SSID attribute: %m");
         }
 
-        return r == -ENODATA ? 0 : 1;
+        return 1;
+
+nodata:
+        if (iftype)
+                *iftype = 0;
+        if (ssid)
+                *ssid = NULL;
+        return 0;
 }
 
 int wifi_get_station(sd_netlink *genl, int ifindex, struct ether_addr *bssid) {
@@ -63,6 +82,10 @@ int wifi_get_station(sd_netlink *genl, int ifindex, struct ether_addr *bssid) {
         sd_genl_family family;
         int r;
 
+        assert(genl);
+        assert(ifindex > 0);
+        assert(bssid);
+
         r = sd_genl_message_new(genl, SD_GENL_NL80211, NL80211_CMD_GET_STATION, &m);
         if (r < 0)
                 return log_debug_errno(r, "Failed to create generic netlink message: %m");
@@ -79,7 +102,7 @@ int wifi_get_station(sd_netlink *genl, int ifindex, struct ether_addr *bssid) {
         if (r < 0)
                 return log_debug_errno(r, "Failed to request information about wifi station: %m");
         if (!reply)
-                return 0;
+                goto nodata;
 
         r = sd_netlink_message_get_errno(reply);
         if (r < 0)
@@ -90,12 +113,18 @@ int wifi_get_station(sd_netlink *genl, int ifindex, struct ether_addr *bssid) {
                 return log_debug_errno(r, "Failed to determine genl family: %m");
         if (family != SD_GENL_NL80211) {
                 log_debug("Received message of unexpected genl family %u, ignoring.", family);
-                return 0;
+                goto nodata;
         }
 
         r = sd_netlink_message_read_ether_addr(reply, NL80211_ATTR_MAC, bssid);
-        if (r < 0 && r != -ENODATA)
+        if (r == -ENODATA)
+                goto nodata;
+        if (r < 0)
                 return log_debug_errno(r, "Failed to get NL80211_ATTR_MAC attribute: %m");
 
-        return r == -ENODATA ? 0 : 1;
+        return 1;
+
+nodata:
+        *bssid = (struct ether_addr) {};
+        return 0;
 }
index 523040b57cbc24c7bd80dd288e2d4886007093c6..06c9710c6e329ad7837a72519656bf169709396e 100644 (file)
@@ -52,6 +52,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_LOG_TARGET,
                 ARG_LOG_COLOR,
                 ARG_LOG_LOCATION,
+                ARG_LOG_TIME,
                 ARG_EXIT_CODE,
                 ARG_TIMEOUT,
         };
@@ -61,6 +62,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "log-target",    required_argument, NULL, ARG_LOG_TARGET   },
                 { "log-color",     optional_argument, NULL, ARG_LOG_COLOR    },
                 { "log-location",  optional_argument, NULL, ARG_LOG_LOCATION },
+                { "log-time",      optional_argument, NULL, ARG_LOG_TIME     },
                 { "exit-code",     required_argument, NULL, ARG_EXIT_CODE    },
                 { "timeout",       required_argument, NULL, ARG_TIMEOUT      },
                 {}
@@ -111,6 +113,17 @@ static int parse_argv(int argc, char *argv[]) {
 
                         break;
 
+                case ARG_LOG_TIME:
+
+                        if (optarg) {
+                                r = log_show_time_from_string(optarg);
+                                if (r < 0)
+                                        log_error_errno(r, "Failed to parse log time setting %s, ignoring: %m", optarg);
+                        } else
+                                log_show_time(true);
+
+                        break;
+
                 case ARG_EXIT_CODE:
                         r = safe_atou8(optarg, &arg_exit_code);
                         if (r < 0)
@@ -549,6 +562,9 @@ int main(int argc, char *argv[]) {
                                 /* Child */
 
                                 execv(args[0], (char * const *) args);
+
+                                /* execv failed (kexec binary missing?), so try simply reboot(RB_KEXEC) */
+                                (void) reboot(cmd);
                                 _exit(EXIT_FAILURE);
                         }
 
index cf716b4e44d769d9de4d00153c87357a09ee4b0f..7029352ca5b010661d18a5939be8219f6aa77d97 100644 (file)
@@ -23,6 +23,7 @@
 #include "fd-util.h"
 #include "fileio.h"
 #include "format-util.h"
+#include "io-util.h"
 #include "log.h"
 #include "main-func.h"
 #include "parse-util.h"
@@ -70,7 +71,7 @@ static int write_hibernate_location_info(const HibernateLocation *hibernate_loca
                         return 0;
                 }
 
-                return log_debug_errno(errno, "/sys/power/resume_offset not writeable: %m");
+                return log_debug_errno(errno, "/sys/power/resume_offset not writable: %m");
         }
 
         xsprintf(offset_str, "%" PRIu64, hibernate_location->offset);
@@ -244,7 +245,6 @@ static int execute_s2h(const SleepConfig *sleep_config) {
         _cleanup_close_ int tfd = -1;
         char buf[FORMAT_TIMESPAN_MAX];
         struct itimerspec ts = {};
-        struct pollfd fds;
         int r;
 
         assert(sleep_config);
@@ -266,19 +266,14 @@ static int execute_s2h(const SleepConfig *sleep_config) {
         if (r < 0)
                 return r;
 
-        fds = (struct pollfd) {
-                .fd = tfd,
-                .events = POLLIN,
-        };
-        r = poll(&fds, 1, 0);
+        r = fd_wait_for_event(tfd, POLLIN, 0);
         if (r < 0)
-                return log_error_errno(errno, "Error polling timerfd: %m");
+                return log_error_errno(r, "Error polling timerfd: %m");
+        if (!FLAGS_SET(r, POLLIN)) /* We woke up before the alarm time, we are done. */
+                return 0;
 
         tfd = safe_close(tfd);
 
-        if (!FLAGS_SET(fds.revents, POLLIN)) /* We woke up before the alarm time, we are done. */
-                return 0;
-
         /* If woken up after alarm time, hibernate */
         log_debug("Attempting to hibernate after waking from %s timer",
                   format_timespan(buf, sizeof(buf), sleep_config->hibernate_delay_sec, USEC_PER_SEC));
index 2ee6fc2f0a6aa0fcf4fbf2539cc4ba475875262b..b461aead60cd9bc459e4604af001d25c24562d3d 100644 (file)
 
 static unsigned arg_connections_max = 256;
 static const char *arg_remote_host = NULL;
+static usec_t arg_exit_idle_time = USEC_INFINITY;
 
 typedef struct Context {
         sd_event *event;
         sd_resolve *resolve;
+        sd_event_source *idle_time;
 
         Set *listen;
         Set *connections;
@@ -75,6 +77,51 @@ static void connection_free(Connection *c) {
         free(c);
 }
 
+static int idle_time_cb(sd_event_source *s, uint64_t usec, void *userdata) {
+        Context *c = userdata;
+        int r;
+
+        if (!set_isempty(c->connections)) {
+                log_warning("Idle timer fired even though there are connections, ignoring");
+                return 0;
+        }
+
+        r = sd_event_exit(c->event, 0);
+        if (r < 0) {
+                log_warning_errno(r, "Error while stopping event loop, ignoring: %m");
+                return 0;
+        }
+        return 0;
+}
+
+static int connection_release(Connection *c) {
+        int r;
+        Context *context = c->context;
+        usec_t idle_instant;
+
+        connection_free(c);
+
+        if (arg_exit_idle_time < USEC_INFINITY && set_isempty(context->connections)) {
+                idle_instant = usec_add(now(CLOCK_MONOTONIC), arg_exit_idle_time);
+                if (context->idle_time) {
+                        r = sd_event_source_set_time(context->idle_time, idle_instant);
+                        if (r < 0)
+                                return log_error_errno(r, "Error while setting idle time: %m");
+
+                        r = sd_event_source_set_enabled(context->idle_time, SD_EVENT_ONESHOT);
+                        if (r < 0)
+                                return log_error_errno(r, "Error while enabling idle time: %m");
+                } else {
+                        r = sd_event_add_time(context->event, &context->idle_time, CLOCK_MONOTONIC,
+                                              idle_instant, 0, idle_time_cb, context);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to create idle timer: %m");
+                }
+        }
+
+        return 0;
+}
+
 static void context_clear(Context *context) {
         assert(context);
 
@@ -83,6 +130,7 @@ static void context_clear(Context *context) {
 
         sd_event_unref(context->event);
         sd_resolve_unref(context->resolve);
+        sd_event_source_unref(context->idle_time);
 }
 
 static int connection_create_pipes(Connection *c, int buffer[static 2], size_t *sz) {
@@ -206,7 +254,7 @@ static int traffic_cb(sd_event_source *s, int fd, uint32_t revents, void *userda
         return 1;
 
 quit:
-        connection_free(c);
+        connection_release(c);
         return 0; /* ignore errors, continue serving */
 }
 
@@ -269,7 +317,7 @@ static int connection_complete(Connection *c) {
         return 0;
 
 fail:
-        connection_free(c);
+        connection_release(c);
         return 0; /* ignore errors, continue serving */
 }
 
@@ -299,7 +347,7 @@ static int connect_cb(sd_event_source *s, int fd, uint32_t revents, void *userda
         return connection_complete(c);
 
 fail:
-        connection_free(c);
+        connection_release(c);
         return 0; /* ignore errors, continue serving */
 }
 
@@ -343,7 +391,7 @@ static int connection_start(Connection *c, struct sockaddr *sa, socklen_t salen)
         return 0;
 
 fail:
-        connection_free(c);
+        connection_release(c);
         return 0; /* ignore errors, continue serving */
 }
 
@@ -361,7 +409,7 @@ static int resolve_handler(sd_resolve_query *q, int ret, const struct addrinfo *
         return connection_start(c, ai->ai_addr, ai->ai_addrlen);
 
 fail:
-        connection_free(c);
+        connection_release(c);
         return 0; /* ignore errors, continue serving */
 }
 
@@ -409,7 +457,7 @@ static int resolve_remote(Connection *c) {
         return 0;
 
 fail:
-        connection_free(c);
+        connection_release(c);
         return 0; /* ignore errors, continue serving */
 }
 
@@ -426,25 +474,27 @@ static int add_connection_socket(Context *context, int fd) {
                 return 0;
         }
 
-        r = set_ensure_allocated(&context->connections, NULL);
-        if (r < 0) {
-                log_oom();
-                return 0;
+        if (context->idle_time) {
+                r = sd_event_source_set_enabled(context->idle_time, SD_EVENT_OFF);
+                if (r < 0)
+                        log_warning_errno(r, "Unable to disable idle timer, continuing: %m");
         }
 
-        c = new0(Connection, 1);
+        c = new(Connection, 1);
         if (!c) {
                 log_oom();
                 return 0;
         }
 
-        c->context = context;
-        c->server_fd = fd;
-        c->client_fd = -1;
-        c->server_to_client_buffer[0] = c->server_to_client_buffer[1] = -1;
-        c->client_to_server_buffer[0] = c->client_to_server_buffer[1] = -1;
+        *c = (Connection) {
+               .context = context,
+               .server_fd = fd,
+               .client_fd = -1,
+               .server_to_client_buffer = {-1, -1},
+               .client_to_server_buffer = {-1, -1},
+        };
 
-        r = set_put(context->connections, c);
+        r = set_ensure_put(&context->connections, NULL, c);
         if (r < 0) {
                 free(c);
                 log_oom();
@@ -496,12 +546,6 @@ static int add_listen_socket(Context *context, int fd) {
         assert(context);
         assert(fd >= 0);
 
-        r = set_ensure_allocated(&context->listen, NULL);
-        if (r < 0) {
-                log_oom();
-                return r;
-        }
-
         r = sd_is_socket(fd, 0, SOCK_STREAM, 1);
         if (r < 0)
                 return log_error_errno(r, "Failed to determine socket type: %m");
@@ -517,7 +561,7 @@ static int add_listen_socket(Context *context, int fd) {
         if (r < 0)
                 return log_error_errno(r, "Failed to add event source: %m");
 
-        r = set_put(context->listen, source);
+        r = set_ensure_put(&context->listen, NULL, source);
         if (r < 0) {
                 log_error_errno(r, "Failed to add source to set: %m");
                 sd_event_source_unref(source);
@@ -535,9 +579,13 @@ static int add_listen_socket(Context *context, int fd) {
 
 static int help(void) {
         _cleanup_free_ char *link = NULL;
+        _cleanup_free_ char *time_link = NULL;
         int r;
 
         r = terminal_urlify_man("systemd-socket-proxyd", "8", &link);
+        if (r < 0)
+                return log_oom();
+        r = terminal_urlify_man("systemd.time", "7", &time_link);
         if (r < 0)
                 return log_oom();
 
@@ -545,11 +593,14 @@ static int help(void) {
                "%1$s [SOCKET]\n\n"
                "Bidirectionally proxy local sockets to another (possibly remote) socket.\n\n"
                "  -c --connections-max=  Set the maximum number of connections to be accepted\n"
+               "     --exit-idle-time=   Exit when without a connection for this duration. See\n"
+               "                         the %3$s for time span format\n"
                "  -h --help              Show this help\n"
                "     --version           Show package version\n"
                "\nSee the %2$s for details.\n"
                , program_invocation_short_name
                , link
+               , time_link
         );
 
         return 0;
@@ -559,11 +610,13 @@ static int parse_argv(int argc, char *argv[]) {
 
         enum {
                 ARG_VERSION = 0x100,
+                ARG_EXIT_IDLE,
                 ARG_IGNORE_ENV
         };
 
         static const struct option options[] = {
                 { "connections-max", required_argument, NULL, 'c'           },
+                { "exit-idle-time",  required_argument, NULL, ARG_EXIT_IDLE },
                 { "help",            no_argument,       NULL, 'h'           },
                 { "version",         no_argument,       NULL, ARG_VERSION   },
                 {}
@@ -597,6 +650,12 @@ static int parse_argv(int argc, char *argv[]) {
 
                         break;
 
+                case ARG_EXIT_IDLE:
+                        r = parse_sec(optarg, &arg_exit_idle_time);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse --exit-idle-time= argument: %s", optarg);
+                        break;
+
                 case '?':
                         return -EINVAL;
 
index 52b9ce455557db838daa17cff58afe980694c410..ca145aebf932d2ad0ef8a37266dd8f7bec3c3ef2 100644 (file)
@@ -238,17 +238,19 @@ static int run(int argc, char *argv[]) {
                         ts = timespec_store(&_ts, t);
                 }
 
-                {
-                        struct pollfd p[3] = {
-                                {.fd = fd,            .events = events_a           },
-                                {.fd = STDIN_FILENO,  .events = events_b & POLLIN  },
-                                {.fd = STDOUT_FILENO, .events = events_b & POLLOUT },
-                        };
-
-                        r = ppoll(p, ELEMENTSOF(p), ts, NULL);
-                }
+                struct pollfd p[3] = {
+                        { .fd = fd,            .events = events_a           },
+                        { .fd = STDIN_FILENO,  .events = events_b & POLLIN  },
+                        { .fd = STDOUT_FILENO, .events = events_b & POLLOUT },
+                };
+
+                r = ppoll(p, ELEMENTSOF(p), ts, NULL);
                 if (r < 0)
                         return log_error_errno(errno, "ppoll() failed: %m");
+                if (p[0].revents & POLLNVAL ||
+                    p[1].revents & POLLNVAL ||
+                    p[2].revents & POLLNVAL)
+                        return log_error_errno(SYNTHETIC_ERRNO(EBADF), "Invalid file descriptor to poll on?");
         }
 
         return 0;
index e92c402e8145f418658216f4a01ad683383185fa..5274cd24b38704f22d0e2de18d113d2e557f8c54 100644 (file)
@@ -140,7 +140,7 @@ static int apply_all(OrderedHashmap *sysctl_options) {
 
                         k = glob_extend(&paths, pattern, GLOB_NOCHECK);
                         if (k < 0) {
-                                if (option->ignore_failure || ERRNO_IS_PRIVILEGE(r))
+                                if (option->ignore_failure || ERRNO_IS_PRIVILEGE(k))
                                         log_debug_errno(k, "Failed to resolve glob '%s', ignoring: %m",
                                                         option->key);
                                 else {
index 4a0aaad0050329aeee41344de770e6fa3ef69acf..c58a19a099d0780a099c93cf044854842cc75753 100644 (file)
 #include "bootspec.h"
 #include "bus-common-errors.h"
 #include "bus-error.h"
+#include "bus-locator.h"
+#include "bus-map-properties.h"
 #include "bus-message.h"
+#include "bus-print-properties.h"
 #include "bus-unit-procs.h"
 #include "bus-unit-util.h"
-#include "bus-util.h"
 #include "bus-wait-for-jobs.h"
 #include "bus-wait-for-units.h"
 #include "cgroup-show.h"
@@ -130,6 +132,7 @@ static const char *arg_kill_who = NULL;
 static int arg_signal = SIGTERM;
 static char *arg_root = NULL;
 static usec_t arg_when = 0;
+static const char *arg_reboot_argument = NULL;
 static enum action {
         ACTION_SYSTEMCTL,
         ACTION_HALT,
@@ -321,28 +324,17 @@ static bool install_client_side(void) {
 }
 
 static int compare_unit_info(const UnitInfo *a, const UnitInfo *b) {
-        const char *d1, *d2;
         int r;
 
         /* First, order by machine */
-        if (!a->machine && b->machine)
-                return -1;
-        if (a->machine && !b->machine)
-                return 1;
-        if (a->machine && b->machine) {
-                r = strcasecmp(a->machine, b->machine);
-                if (r != 0)
-                        return r;
-        }
+        r = strcasecmp_ptr(a->machine, b->machine);
+        if (r != 0)
+                return r;
 
         /* Second, order by unit type */
-        d1 = strrchr(a->id, '.');
-        d2 = strrchr(b->id, '.');
-        if (d1 && d2) {
-                r = strcasecmp(d1, d2);
-                if (r != 0)
-                        return r;
-        }
+        r = strcasecmp_ptr(strrchr(a->id, '.'), strrchr(b->id, '.'));
+        if (r != 0)
+                return r;
 
         /* Third, order by name */
         return strcasecmp(a->id, b->id);
@@ -390,10 +382,23 @@ static bool output_show_unit(const UnitInfo *u, char **patterns) {
         return true;
 }
 
+static int output_table(Table *table) {
+        int r;
+
+        assert(table);
+
+        if (OUTPUT_MODE_IS_JSON(arg_output))
+                r = table_print_json(table, NULL, output_mode_to_json_format_flags(arg_output) | JSON_FORMAT_COLOR_AUTO);
+        else
+                r = table_print(table, NULL);
+        if (r < 0)
+                return table_log_print_error(r);
+
+        return 0;
+}
+
 static int output_units_list(const UnitInfo *unit_infos, unsigned c) {
         _cleanup_(table_unrefp) Table *table = NULL;
-        const UnitInfo *u;
-        int job_count = 0;
         int r;
 
         table = table_new("", "unit", "load", "active", "sub", "job", "description");
@@ -401,8 +406,8 @@ static int output_units_list(const UnitInfo *unit_infos, unsigned c) {
                 return log_oom();
 
         table_set_header(table, !arg_no_legend);
-        if (arg_no_legend) {
-                /* Hide the 'glyph' column when --no-legend is requested */
+        if (arg_plain) {
+                /* Hide the 'glyph' column when --plain is requested */
                 r = table_hide_column_from_display(table, 0);
                 if (r < 0)
                         return log_error_errno(r, "Failed to hide column: %m");
@@ -410,7 +415,10 @@ static int output_units_list(const UnitInfo *unit_infos, unsigned c) {
         if (arg_full)
                 table_set_width(table, 0);
 
-        for (u = unit_infos; unit_infos && u < unit_infos + c; u++) {
+        (void) table_set_empty_string(table, "-");
+
+        int job_count = 0;
+        for (const UnitInfo *u = unit_infos; unit_infos && u < unit_infos + c; u++) {
                 _cleanup_free_ char *j = NULL;
                 const char *on_underline = "", *on_loaded = "", *on_active = "";
                 const char *on_circle = "", *id;
@@ -423,14 +431,15 @@ static int output_units_list(const UnitInfo *unit_infos, unsigned c) {
                 }
 
                 if (STR_IN_SET(u->load_state, "error", "not-found", "bad-setting", "masked") && !arg_plain) {
-                        on_circle = ansi_highlight_yellow();
+                        on_circle = underline ? ansi_highlight_yellow_underline() : ansi_highlight_yellow();
                         circle = true;
                         on_loaded = underline ? ansi_highlight_red_underline() : ansi_highlight_red();
                 } else if (streq(u->active_state, "failed") && !arg_plain) {
-                        on_circle = ansi_highlight_red();
+                        on_circle = underline ? ansi_highlight_red_underline() : ansi_highlight_red();
                         circle = true;
                         on_active = underline ? ansi_highlight_red_underline() : ansi_highlight_red();
                 } else {
+                        on_circle = on_underline;
                         on_active = on_underline;
                         on_loaded = on_underline;
                 }
@@ -446,19 +455,19 @@ static int output_units_list(const UnitInfo *unit_infos, unsigned c) {
 
                 r = table_add_many(table,
                                    TABLE_STRING, circle ? special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE) : " ",
-                                   TABLE_SET_COLOR, on_circle,
+                                   TABLE_SET_BOTH_COLORS, on_circle,
                                    TABLE_STRING, id,
-                                   TABLE_SET_COLOR, on_active,
+                                   TABLE_SET_BOTH_COLORS, on_active,
                                    TABLE_STRING, u->load_state,
-                                   TABLE_SET_COLOR, on_loaded,
+                                   TABLE_SET_BOTH_COLORS, on_loaded,
                                    TABLE_STRING, u->active_state,
-                                   TABLE_SET_COLOR, on_active,
+                                   TABLE_SET_BOTH_COLORS, on_active,
                                    TABLE_STRING, u->sub_state,
-                                   TABLE_SET_COLOR, on_active,
+                                   TABLE_SET_BOTH_COLORS, on_active,
                                    TABLE_STRING, u->job_id ? u->job_type: "",
-                                   TABLE_SET_COLOR, u->job_id ? on_underline : "",
+                                   TABLE_SET_BOTH_COLORS, u->job_id ? on_underline : "",
                                    TABLE_STRING, u->description,
-                                   TABLE_SET_COLOR, on_underline);
+                                   TABLE_SET_BOTH_COLORS, on_underline);
                 if (r < 0)
                         return table_log_add_error(r);
 
@@ -473,9 +482,9 @@ static int output_units_list(const UnitInfo *unit_infos, unsigned c) {
                         return log_error_errno(r, "Failed to hide column: %m");
         }
 
-        r = table_print(table, NULL);
+        r = output_table(table);
         if (r < 0)
-                return log_error_errno(r, "Failed to print the table: %m");
+                return r;
 
         if (!arg_no_legend) {
                 const char *on, *off;
@@ -529,13 +538,7 @@ static int get_unit_list(
         assert(unit_infos);
         assert(_reply);
 
-        r = sd_bus_message_new_method_call(
-                        bus,
-                        &m,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "ListUnitsByPatterns");
+        r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "ListUnitsByPatterns");
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -556,13 +559,7 @@ static int get_unit_list(
                 m = sd_bus_message_unref(m);
                 sd_bus_error_free(&error);
 
-                r = sd_bus_message_new_method_call(
-                                bus,
-                                &m,
-                                "org.freedesktop.systemd1",
-                                "/org/freedesktop/systemd1",
-                                "org.freedesktop.systemd1.Manager",
-                                "ListUnitsFiltered");
+                r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "ListUnitsFiltered");
                 if (r < 0)
                         return bus_log_create_error(r);
 
@@ -968,21 +965,15 @@ static int socket_info_compare(const struct socket_info *a, const struct socket_
         assert(a);
         assert(b);
 
-        if (!a->machine && b->machine)
-                return -1;
-        if (a->machine && !b->machine)
-                return 1;
-        if (a->machine && b->machine) {
-                r = strcasecmp(a->machine, b->machine);
-                if (r != 0)
-                        return r;
-        }
+        r = strcasecmp_ptr(a->machine, b->machine);
+        if (r != 0)
+                return r;
 
         r = strcmp(a->path, b->path);
-        if (r == 0)
-                r = strcmp(a->type, b->type);
+        if (r != 0)
+                return r;
 
-        return r;
+        return strcmp(a->type, b->type);
 }
 
 static int output_sockets_list(struct socket_info *socket_infos, unsigned cs) {
@@ -991,7 +982,7 @@ static int output_sockets_list(struct socket_info *socket_infos, unsigned cs) {
         const char *on, *off;
         int r;
 
-        table = table_new("listen", "type", "units", "activates");
+        table = table_new("listen", "type", "unit", "activates");
         if (!table)
                 return log_oom();
 
@@ -1006,9 +997,11 @@ static int output_sockets_list(struct socket_info *socket_infos, unsigned cs) {
         if (arg_full)
                 table_set_width(table, 0);
 
+        (void) table_set_empty_string(table, "-");
+
         if (cs) {
                 for (s = socket_infos; s < socket_infos + cs; s++) {
-                        _cleanup_free_ char *j = NULL, *activates = NULL;
+                        _cleanup_free_ char *j = NULL;
                         const char *path;
 
                         if (s->machine) {
@@ -1019,17 +1012,25 @@ static int output_sockets_list(struct socket_info *socket_infos, unsigned cs) {
                         } else
                                 path = s->path;
 
-                        activates = strv_join(s->triggered, ", ");
-                        if (!activates)
-                                return log_oom();
-
                         r = table_add_many(table,
                                            TABLE_STRING, path,
                                            TABLE_STRING, s->type,
-                                           TABLE_STRING, s->id,
-                                           TABLE_STRING, activates);
+                                           TABLE_STRING, s->id);
                         if (r < 0)
                                 return table_log_add_error(r);
+
+                        if (strv_isempty(s->triggered))
+                                r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
+                        else if (strv_length(s->triggered) == 1)
+                                r = table_add_cell(table, NULL, TABLE_STRING, s->triggered[0]);
+                        else
+                                /* This should never happen, currently our socket units can only trigger a
+                                 * single unit. But let's handle this anyway, who knows what the future
+                                 * brings? */
+                                r = table_add_cell(table, NULL, TABLE_STRV, s->triggered);
+                        if (r < 0)
+                                return table_log_add_error(r);
+
                 }
 
                 on = ansi_highlight();
@@ -1039,9 +1040,9 @@ static int output_sockets_list(struct socket_info *socket_infos, unsigned cs) {
                 off = ansi_normal();
         }
 
-        r = table_print(table, NULL);
+        r = output_table(table);
         if (r < 0)
-                return log_error_errno(r, "Failed to print the table: %m");
+                return r;
 
         if (!arg_no_legend) {
                 printf("\n%s%u sockets listed.%s\n", on, cs, off);
@@ -1217,15 +1218,9 @@ static int timer_info_compare(const struct timer_info *a, const struct timer_inf
         assert(a);
         assert(b);
 
-        if (!a->machine && b->machine)
-                return -1;
-        if (a->machine && !b->machine)
-                return 1;
-        if (a->machine && b->machine) {
-                r = strcasecmp(a->machine, b->machine);
-                if (r != 0)
-                        return r;
-        }
+        r = strcasecmp_ptr(a->machine, b->machine);
+        if (r != 0)
+                return r;
 
         r = CMP(a->next_elapse, b->next_elapse);
         if (r != 0)
@@ -1250,6 +1245,8 @@ static int output_timers_list(struct timer_info *timer_infos, unsigned n) {
         if (arg_full)
                 table_set_width(table, 0);
 
+        (void) table_set_empty_string(table, "-");
+
         if (n > 0) {
                 for (t = timer_infos; t < timer_infos + n; t++) {
                         _cleanup_free_ char *j = NULL, *activates = NULL;
@@ -1285,9 +1282,9 @@ static int output_timers_list(struct timer_info *timer_infos, unsigned n) {
                 off = ansi_normal();
         }
 
-        r = table_print(table, NULL);
+        r = output_table(table);
         if (r < 0)
-                return log_error_errno(r, "Failed to print the table: %m");
+                return r;
 
         if (!arg_no_legend) {
                 printf("\n%s%u timers listed.%s\n", on, n, off);
@@ -1441,9 +1438,18 @@ static bool output_show_unit_file(const UnitFileList *u, char **states, char **p
         return true;
 }
 
+static bool show_preset_for_state(UnitFileState state) {
+        /* Don't show preset state in those unit file states, it'll only confuse users. */
+        return !IN_SET(state,
+                       UNIT_FILE_ALIAS,
+                       UNIT_FILE_STATIC,
+                       UNIT_FILE_GENERATED,
+                       UNIT_FILE_TRANSIENT);
+}
+
 static int output_unit_file_list(const UnitFileList *units, unsigned c) {
         _cleanup_(table_unrefp) Table *table = NULL;
-        const UnitFileList *u;
+        _cleanup_(unit_file_presets_freep) UnitFilePresets presets = {};
         int r;
 
         table = table_new("unit file", "state", "vendor preset");
@@ -1454,9 +1460,10 @@ static int output_unit_file_list(const UnitFileList *units, unsigned c) {
         if (arg_full)
                 table_set_width(table, 0);
 
-        for (u = units; u < units + c; u++) {
+        (void) table_set_empty_string(table, "-");
+
+        for (const UnitFileList *u = units; u < units + c; u++) {
                 const char *on_underline = NULL, *on_unit_color = NULL, *id;
-                const char *on_preset_color = NULL, *unit_preset_str;
                 bool underline;
 
                 underline = u + 1 < units + c &&
@@ -1471,39 +1478,52 @@ static int output_unit_file_list(const UnitFileList *units, unsigned c) {
                            UNIT_FILE_DISABLED,
                            UNIT_FILE_BAD))
                         on_unit_color = underline ? ansi_highlight_red_underline() : ansi_highlight_red();
-                else if (u->state == UNIT_FILE_ENABLED)
+                else if (IN_SET(u->state,
+                                UNIT_FILE_ENABLED,
+                                UNIT_FILE_ALIAS))
                         on_unit_color = underline ? ansi_highlight_green_underline() : ansi_highlight_green();
                 else
                         on_unit_color = on_underline;
 
                 id = basename(u->path);
 
-                r = unit_file_query_preset(arg_scope, arg_root, id);
-                if (r < 0) {
-                        unit_preset_str = "n/a";
-                        on_preset_color = underline ? on_underline : ansi_normal();
-                } else if (r == 0) {
-                        unit_preset_str = "disabled";
-                        on_preset_color = underline ? ansi_highlight_red_underline() : ansi_highlight_red();
-                } else {
-                        unit_preset_str = "enabled";
-                        on_preset_color = underline ? ansi_highlight_green_underline() : ansi_highlight_green();
-                }
-
                 r = table_add_many(table,
                                    TABLE_STRING, id,
-                                   TABLE_SET_COLOR, strempty(on_underline),
+                                   TABLE_SET_BOTH_COLORS, strempty(on_underline),
                                    TABLE_STRING, unit_file_state_to_string(u->state),
-                                   TABLE_SET_COLOR, strempty(on_unit_color),
-                                   TABLE_STRING, unit_preset_str,
-                                   TABLE_SET_COLOR, strempty(on_preset_color));
+                                   TABLE_SET_BOTH_COLORS, strempty(on_unit_color));
+                if (r < 0)
+                        return table_log_add_error(r);
+
+                if (show_preset_for_state(u->state)) {
+                        const char *unit_preset_str, *on_preset_color;
+
+                        r = unit_file_query_preset(arg_scope, arg_root, id, &presets);
+                        if (r < 0) {
+                                unit_preset_str = "n/a";
+                                on_preset_color = underline ? on_underline : ansi_normal();
+                        } else if (r == 0) {
+                                unit_preset_str = "disabled";
+                                on_preset_color = underline ? ansi_highlight_red_underline() : ansi_highlight_red();
+                        } else {
+                                unit_preset_str = "enabled";
+                                on_preset_color = underline ? ansi_highlight_green_underline() : ansi_highlight_green();
+                        }
+
+                        r = table_add_many(table,
+                                           TABLE_STRING, unit_preset_str,
+                                           TABLE_SET_BOTH_COLORS, strempty(on_preset_color));
+                } else
+                        r = table_add_many(table,
+                                           TABLE_EMPTY,
+                                           TABLE_SET_BOTH_COLORS, underline ? ansi_grey_underline() : ansi_grey());
                 if (r < 0)
                         return table_log_add_error(r);
         }
 
-        r = table_print(table, NULL);
+        r = output_table(table);
         if (r < 0)
-                return log_error_errno(r, "Failed to print the table: %m");
+                return r;
 
         if (!arg_no_legend)
                 printf("\n%u unit files listed.\n", c);
@@ -1567,13 +1587,7 @@ static int list_unit_files(int argc, char *argv[], void *userdata) {
                 if (r < 0)
                         return r;
 
-                r = sd_bus_message_new_method_call(
-                                bus,
-                                &m,
-                                "org.freedesktop.systemd1",
-                                "/org/freedesktop/systemd1",
-                                "org.freedesktop.systemd1.Manager",
-                                "ListUnitFilesByPatterns");
+                r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "ListUnitFilesByPatterns");
                 if (r < 0)
                         return bus_log_create_error(r);
 
@@ -1605,13 +1619,7 @@ static int list_unit_files(int argc, char *argv[], void *userdata) {
                         m = sd_bus_message_unref(m);
                         sd_bus_error_free(&error);
 
-                        r = sd_bus_message_new_method_call(
-                                        bus,
-                                        &m,
-                                        "org.freedesktop.systemd1",
-                                        "/org/freedesktop/systemd1",
-                                        "org.freedesktop.systemd1.Manager",
-                                        "ListUnitFiles");
+                        r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "ListUnitFiles");
                         if (r < 0)
                                 return bus_log_create_error(r);
 
@@ -1659,6 +1667,9 @@ static int list_unit_files(int argc, char *argv[], void *userdata) {
                 for (unit = units; unit < units + c; unit++)
                         free(unit->path);
 
+        if (c == 0)
+                return -ENOENT;
+
         return 0;
 }
 
@@ -1984,8 +1995,8 @@ static int output_machines_list(struct machine_info *machine_infos, unsigned n)
                 return log_oom();
 
         table_set_header(table, !arg_no_legend);
-        if (arg_no_legend) {
-                /* Hide the 'glyph' column when --no-legend is requested */
+        if (arg_plain) {
+                /* Hide the 'glyph' column when --plain is requested */
                 r = table_hide_column_from_display(table, 0);
                 if (r < 0)
                         return log_error_errno(r, "Failed to hide column: %m");
@@ -1993,6 +2004,8 @@ static int output_machines_list(struct machine_info *machine_infos, unsigned n)
         if (arg_full)
                 table_set_width(table, 0);
 
+        (void) table_set_empty_string(table, "-");
+
         for (m = machine_infos; m < machine_infos + n; m++) {
                 _cleanup_free_ char *mname = NULL;
                 const char *on_state = "", *on_failed = "";
@@ -2030,9 +2043,9 @@ static int output_machines_list(struct machine_info *machine_infos, unsigned n)
                         return table_log_add_error(r);
         }
 
-        r = table_print(table, NULL);
+        r = output_table(table);
         if (r < 0)
-                return log_error_errno(r, "Failed to print the table: %m");
+                return r;
 
         if (!arg_no_legend) {
                 printf("\n");
@@ -2066,46 +2079,83 @@ static int list_machines(int argc, char *argv[], void *userdata) {
         return rc;
 }
 
-static int get_default(int argc, char *argv[], void *userdata) {
-        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
-        _cleanup_free_ char *_path = NULL;
-        const char *path;
+static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
+        char **ret = data;
+
+        if (streq(key, "systemd.unit")) {
+                if (proc_cmdline_value_missing(key, value))
+                        return 0;
+                if (!unit_name_is_valid(value, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE))
+                        return log_warning("Unit name specified on %s= is not valid, ignoring: %s", key, value);
+
+                return free_and_strdup_warn(ret, key);
+
+        } else if (!value) {
+                if (runlevel_to_target(key))
+                        return free_and_strdup_warn(ret, key);
+        }
+
+        return 0;
+}
+
+static void emit_cmdline_warning(void) {
+        if (arg_quiet || arg_root)
+                /* don't bother checking the commandline if we're operating on a container */
+                return;
+
+        _cleanup_free_ char *override = NULL;
+        int r;
+
+        r = proc_cmdline_parse(parse_proc_cmdline_item, &override, 0);
+        if (r < 0)
+                log_debug_errno(r, "Failed to parse kernel command line, ignoring: %m");
+        if (override)
+                log_notice("Note: found \"%s\" on the kernel commandline, which overrides the default unit.",
+                           override);
+}
+
+static int determine_default(char **ret_name) {
         int r;
 
         if (install_client_side()) {
-                r = unit_file_get_default(arg_scope, arg_root, &_path);
+                r = unit_file_get_default(arg_scope, arg_root, ret_name);
                 if (r < 0)
                         return log_error_errno(r, "Failed to get default target: %m");
-                path = _path;
+                return 0;
 
-                r = 0;
         } else {
-                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
                 sd_bus *bus;
+                _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+                const char *name;
 
                 r = acquire_bus(BUS_MANAGER, &bus);
                 if (r < 0)
                         return r;
 
-                r = sd_bus_call_method(
-                                bus,
-                                "org.freedesktop.systemd1",
-                                "/org/freedesktop/systemd1",
-                                "org.freedesktop.systemd1.Manager",
-                                "GetDefaultTarget",
-                                &error,
-                                &reply,
-                                NULL);
+                r = bus_call_method(bus, bus_systemd_mgr, "GetDefaultTarget", &error, &reply, NULL);
                 if (r < 0)
                         return log_error_errno(r, "Failed to get default target: %s", bus_error_message(&error, r));
 
-                r = sd_bus_message_read(reply, "s", &path);
+                r = sd_bus_message_read(reply, "s", &name);
                 if (r < 0)
                         return bus_log_parse_error(r);
+
+                return free_and_strdup_warn(ret_name, name);
         }
+}
 
-        if (path)
-                printf("%s\n", path);
+static int get_default(int argc, char *argv[], void *userdata) {
+        _cleanup_free_ char *name = NULL;
+        int r;
+
+        r = determine_default(&name);
+        if (r < 0)
+                return r;
+
+        printf("%s\n", name);
+
+        emit_cmdline_warning();
 
         return 0;
 }
@@ -2142,15 +2192,7 @@ static int set_default(int argc, char *argv[], void *userdata) {
                 if (r < 0)
                         return r;
 
-                r = sd_bus_call_method(
-                                bus,
-                                "org.freedesktop.systemd1",
-                                "/org/freedesktop/systemd1",
-                                "org.freedesktop.systemd1.Manager",
-                                "SetDefaultTarget",
-                                &error,
-                                &reply,
-                                "sb", unit, 1);
+                r = bus_call_method(bus, bus_systemd_mgr, "SetDefaultTarget", &error, &reply, "sb", unit, 1);
                 if (r < 0)
                         return log_error_errno(r, "Failed to set default target: %s", bus_error_message(&error, r));
 
@@ -2165,6 +2207,19 @@ static int set_default(int argc, char *argv[], void *userdata) {
                         r = 0;
         }
 
+        emit_cmdline_warning();
+
+        if (!arg_quiet) {
+                _cleanup_free_ char *final = NULL;
+
+                r = determine_default(&final);
+                if (r < 0)
+                        return r;
+
+                if (!streq(final, unit))
+                        log_notice("Note: \"%s\" is the default unit (possibly a runtime override).", final);
+        }
+
 finish:
         unit_file_changes_free(changes, n_changes);
 
@@ -2180,15 +2235,7 @@ static int output_waiting_jobs(sd_bus *bus, Table *table, uint32_t id, const cha
 
         assert(bus);
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        method,
-                        &error,
-                        &reply,
-                        "u", id);
+        r = bus_call_method(bus, bus_systemd_mgr, method, &error, &reply, "u", id);
         if (r < 0)
                 return log_debug_errno(r, "Failed to get waiting jobs for job %" PRIu32, id);
 
@@ -2255,13 +2302,14 @@ static int output_jobs_list(sd_bus *bus, const struct job_info* jobs, unsigned n
         if (arg_full)
                 table_set_width(table, 0);
 
+        (void) table_set_empty_string(table, "-");
+
         for (j = jobs; j < jobs + n; j++) {
                 if (streq(j->state, "running"))
                         on = ansi_highlight();
                 else
                         on =  "";
 
-
                 r = table_add_many(table,
                                    TABLE_UINT, j->id,
                                    TABLE_STRING, j->name,
@@ -2312,15 +2360,7 @@ static int list_jobs(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return r;
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "ListJobs",
-                        &error,
-                        &reply,
-                        NULL);
+        r = bus_call_method(bus, bus_systemd_mgr, "ListJobs", &error, &reply, NULL);
         if (r < 0)
                 return log_error_errno(r, "Failed to list jobs: %s", bus_error_message(&error, r));
 
@@ -2376,15 +2416,7 @@ static int cancel_job(int argc, char *argv[], void *userdata) {
                 if (q < 0)
                         return log_error_errno(q, "Failed to parse job id \"%s\": %m", *name);
 
-                q = sd_bus_call_method(
-                                bus,
-                                "org.freedesktop.systemd1",
-                                "/org/freedesktop/systemd1",
-                                "org.freedesktop.systemd1.Manager",
-                                "CancelJob",
-                                &error,
-                                NULL,
-                                "u", id);
+                q = bus_call_method(bus, bus_systemd_mgr, "CancelJob", &error, NULL, "u", id);
                 if (q < 0) {
                         log_error_errno(q, "Failed to cancel job %"PRIu32": %s", id, bus_error_message(&error, q));
                         if (r == 0)
@@ -2406,15 +2438,7 @@ static int need_daemon_reload(sd_bus *bus, const char *unit) {
         /* We don't use unit_dbus_path_from_name() directly since we
          * don't want to load the unit if it isn't loaded. */
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "GetUnit",
-                        NULL,
-                        &reply,
-                        "s", unit);
+        r = bus_call_method(bus, bus_systemd_mgr, "GetUnit", NULL, &reply, "s", unit);
         if (r < 0)
                 return r;
 
@@ -2628,7 +2652,7 @@ static int unit_find_paths(
                 if (ret_dropin_paths) {
                         r = unit_file_find_dropin_paths(arg_root, lp->search_path, NULL,
                                                         ".d", ".conf",
-                                                        names, &dropins);
+                                                        NULL, names, &dropins);
                         if (r < 0)
                                 return r;
                 }
@@ -2838,11 +2862,9 @@ static int start_unit_one(
                 _cleanup_(sd_bus_error_free) sd_bus_error enqueue_error = SD_BUS_ERROR_NULL;
 
                 /* Use the new, fancy EnqueueUnitJob() API if the user wants us to print the transaction */
-                r = sd_bus_call_method(
+                r = bus_call_method(
                                 bus,
-                                "org.freedesktop.systemd1",
-                                "/org/freedesktop/systemd1",
-                                "org.freedesktop.systemd1.Manager",
+                                bus_systemd_mgr,
                                 "EnqueueUnitJob",
                                 &enqueue_error,
                                 &reply,
@@ -2888,15 +2910,7 @@ static int start_unit_one(
         }
 
         if (!done) {
-                r = sd_bus_call_method(
-                                bus,
-                                "org.freedesktop.systemd1",
-                                "/org/freedesktop/systemd1",
-                                "org.freedesktop.systemd1.Manager",
-                                method,
-                                error,
-                                &reply,
-                                "ss", name, mode);
+                r = bus_call_method(bus, bus_systemd_mgr, method, error, &reply, "ss", name, mode);
                 if (r < 0)
                         goto fail;
 
@@ -3085,15 +3099,7 @@ static int start_unit(int argc, char *argv[], void *userdata) {
         }
 
         if (arg_wait) {
-                r = sd_bus_call_method_async(
-                                bus,
-                                NULL,
-                                "org.freedesktop.systemd1",
-                                "/org/freedesktop/systemd1",
-                                "org.freedesktop.systemd1.Manager",
-                                "Subscribe",
-                                NULL, NULL,
-                                NULL);
+                r = bus_call_method_async(bus, NULL, bus_systemd_mgr, "Subscribe", NULL, NULL, NULL);
                 if (r < 0)
                         return log_error_errno(r, "Failed to enable subscription: %m");
 
@@ -3160,18 +3166,7 @@ static int logind_set_wall_message(void) {
         if (arg_dry_run)
                 return 0;
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.login1",
-                        "/org/freedesktop/login1",
-                        "org.freedesktop.login1.Manager",
-                        "SetWallMessage",
-                        &error,
-                        NULL,
-                        "sb",
-                        m,
-                        !arg_no_wall);
-
+        r = bus_call_method(bus, bus_login_mgr, "SetWallMessage", &error, NULL, "sb", m, !arg_no_wall);
         if (r < 0)
                 return log_warning_errno(r, "Failed to set wall message, ignoring: %s", bus_error_message(&error, r));
         return 0;
@@ -3213,15 +3208,7 @@ static int logind_reboot(enum action a) {
         if (arg_dry_run)
                 return 0;
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.login1",
-                        "/org/freedesktop/login1",
-                        "org.freedesktop.login1.Manager",
-                        actions[a].method,
-                        &error,
-                        NULL,
-                        "b", arg_ask_password);
+        r = bus_call_method(bus, bus_login_mgr, actions[a].method, &error, NULL, "b", arg_ask_password);
         if (r < 0)
                 return log_error_errno(r, "Failed to %s via logind: %s", actions[a].description, bus_error_message(&error, r));
 
@@ -3261,15 +3248,7 @@ static int logind_check_inhibitors(enum action a) {
         if (r < 0)
                 return r;
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.login1",
-                        "/org/freedesktop/login1",
-                        "org.freedesktop.login1.Manager",
-                        "ListInhibitors",
-                        NULL,
-                        &reply,
-                        NULL);
+        r = bus_call_method(bus, bus_login_mgr, "ListInhibitors", NULL, &reply, NULL);
         if (r < 0)
                 /* If logind is not around, then there are no inhibitors... */
                 return 0;
@@ -3364,15 +3343,7 @@ static int prepare_firmware_setup(void) {
         if (r < 0)
                 return r;
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.login1",
-                        "/org/freedesktop/login1",
-                        "org.freedesktop.login1.Manager",
-                        "SetRebootToFirmwareSetup",
-                        &error,
-                        NULL,
-                        "b", true);
+        r = bus_call_method(bus, bus_login_mgr, "SetRebootToFirmwareSetup", &error, NULL, "b", true);
         if (r < 0)
                 return log_error_errno(r, "Cannot indicate to EFI to boot into setup mode: %s", bus_error_message(&error, r));
 
@@ -3397,15 +3368,7 @@ static int prepare_boot_loader_menu(void) {
         if (r < 0)
                 return r;
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.login1",
-                        "/org/freedesktop/login1",
-                        "org.freedesktop.login1.Manager",
-                        "SetRebootToBootLoaderMenu",
-                        &error,
-                        NULL,
-                        "t", arg_boot_loader_menu);
+        r = bus_call_method(bus, bus_login_mgr, "SetRebootToBootLoaderMenu", &error, NULL, "t", arg_boot_loader_menu);
         if (r < 0)
                 return log_error_errno(r, "Cannot indicate to boot loader to enter boot loader entry menu: %s", bus_error_message(&error, r));
 
@@ -3430,15 +3393,7 @@ static int prepare_boot_loader_entry(void) {
         if (r < 0)
                 return r;
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.login1",
-                        "/org/freedesktop/login1",
-                        "org.freedesktop.login1.Manager",
-                        "SetRebootToBootLoaderEntry",
-                        &error,
-                        NULL,
-                        "s", arg_boot_loader_entry);
+        r = bus_call_method(bus, bus_login_mgr, "SetRebootToBootLoaderEntry", &error, NULL, "s", arg_boot_loader_entry);
         if (r < 0)
                 return log_error_errno(r, "Cannot set boot into loader entry '%s': %s", arg_boot_loader_entry, bus_error_message(&error, r));
 
@@ -3541,15 +3496,7 @@ static int set_exit_code(uint8_t code) {
         if (r < 0)
                 return r;
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "SetExitCode",
-                        &error,
-                        NULL,
-                        "y", code);
+        r = bus_call_method(bus, bus_systemd_mgr, "SetExitCode", &error, NULL, "y", code);
         if (r < 0)
                 return log_error_errno(r, "Failed to set exit code: %s", bus_error_message(&error, r));
 
@@ -3588,10 +3535,23 @@ static int start_special(int argc, char *argv[], void *userdata) {
         if (r < 0)
                 return r;
 
-        if (a == ACTION_REBOOT && argc > 1) {
-                r = update_reboot_parameter_and_warn(argv[1], false);
-                if (r < 0)
-                        return r;
+        if (a == ACTION_REBOOT) {
+                const char *arg = NULL;
+
+                if (argc > 1) {
+                        if (arg_reboot_argument)
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Both --reboot-argument= and positional argument passed to reboot command, refusing.");
+
+                        log_notice("Positional argument to reboot command is deprecated, please use --reboot-argument= instead. Accepting anyway.");
+                        arg = argv[1];
+                } else
+                        arg = arg_reboot_argument;
+
+                if (arg) {
+                        r = update_reboot_parameter_and_warn(arg, false);
+                        if (r < 0)
+                                return r;
+                }
 
         } else if (a == ACTION_KEXEC) {
                 r = load_kexec_kernel();
@@ -3754,11 +3714,9 @@ static int kill_unit(int argc, char *argv[], void *userdata) {
         STRV_FOREACH(name, names) {
                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
 
-                q = sd_bus_call_method(
+                q = bus_call_method(
                                 bus,
-                                "org.freedesktop.systemd1",
-                                "/org/freedesktop/systemd1",
-                                "org.freedesktop.systemd1.Manager",
+                                bus_systemd_mgr,
                                 "KillUnit",
                                 &error,
                                 NULL,
@@ -3773,11 +3731,12 @@ static int kill_unit(int argc, char *argv[], void *userdata) {
         return r;
 }
 
-static int clean_unit(int argc, char *argv[], void *userdata) {
+static int clean_or_freeze_unit(int argc, char *argv[], void *userdata) {
         _cleanup_(bus_wait_for_units_freep) BusWaitForUnits *w = NULL;
         _cleanup_strv_free_ char **names = NULL;
         int r, ret = EXIT_SUCCESS;
         char **name;
+        const char *method;
         sd_bus *bus;
 
         r = acquire_bus(BUS_FULL, &bus);
@@ -3802,21 +3761,22 @@ static int clean_unit(int argc, char *argv[], void *userdata) {
                         return log_error_errno(r, "Failed to allocate unit waiter: %m");
         }
 
+        if (streq(argv[0], "clean"))
+                method = "CleanUnit";
+        else if (streq(argv[0], "freeze"))
+                method = "FreezeUnit";
+        else if (streq(argv[0], "thaw"))
+                method = "ThawUnit";
+        else
+                assert_not_reached("Unhandled method");
+
         STRV_FOREACH(name, names) {
                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
                 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
 
                 if (w) {
                         /* If we shall wait for the cleaning to complete, let's add a ref on the unit first */
-                        r = sd_bus_call_method(
-                                        bus,
-                                        "org.freedesktop.systemd1",
-                                        "/org/freedesktop/systemd1",
-                                        "org.freedesktop.systemd1.Manager",
-                                        "RefUnit",
-                                        &error,
-                                        NULL,
-                                        "s", *name);
+                        r = bus_call_method(bus, bus_systemd_mgr, "RefUnit", &error, NULL, "s", *name);
                         if (r < 0) {
                                 log_error_errno(r, "Failed to add reference to unit %s: %s", *name, bus_error_message(&error, r));
                                 if (ret == EXIT_SUCCESS)
@@ -3825,13 +3785,7 @@ static int clean_unit(int argc, char *argv[], void *userdata) {
                         }
                 }
 
-                r = sd_bus_message_new_method_call(
-                                bus,
-                                &m,
-                                "org.freedesktop.systemd1",
-                                "/org/freedesktop/systemd1",
-                                "org.freedesktop.systemd1.Manager",
-                                "CleanUnit");
+                r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, method);
                 if (r < 0)
                         return bus_log_create_error(r);
 
@@ -3839,13 +3793,15 @@ static int clean_unit(int argc, char *argv[], void *userdata) {
                 if (r < 0)
                         return bus_log_create_error(r);
 
-                r = sd_bus_message_append_strv(m, arg_clean_what);
-                if (r < 0)
-                        return bus_log_create_error(r);
+                if (streq(method, "CleanUnit")) {
+                        r = sd_bus_message_append_strv(m, arg_clean_what);
+                        if (r < 0)
+                                return bus_log_create_error(r);
+                }
 
                 r = sd_bus_call(bus, m, 0, &error, NULL);
                 if (r < 0) {
-                        log_error_errno(r, "Failed to clean unit %s: %s", *name, bus_error_message(&error, r));
+                        log_error_errno(r, "Failed to %s unit %s: %s", argv[0], *name, bus_error_message(&error, r));
                         if (ret == EXIT_SUCCESS) {
                                 ret = r;
                                 continue;
@@ -3985,6 +3941,7 @@ typedef struct UnitStatusInfo {
         const char *id;
         const char *load_state;
         const char *active_state;
+        const char *freezer_state;
         const char *sub_state;
         const char *unit_file_state;
         const char *unit_file_preset;
@@ -4121,7 +4078,7 @@ static void print_status_info(
                 bool *ellipsized) {
 
         char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], since2[FORMAT_TIMESTAMP_MAX];
-        const char *s1, *s2, *active_on, *active_off, *on, *off, *ss;
+        const char *s1, *s2, *active_on, *active_off, *on, *off, *ss, *fs;
         _cleanup_free_ char *formatted_path = NULL;
         ExecStatusInfo *p;
         usec_t timestamp;
@@ -4159,14 +4116,18 @@ static void print_status_info(
         if (!isempty(i->load_error))
                 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) &&
-                 !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))
-                printf("     Loaded: %s%s%s (%s; %s)\n",
-                       on, strna(i->load_state), off, path, i->unit_file_state);
-        else if (path)
+        else if (path && !isempty(i->unit_file_state)) {
+                bool show_preset = !isempty(i->unit_file_preset) &&
+                        show_preset_for_state(unit_file_state_from_string(i->unit_file_state));
+
+                printf("     Loaded: %s%s%s (%s; %s%s%s)\n",
+                       on, strna(i->load_state), off,
+                       path,
+                       i->unit_file_state,
+                       show_preset ? "; vendor preset: " : "",
+                       show_preset ? i->unit_file_preset : "");
+
+        } else if (path)
                 printf("     Loaded: %s%s%s (%s)\n",
                        on, strna(i->load_state), off, path);
         else
@@ -4221,6 +4182,10 @@ static void print_status_info(
                 printf("     Active: %s%s%s",
                        active_on, strna(i->active_state), active_off);
 
+        fs = !isempty(i->freezer_state) && !streq(i->freezer_state, "running") ? i->freezer_state : NULL;
+        if (fs)
+                printf(" %s(%s)%s", ansi_highlight_yellow(), fs, ansi_normal());
+
         if (!isempty(i->result) && !streq(i->result, "success"))
                 printf(" (Result: %s)", i->result);
 
@@ -4856,13 +4821,13 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m
 
                 } else if (STR_IN_SET(name, "SystemCallFilter", "RestrictAddressFamilies")) {
                         _cleanup_strv_free_ char **l = NULL;
-                        int whitelist;
+                        int allow_list;
 
                         r = sd_bus_message_enter_container(m, 'r', "bas");
                         if (r < 0)
                                 return bus_log_parse_error(r);
 
-                        r = sd_bus_message_read(m, "b", &whitelist);
+                        r = sd_bus_message_read(m, "b", &allow_list);
                         if (r < 0)
                                 return bus_log_parse_error(r);
 
@@ -4874,7 +4839,7 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m
                         if (r < 0)
                                 return bus_log_parse_error(r);
 
-                        if (all || whitelist || !strv_isempty(l)) {
+                        if (all || allow_list || !strv_isempty(l)) {
                                 bool first = true;
                                 char **i;
 
@@ -4883,7 +4848,7 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m
                                         fputc('=', stdout);
                                 }
 
-                                if (!whitelist)
+                                if (!allow_list)
                                         fputc('~', stdout);
 
                                 STRV_FOREACH(i, l) {
@@ -5225,7 +5190,7 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m
 
                         return 1;
 
-                } else if (contents[0] == SD_BUS_TYPE_BYTE && streq(name, "StandardInputData")) {
+                } else if (contents[0] == SD_BUS_TYPE_BYTE && STR_IN_SET(name, "StandardInputData", "RootHashSignature")) {
                         _cleanup_free_ char *h = NULL;
                         const void *p;
                         size_t sz;
@@ -5478,12 +5443,14 @@ static int show_one(
         static const struct bus_properties_map property_map[] = {
                 { "LoadState",                      "s",               NULL,           offsetof(UnitStatusInfo, load_state)                        },
                 { "ActiveState",                    "s",               NULL,           offsetof(UnitStatusInfo, active_state)                      },
+                { "FreezerState",                   "s",               NULL,           offsetof(UnitStatusInfo, freezer_state)                     },
                 { "Documentation",                  "as",              NULL,           offsetof(UnitStatusInfo, documentation)                     },
                 {}
         }, status_map[] = {
                 { "Id",                             "s",               NULL,           offsetof(UnitStatusInfo, id)                                },
                 { "LoadState",                      "s",               NULL,           offsetof(UnitStatusInfo, load_state)                        },
                 { "ActiveState",                    "s",               NULL,           offsetof(UnitStatusInfo, active_state)                      },
+                { "FreezerState",                   "s",               NULL,           offsetof(UnitStatusInfo, freezer_state)                     },
                 { "SubState",                       "s",               NULL,           offsetof(UnitStatusInfo, sub_state)                         },
                 { "UnitFileState",                  "s",               NULL,           offsetof(UnitStatusInfo, unit_file_state)                   },
                 { "UnitFilePreset",                 "s",               NULL,           offsetof(UnitStatusInfo, unit_file_preset)                  },
@@ -5656,15 +5623,7 @@ static int get_unit_dbus_path_by_pid(
         char *u;
         int r;
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "GetUnitByPID",
-                        &error,
-                        &reply,
-                        "u", pid);
+        r = bus_call_method(bus, bus_systemd_mgr, "GetUnitByPID", &error, &reply, "u", pid);
         if (r < 0)
                 return log_error_errno(r, "Failed to get unit for PID %"PRIu32": %s", pid, bus_error_message(&error, r));
 
@@ -5993,13 +5952,7 @@ static int set_property(int argc, char *argv[], void *userdata) {
 
         polkit_agent_open_maybe();
 
-        r = sd_bus_message_new_method_call(
-                        bus,
-                        &m,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "SetUnitProperties");
+        r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "SetUnitProperties");
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -6066,13 +6019,7 @@ static int daemon_reload(int argc, char *argv[], void *userdata) {
                 assert_not_reached("Unexpected action");
         }
 
-        r = sd_bus_message_new_method_call(
-                        bus,
-                        &m,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        method);
+        r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, method);
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -6120,15 +6067,7 @@ static int trivial_method(int argc, char *argv[], void *userdata) {
                 streq(argv[0], "exit")          ? "Exit" :
                              /* poweroff */       "PowerOff";
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        method,
-                        &error,
-                        NULL,
-                        NULL);
+        r = bus_call_method(bus, bus_systemd_mgr, method, &error, NULL, NULL);
         if (r < 0 && arg_action == ACTION_SYSTEMCTL)
                 return log_error_errno(r, "Failed to execute operation: %s", bus_error_message(&error, r));
 
@@ -6160,15 +6099,7 @@ static int reset_failed(int argc, char *argv[], void *userdata) {
         STRV_FOREACH(name, names) {
                 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
 
-                q = sd_bus_call_method(
-                                bus,
-                                "org.freedesktop.systemd1",
-                                "/org/freedesktop/systemd1",
-                                "org.freedesktop.systemd1.Manager",
-                                "ResetFailedUnit",
-                                &error,
-                                NULL,
-                                "s", *name);
+                q = bus_call_method(bus, bus_systemd_mgr, "ResetFailedUnit", &error, NULL, "s", *name);
                 if (q < 0) {
                         log_error_errno(q, "Failed to reset failed state of unit %s: %s", *name, bus_error_message(&error, q));
                         if (r == 0)
@@ -6209,15 +6140,7 @@ static int show_environment(int argc, char *argv[], void *userdata) {
 
         (void) pager_open(arg_pager_flags);
 
-        r = sd_bus_get_property(
-                        bus,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "Environment",
-                        &error,
-                        &reply,
-                        "as");
+        r = bus_get_property(bus, bus_systemd_mgr, "Environment", &error, &reply, "as");
         if (r < 0)
                 return log_error_errno(r, "Failed to get environment: %s", bus_error_message(&error, r));
 
@@ -6298,15 +6221,7 @@ static int switch_root(int argc, char *argv[], void *userdata) {
 
         log_debug("Switching root - root: %s; init: %s", root, strna(init));
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "SwitchRoot",
-                        &error,
-                        NULL,
-                        "ss", root, init);
+        r = bus_call_method(bus, bus_systemd_mgr, "SwitchRoot", &error, NULL, "ss", root, init);
         if (r < 0) {
                 (void) default_signals(SIGTERM, -1);
 
@@ -6328,14 +6243,7 @@ static int log_level(int argc, char *argv[], void *userdata) {
         if (argc == 1) {
                 _cleanup_free_ char *level = NULL;
 
-                r = sd_bus_get_property_string(
-                                bus,
-                                "org.freedesktop.systemd1",
-                                "/org/freedesktop/systemd1",
-                                "org.freedesktop.systemd1.Manager",
-                                "LogLevel",
-                                &error,
-                                &level);
+                r = bus_get_property_string(bus, bus_systemd_mgr, "LogLevel", &error, &level);
                 if (r < 0)
                         return log_error_errno(r, "Failed to get log level: %s", bus_error_message(&error, r));
 
@@ -6344,15 +6252,7 @@ static int log_level(int argc, char *argv[], void *userdata) {
         } else {
                 assert(argc == 2);
 
-                r = sd_bus_set_property(
-                                bus,
-                                "org.freedesktop.systemd1",
-                                "/org/freedesktop/systemd1",
-                                "org.freedesktop.systemd1.Manager",
-                                "LogLevel",
-                                &error,
-                                "s",
-                                argv[1]);
+                r = bus_set_property(bus, bus_systemd_mgr, "LogLevel", &error, "s", argv[1]);
                 if (r < 0)
                         return log_error_errno(r, "Failed to set log level: %s", bus_error_message(&error, r));
         }
@@ -6372,14 +6272,7 @@ static int log_target(int argc, char *argv[], void *userdata) {
         if (argc == 1) {
                 _cleanup_free_ char *target = NULL;
 
-                r = sd_bus_get_property_string(
-                                bus,
-                                "org.freedesktop.systemd1",
-                                "/org/freedesktop/systemd1",
-                                "org.freedesktop.systemd1.Manager",
-                                "LogTarget",
-                                &error,
-                                &target);
+                r = bus_get_property_string(bus, bus_systemd_mgr, "LogTarget", &error, &target);
                 if (r < 0)
                         return log_error_errno(r, "Failed to get log target: %s", bus_error_message(&error, r));
 
@@ -6388,15 +6281,7 @@ static int log_target(int argc, char *argv[], void *userdata) {
         } else {
                 assert(argc == 2);
 
-                r = sd_bus_set_property(
-                                bus,
-                                "org.freedesktop.systemd1",
-                                "/org/freedesktop/systemd1",
-                                "org.freedesktop.systemd1.Manager",
-                                "LogTarget",
-                                &error,
-                                "s",
-                                argv[1]);
+                r = bus_set_property(bus, bus_systemd_mgr, "LogTarget", &error, "s", argv[1]);
                 if (r < 0)
                         return log_error_errno(r, "Failed to set log target: %s", bus_error_message(&error, r));
         }
@@ -6417,15 +6302,7 @@ static int service_watchdogs(int argc, char *argv[], void *userdata) {
 
         if (argc == 1) {
                 /* get ServiceWatchdogs */
-                r = sd_bus_get_property_trivial(
-                                bus,
-                                "org.freedesktop.systemd1",
-                                "/org/freedesktop/systemd1",
-                                "org.freedesktop.systemd1.Manager",
-                                "ServiceWatchdogs",
-                                &error,
-                                'b',
-                                &b);
+                r = bus_get_property_trivial(bus, bus_systemd_mgr, "ServiceWatchdogs", &error, 'b', &b);
                 if (r < 0)
                         return log_error_errno(r, "Failed to get service-watchdog state: %s", bus_error_message(&error, r));
 
@@ -6439,15 +6316,7 @@ static int service_watchdogs(int argc, char *argv[], void *userdata) {
                 if (b < 0)
                         return log_error_errno(b, "Failed to parse service-watchdogs argument: %m");
 
-                r = sd_bus_set_property(
-                                bus,
-                                "org.freedesktop.systemd1",
-                                "/org/freedesktop/systemd1",
-                                "org.freedesktop.systemd1.Manager",
-                                "ServiceWatchdogs",
-                                &error,
-                                "b",
-                                b);
+                r = bus_set_property(bus, bus_systemd_mgr, "ServiceWatchdogs", &error, "b", b);
                 if (r < 0)
                         return log_error_errno(r, "Failed to set service-watchdog state: %s", bus_error_message(&error, r));
         }
@@ -6475,13 +6344,7 @@ static int set_environment(int argc, char *argv[], void *userdata) {
                 ? "SetEnvironment"
                 : "UnsetEnvironment";
 
-        r = sd_bus_message_new_method_call(
-                        bus,
-                        &m,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        method);
+        r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, method);
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -6508,13 +6371,7 @@ static int import_environment(int argc, char *argv[], void *userdata) {
 
         polkit_agent_open_maybe();
 
-        r = sd_bus_message_new_method_call(
-                        bus,
-                        &m,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "SetEnvironment");
+        r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "SetEnvironment");
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -6953,13 +6810,7 @@ static int enable_unit(int argc, char *argv[], void *userdata) {
                 } else
                         assert_not_reached("Unknown verb");
 
-                r = sd_bus_message_new_method_call(
-                                bus,
-                                &m,
-                                "org.freedesktop.systemd1",
-                                "/org/freedesktop/systemd1",
-                                "org.freedesktop.systemd1.Manager",
-                                method);
+                r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, method);
                 if (r < 0)
                         return bus_log_create_error(r);
 
@@ -7095,13 +6946,7 @@ static int add_dependency(int argc, char *argv[], void *userdata) {
 
                 polkit_agent_open_maybe();
 
-                r = sd_bus_message_new_method_call(
-                                bus,
-                                &m,
-                                "org.freedesktop.systemd1",
-                                "/org/freedesktop/systemd1",
-                                "org.freedesktop.systemd1.Manager",
-                                "AddDependencyUnitFiles");
+                r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "AddDependencyUnitFiles");
                 if (r < 0)
                         return bus_log_create_error(r);
 
@@ -7157,11 +7002,9 @@ static int preset_all(int argc, char *argv[], void *userdata) {
 
                 polkit_agent_open_maybe();
 
-                r = sd_bus_call_method(
+                r = bus_call_method(
                                 bus,
-                                "org.freedesktop.systemd1",
-                                "/org/freedesktop/systemd1",
-                                "org.freedesktop.systemd1.Manager",
+                                bus_systemd_mgr,
                                 "PresetAllUnitFiles",
                                 &error,
                                 &reply,
@@ -7218,15 +7061,7 @@ static int show_installation_targets(sd_bus *bus, const char *name) {
         const char *link;
         int r;
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "GetUnitFileLinks",
-                        &error,
-                        &reply,
-                        "sb", name, arg_runtime);
+        r = bus_call_method(bus, bus_systemd_mgr, "GetUnitFileLinks", &error, &reply, "sb", name, arg_runtime);
         if (r < 0)
                 return log_error_errno(r, "Failed to get unit file links for %s: %s", name, bus_error_message(&error, r));
 
@@ -7275,6 +7110,7 @@ static int unit_is_enabled(int argc, char *argv[], void *userdata) {
                                    UNIT_FILE_ENABLED,
                                    UNIT_FILE_ENABLED_RUNTIME,
                                    UNIT_FILE_STATIC,
+                                   UNIT_FILE_ALIAS,
                                    UNIT_FILE_INDIRECT,
                                    UNIT_FILE_GENERATED))
                                 enabled = true;
@@ -7302,15 +7138,7 @@ static int unit_is_enabled(int argc, char *argv[], void *userdata) {
                         _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
                         const char *s;
 
-                        r = sd_bus_call_method(
-                                        bus,
-                                        "org.freedesktop.systemd1",
-                                        "/org/freedesktop/systemd1",
-                                        "org.freedesktop.systemd1.Manager",
-                                        "GetUnitFileState",
-                                        &error,
-                                        &reply,
-                                        "s", *name);
+                        r = bus_call_method(bus, bus_systemd_mgr, "GetUnitFileState", &error, &reply, "s", *name);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to get unit file state for %s: %s", *name, bus_error_message(&error, r));
 
@@ -7341,14 +7169,7 @@ static int match_startup_finished(sd_bus_message *m, void *userdata, sd_bus_erro
 
         assert(state);
 
-        r = sd_bus_get_property_string(
-                        sd_bus_message_get_bus(m),
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "SystemState",
-                        NULL,
-                        state);
+        r = bus_get_property_string(sd_bus_message_get_bus(m), bus_systemd_mgr, "SystemState", NULL, state);
 
         sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), r);
         return 0;
@@ -7377,12 +7198,10 @@ static int is_system_running(int argc, char *argv[], void *userdata) {
                 if (r >= 0)
                         r = sd_bus_attach_event(bus, event, 0);
                 if (r >= 0)
-                        r = sd_bus_match_signal_async(
+                        r = bus_match_signal_async(
                                         bus,
                                         &slot_startup_finished,
-                                        "org.freedesktop.systemd1",
-                                        "/org/freedesktop/systemd1",
-                                        "org.freedesktop.systemd1.Manager",
+                                        bus_systemd_mgr,
                                         "StartupFinished",
                                         match_startup_finished, NULL, &state);
                 if (r < 0) {
@@ -7391,14 +7210,7 @@ static int is_system_running(int argc, char *argv[], void *userdata) {
                 }
         }
 
-        r = sd_bus_get_property_string(
-                        bus,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "SystemState",
-                        &error,
-                        &state);
+        r = bus_get_property_string(bus, bus_systemd_mgr, "SystemState", &error, &state);
         if (r < 0) {
                 log_warning_errno(r, "Failed to query system state: %s", bus_error_message(&error, r));
 
@@ -7827,6 +7639,8 @@ static int systemctl_help(void) {
                "  kill UNIT...                        Send signal to processes of a unit\n"
                "  clean UNIT...                       Clean runtime, cache, state, logs or\n"
                "                                      configuration of unit\n"
+               "  freeze PATTERN...                   Freeze execution of unit processes\n"
+               "  thaw PATTERN...                     Resume execution of a frozen unit\n"
                "  is-active PATTERN...                Check whether units are active\n"
                "  is-failed PATTERN...                Check whether units are failed\n"
                "  status [PATTERN...|PID...]          Show runtime status of one or more units\n"
@@ -7886,7 +7700,7 @@ static int systemctl_help(void) {
                "  emergency                           Enter system emergency mode\n"
                "  halt                                Shut down and halt the system\n"
                "  poweroff                            Shut down and power-off the system\n"
-               "  reboot [ARG]                        Shut down and reboot the system\n"
+               "  reboot                              Shut down and reboot the system\n"
                "  kexec                               Shut down and reboot the system with kexec\n"
                "  exit [EXIT_CODE]                    Request user instance or container exit\n"
                "  switch-root ROOT [INIT]             Change to a different root file system\n"
@@ -7906,6 +7720,7 @@ static int systemctl_help(void) {
                "     --state=STATE       List units with particular LOAD or SUB or ACTIVE state\n"
                "     --failed            Shorcut for --state=failed\n"
                "  -p --property=NAME     Show only properties by this name\n"
+               "  -P NAME                Equivalent to --value --property=NAME\n"
                "  -a --all               Show all properties/all units currently in memory,\n"
                "                         including dead/empty ones. To list all units installed\n"
                "                         on the system, use 'list-unit-files' instead.\n"
@@ -8153,14 +7968,7 @@ static int help_boot_loader_entry(void) {
         if (r < 0)
                 return r;
 
-        r = sd_bus_get_property_strv(
-                        bus,
-                        "org.freedesktop.login1",
-                        "/org/freedesktop/login1",
-                        "org.freedesktop.login1.Manager",
-                        "BootLoaderEntries",
-                        &error,
-                        &l);
+        r = bus_get_property_strv(bus, bus_login_mgr, "BootLoaderEntries", &error, &l);
         if (r < 0)
                 return log_error_errno(r, "Failed to enumerate boot loader entries: %s", bus_error_message(&error, r));
 
@@ -8210,6 +8018,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                 ARG_WITH_DEPENDENCIES,
                 ARG_WAIT,
                 ARG_WHAT,
+                ARG_REBOOT_ARG,
         };
 
         static const struct option options[] = {
@@ -8263,6 +8072,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                 { "message",             required_argument, NULL, ARG_MESSAGE             },
                 { "show-transaction",    no_argument,       NULL, 'T'                     },
                 { "what",                required_argument, NULL, ARG_WHAT                },
+                { "reboot-argument",     required_argument, NULL, ARG_REBOOT_ARG          },
                 {}
         };
 
@@ -8274,7 +8084,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
         /* we default to allowing interactive authorization only in systemctl (not in the legacy commands) */
         arg_ask_password = true;
 
-        while ((c = getopt_long(argc, argv, "ht:p:alqfs:H:M:n:o:iTr.::", options, NULL)) >= 0)
+        while ((c = getopt_long(argc, argv, "ht:p:P:alqfs:H:M:n:o:iTr.::", options, NULL)) >= 0)
 
                 switch (c) {
 
@@ -8329,6 +8139,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                         break;
                 }
 
+                case 'P':
+                        arg_value = true;
+                        _fallthrough_;
+
                 case 'p':
                         /* Make sure that if the empty property list was specified, we won't show any
                            properties. */
@@ -8353,9 +8167,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                                 }
                         }
 
-                        /* If the user asked for a particular
-                         * property, show it to him, even if it is
-                         * empty. */
+                        /* If the user asked for a particular property, show it, even if it is empty. */
                         arg_all = true;
 
                         break;
@@ -8519,6 +8331,11 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                                        "Unknown output '%s'.",
                                                        optarg);
+
+                        if (OUTPUT_MODE_IS_JSON(arg_output)) {
+                                arg_no_legend = true;
+                                arg_plain = true;
+                        }
                         break;
 
                 case 'i':
@@ -8651,6 +8468,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                         break;
                 }
 
+                case ARG_REBOOT_ARG:
+                        arg_reboot_argument = optarg;
+                        break;
+
                 case '.':
                         /* Output an error mimicking getopt, and print a hint afterwards */
                         log_error("%s: invalid option -- '.'", program_invocation_name);
@@ -9097,7 +8918,9 @@ static int systemctl_main(int argc, char *argv[]) {
                 { "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               },
-                { "clean",                 2,        VERB_ANY, VERB_ONLINE_ONLY, clean_unit              },
+                { "clean",                 2,        VERB_ANY, VERB_ONLINE_ONLY, clean_or_freeze_unit    },
+                { "freeze",                2,        VERB_ANY, VERB_ONLINE_ONLY, clean_or_freeze_unit    },
+                { "thaw",                  2,        VERB_ANY, VERB_ONLINE_ONLY, clean_or_freeze_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       },
@@ -9107,9 +8930,9 @@ static int systemctl_main(int argc, char *argv[]) {
                 { "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           },
-                { "log-level",             VERB_ANY, 2,        0,                log_level               },
-                { "log-target",            VERB_ANY, 2,        0,                log_target              },
-                { "service-watchdogs",     VERB_ANY, 2,        0,                service_watchdogs       },
+                { "log-level",             VERB_ANY, 2,        VERB_ONLINE_ONLY, log_level               },
+                { "log-target",            VERB_ANY, 2,        VERB_ONLINE_ONLY, log_target              },
+                { "service-watchdogs",     VERB_ANY, 2,        VERB_ONLINE_ONLY, service_watchdogs       },
                 { "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         },
@@ -9149,6 +8972,12 @@ static int systemctl_main(int argc, char *argv[]) {
                 {}
         };
 
+        const Verb *verb = verbs_find_verb(argv[optind], verbs);
+        if (verb && (verb->flags & VERB_ONLINE_ONLY) && arg_root)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "Verb '%s' cannot be used with --root=.",
+                                       argv[optind] ?: verb->verb);
+
         return dispatch_verb(argc, argv, verbs, NULL);
 }
 
@@ -9263,17 +9092,7 @@ static int logind_schedule_shutdown(void) {
 
         (void) logind_set_wall_message();
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.login1",
-                        "/org/freedesktop/login1",
-                        "org.freedesktop.login1.Manager",
-                        "ScheduleShutdown",
-                        &error,
-                        NULL,
-                        "st",
-                        action,
-                        arg_when);
+        r = bus_call_method(bus, bus_login_mgr, "ScheduleShutdown", &error, NULL, "st", action, arg_when);
         if (r < 0)
                 return log_warning_errno(r, "Failed to call ScheduleShutdown in logind, proceeding with immediate shutdown: %s", bus_error_message(&error, r));
 
@@ -9375,14 +9194,7 @@ static int logind_cancel_shutdown(void) {
 
         (void) logind_set_wall_message();
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.login1",
-                        "/org/freedesktop/login1",
-                        "org.freedesktop.login1.Manager",
-                        "CancelScheduledShutdown",
-                        &error,
-                        NULL, NULL);
+        r = bus_call_method(bus, bus_login_mgr, "CancelScheduledShutdown", &error, NULL, NULL);
         if (r < 0)
                 return log_warning_errno(r, "Failed to talk to logind, shutdown hasn't been cancelled: %s", bus_error_message(&error, r));
 
@@ -9397,7 +9209,7 @@ static int run(int argc, char *argv[]) {
         int r;
 
         setlocale(LC_ALL, "");
-        log_parse_environment();
+        log_parse_environment_cli();
         log_open();
 
         /* The journal merging logic potentially needs a lot of fds. */
index 05196554ac0a11e5c2fa24973da2439ae2f0c8a5..62baf7784e492f90c8baea9bcef1d50395a8140c 100644 (file)
@@ -12,6 +12,7 @@ _systemd_headers = '''
         sd-journal.h
         sd-login.h
         sd-messages.h
+        sd-path.h
 '''.split()
 
 # https://github.com/mesonbuild/meson/issues/1633
@@ -23,6 +24,7 @@ _not_installed_headers = '''
         sd-dhcp-client.h
         sd-dhcp-lease.h
         sd-dhcp-option.h
+        sd-dhcp6-option.h
         sd-dhcp-server.h
         sd-ipv4acd.h
         sd-ipv4ll.h
@@ -30,7 +32,6 @@ _not_installed_headers = '''
         sd-ndisc.h
         sd-netlink.h
         sd-network.h
-        sd-path.h
         sd-radv.h
         sd-resolve.h
         sd-utf8.h
index 95b5914236a478b6eeb7028b4c2002dc4c71b65d..b10a3e04bc3490cb7d50987126433f1fb095138c 100644 (file)
@@ -44,6 +44,7 @@ enum {
         SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION  = 1ULL << 6,
         SD_BUS_VTABLE_PROPERTY_EXPLICIT            = 1ULL << 7,
         SD_BUS_VTABLE_SENSITIVE                    = 1ULL << 8, /* covers both directions: method call + reply */
+        SD_BUS_VTABLE_ABSOLUTE_OFFSET              = 1ULL << 9,
         _SD_BUS_VTABLE_CAPABILITY_MASK             = 0xFFFFULL << 40
 };
 
@@ -187,6 +188,124 @@ struct sd_bus_vtable {
                 .x = { { 0 } },                                         \
         }
 
+#define _SD_ECHO(X) X
+#define _SD_CONCAT(X) #X "\0"
+
+#define _SD_VARARGS_FOREACH_EVEN_01(FN, X, ...)
+#define _SD_VARARGS_FOREACH_EVEN_02(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_01(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_03(FN, X, ...)       _SD_VARARGS_FOREACH_EVEN_02(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_04(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_03(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_05(FN, X, ...)       _SD_VARARGS_FOREACH_EVEN_04(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_06(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_05(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_07(FN, X, ...)       _SD_VARARGS_FOREACH_EVEN_06(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_08(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_07(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_09(FN, X, ...)       _SD_VARARGS_FOREACH_EVEN_08(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_10(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_09(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_11(FN, X, ...)       _SD_VARARGS_FOREACH_EVEN_10(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_12(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_11(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_13(FN, X, ...)       _SD_VARARGS_FOREACH_EVEN_12(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_14(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_13(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_15(FN, X, ...)       _SD_VARARGS_FOREACH_EVEN_14(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_16(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_15(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_17(FN, X, ...)       _SD_VARARGS_FOREACH_EVEN_16(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_18(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_17(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_19(FN, X, ...)       _SD_VARARGS_FOREACH_EVEN_18(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_20(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_19(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_21(FN, X, ...)       _SD_VARARGS_FOREACH_EVEN_20(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_22(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_21(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_23(FN, X, ...)       _SD_VARARGS_FOREACH_EVEN_22(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_24(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_23(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_25(FN, X, ...)       _SD_VARARGS_FOREACH_EVEN_24(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_26(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_25(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_27(FN, X, ...)       _SD_VARARGS_FOREACH_EVEN_26(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_28(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_27(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_29(FN, X, ...)       _SD_VARARGS_FOREACH_EVEN_28(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_30(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_29(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_31(FN, X, ...)       _SD_VARARGS_FOREACH_EVEN_30(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_32(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_31(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_33(FN, X, ...)       _SD_VARARGS_FOREACH_EVEN_32(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_34(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_33(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_35(FN, X, ...)       _SD_VARARGS_FOREACH_EVEN_34(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_36(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_35(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_37(FN, X, ...)       _SD_VARARGS_FOREACH_EVEN_36(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_38(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_37(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_39(FN, X, ...)       _SD_VARARGS_FOREACH_EVEN_38(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_40(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_39(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_41(FN, X, ...)       _SD_VARARGS_FOREACH_EVEN_40(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_42(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_41(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_43(FN, X, ...)       _SD_VARARGS_FOREACH_EVEN_42(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_44(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_43(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_45(FN, X, ...)       _SD_VARARGS_FOREACH_EVEN_44(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_46(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_45(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_47(FN, X, ...)       _SD_VARARGS_FOREACH_EVEN_46(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_48(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_47(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_49(FN, X, ...)       _SD_VARARGS_FOREACH_EVEN_48(FN, __VA_ARGS__)
+#define _SD_VARARGS_FOREACH_EVEN_50(FN, X, ...) FN(X) _SD_VARARGS_FOREACH_EVEN_49(FN, __VA_ARGS__)
+
+#define _SD_VARARGS_FOREACH_EVEN_SEQ(_01, _02, _03, _04, _05, _06, _07, _08, _09, _10, \
+                                     _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, \
+                                     _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, \
+                                     _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, \
+                                     _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, \
+                                     NAME, ...) NAME
+
+#define _SD_VARARGS_FOREACH_EVEN(FN, ...)                                                      \
+        _SD_VARARGS_FOREACH_EVEN_SEQ(__VA_ARGS__,                                              \
+                                     _SD_VARARGS_FOREACH_EVEN_50, _SD_VARARGS_FOREACH_EVEN_49, \
+                                     _SD_VARARGS_FOREACH_EVEN_48, _SD_VARARGS_FOREACH_EVEN_47, \
+                                     _SD_VARARGS_FOREACH_EVEN_46, _SD_VARARGS_FOREACH_EVEN_45, \
+                                     _SD_VARARGS_FOREACH_EVEN_44, _SD_VARARGS_FOREACH_EVEN_43, \
+                                     _SD_VARARGS_FOREACH_EVEN_42, _SD_VARARGS_FOREACH_EVEN_41, \
+                                     _SD_VARARGS_FOREACH_EVEN_40, _SD_VARARGS_FOREACH_EVEN_39, \
+                                     _SD_VARARGS_FOREACH_EVEN_38, _SD_VARARGS_FOREACH_EVEN_37, \
+                                     _SD_VARARGS_FOREACH_EVEN_36, _SD_VARARGS_FOREACH_EVEN_35, \
+                                     _SD_VARARGS_FOREACH_EVEN_34, _SD_VARARGS_FOREACH_EVEN_33, \
+                                     _SD_VARARGS_FOREACH_EVEN_32, _SD_VARARGS_FOREACH_EVEN_31, \
+                                     _SD_VARARGS_FOREACH_EVEN_30, _SD_VARARGS_FOREACH_EVEN_29, \
+                                     _SD_VARARGS_FOREACH_EVEN_28, _SD_VARARGS_FOREACH_EVEN_27, \
+                                     _SD_VARARGS_FOREACH_EVEN_26, _SD_VARARGS_FOREACH_EVEN_25, \
+                                     _SD_VARARGS_FOREACH_EVEN_24, _SD_VARARGS_FOREACH_EVEN_23, \
+                                     _SD_VARARGS_FOREACH_EVEN_22, _SD_VARARGS_FOREACH_EVEN_21, \
+                                     _SD_VARARGS_FOREACH_EVEN_20, _SD_VARARGS_FOREACH_EVEN_19, \
+                                     _SD_VARARGS_FOREACH_EVEN_18, _SD_VARARGS_FOREACH_EVEN_17, \
+                                     _SD_VARARGS_FOREACH_EVEN_16, _SD_VARARGS_FOREACH_EVEN_15, \
+                                     _SD_VARARGS_FOREACH_EVEN_14, _SD_VARARGS_FOREACH_EVEN_13, \
+                                     _SD_VARARGS_FOREACH_EVEN_12, _SD_VARARGS_FOREACH_EVEN_11, \
+                                     _SD_VARARGS_FOREACH_EVEN_10, _SD_VARARGS_FOREACH_EVEN_09, \
+                                     _SD_VARARGS_FOREACH_EVEN_08, _SD_VARARGS_FOREACH_EVEN_07, \
+                                     _SD_VARARGS_FOREACH_EVEN_06, _SD_VARARGS_FOREACH_EVEN_05, \
+                                     _SD_VARARGS_FOREACH_EVEN_04, _SD_VARARGS_FOREACH_EVEN_03, \
+                                     _SD_VARARGS_FOREACH_EVEN_02, _SD_VARARGS_FOREACH_EVEN_01) \
+                                     (FN, __VA_ARGS__)
+
+#define SD_BUS_ARGS(...) __VA_ARGS__
+#define SD_BUS_RESULT(...) __VA_ARGS__
+
+#define SD_BUS_NO_ARGS SD_BUS_ARGS(NULL,)
+#define SD_BUS_NO_RESULT SD_BUS_RESULT(NULL,)
+
+#define SD_BUS_METHOD_WITH_ARGS(_member, _args, _result, _handler, _flags)                 \
+        SD_BUS_METHOD_WITH_NAMES(_member,                                                  \
+                                _SD_VARARGS_FOREACH_EVEN(_SD_ECHO, _args),                 \
+                                _SD_VARARGS_FOREACH_EVEN(_SD_CONCAT, _args, ""),           \
+                                _SD_VARARGS_FOREACH_EVEN(_SD_ECHO, _result),               \
+                                _SD_VARARGS_FOREACH_EVEN(_SD_CONCAT, _result, ""),         \
+                                _handler, _flags)
+
+#define SD_BUS_METHOD_WITH_ARGS_OFFSET(_member, _args, _result, _handler, _offset, _flags) \
+        SD_BUS_METHOD_WITH_NAMES_OFFSET(_member,                                           \
+                                        _SD_VARARGS_FOREACH_EVEN(_SD_ECHO, _args),         \
+                                        _SD_VARARGS_FOREACH_EVEN(_SD_CONCAT, _args, ""),   \
+                                        _SD_VARARGS_FOREACH_EVEN(_SD_ECHO, _result),       \
+                                        _SD_VARARGS_FOREACH_EVEN(_SD_CONCAT, _result, ""), \
+                                        _handler, _offset, _flags)
+
+#define SD_BUS_SIGNAL_WITH_ARGS(_member, _args, _flags)                                    \
+        SD_BUS_SIGNAL_WITH_NAMES(_member,                                                  \
+                                 _SD_VARARGS_FOREACH_EVEN(_SD_ECHO, _args),                \
+                                 _SD_VARARGS_FOREACH_EVEN(_SD_CONCAT, _args, ""),          \
+                                 _flags)
+
 _SD_END_DECLARATIONS;
 
 #endif
index e6f3298745722e28e6399f4a9f26fdec829d213b..d4b6befc8cb941f70343c44310aa00234fe85799 100644 (file)
@@ -55,7 +55,7 @@ typedef struct {
 } sd_bus_error;
 
 typedef struct {
-        const charname;
+        const char *name;
         int code;
 } sd_bus_error_map;
 
@@ -124,6 +124,13 @@ typedef _sd_destroy_t sd_bus_destroy_t;
 #include "sd-bus-protocol.h"
 #include "sd-bus-vtable.h"
 
+/* Naming */
+
+int sd_bus_interface_name_is_valid(const char *p);
+int sd_bus_service_name_is_valid(const char *p);
+int sd_bus_member_name_is_valid(const char *p);
+int sd_bus_object_path_is_valid(const char *p);
+
 /* Connections */
 
 int sd_bus_default(sd_bus **ret);
@@ -143,7 +150,7 @@ int sd_bus_new(sd_bus **ret);
 
 int sd_bus_set_address(sd_bus *bus, const char *address);
 int sd_bus_set_fd(sd_bus *bus, int input_fd, int output_fd);
-int sd_bus_set_exec(sd_bus *bus, const char *path, char *const argv[]);
+int sd_bus_set_exec(sd_bus *bus, const char *path, char *const *argv);
 int sd_bus_get_address(sd_bus *bus, const char **address);
 int sd_bus_set_bus_client(sd_bus *bus, int b);
 int sd_bus_is_bus_client(sd_bus *bus);
@@ -177,13 +184,13 @@ int sd_bus_get_sender(sd_bus *bus, const char **ret);
 
 int sd_bus_start(sd_bus *bus);
 
-int sd_bus_try_close(sd_bus *bus);
+int sd_bus_try_close(sd_bus *bus) _sd_deprecated_;
 void sd_bus_close(sd_bus *bus);
 
-sd_bus *sd_bus_ref(sd_bus *bus);
-sd_bus *sd_bus_unref(sd_bus *bus);
-sd_bus *sd_bus_close_unref(sd_bus *bus);
-sd_bus *sd_bus_flush_close_unref(sd_bus *bus);
+sd_bussd_bus_ref(sd_bus *bus);
+sd_bussd_bus_unref(sd_bus *bus);
+sd_bussd_bus_close_unref(sd_bus *bus);
+sd_bussd_bus_flush_close_unref(sd_bus *bus);
 
 void sd_bus_default_flush_close(void);
 
@@ -204,7 +211,7 @@ int sd_bus_get_fd(sd_bus *bus);
 int sd_bus_get_events(sd_bus *bus);
 int sd_bus_get_timeout(sd_bus *bus, uint64_t *timeout_usec);
 int sd_bus_process(sd_bus *bus, sd_bus_message **r);
-int sd_bus_process_priority(sd_bus *bus, int64_t max_priority, sd_bus_message **r);
+int sd_bus_process_priority(sd_bus *bus, int64_t max_priority, sd_bus_message **r) _sd_deprecated_;
 int sd_bus_wait(sd_bus *bus, uint64_t timeout_usec);
 int sd_bus_flush(sd_bus *bus);
 int sd_bus_enqueue_for_read(sd_bus *bus, sd_bus_message *m);
@@ -216,7 +223,7 @@ void* sd_bus_get_current_userdata(sd_bus *bus);
 
 int sd_bus_attach_event(sd_bus *bus, sd_event *e, int priority);
 int sd_bus_detach_event(sd_bus *bus);
-sd_event *sd_bus_get_event(sd_bus *bus);
+sd_eventsd_bus_get_event(sd_bus *bus);
 
 int sd_bus_get_n_queued_read(sd_bus *bus, uint64_t *ret);
 int sd_bus_get_n_queued_write(sd_bus *bus, uint64_t *ret);
@@ -240,8 +247,8 @@ sd_bus_slot* sd_bus_slot_ref(sd_bus_slot *slot);
 sd_bus_slot* sd_bus_slot_unref(sd_bus_slot *slot);
 
 sd_bus* sd_bus_slot_get_bus(sd_bus_slot *slot);
-void *sd_bus_slot_get_userdata(sd_bus_slot *slot);
-void *sd_bus_slot_set_userdata(sd_bus_slot *slot, void *userdata);
+voidsd_bus_slot_get_userdata(sd_bus_slot *slot);
+voidsd_bus_slot_set_userdata(sd_bus_slot *slot, void *userdata);
 int sd_bus_slot_set_description(sd_bus_slot *slot, const char *description);
 int sd_bus_slot_get_description(sd_bus_slot *slot, const char **description);
 int sd_bus_slot_get_floating(sd_bus_slot *slot);
@@ -251,7 +258,7 @@ int sd_bus_slot_get_destroy_callback(sd_bus_slot *s, sd_bus_destroy_t *callback)
 
 sd_bus_message* sd_bus_slot_get_current_message(sd_bus_slot *slot);
 sd_bus_message_handler_t sd_bus_slot_get_current_handler(sd_bus_slot *slot);
-void *sd_bus_slot_get_current_userdata(sd_bus_slot *slot);
+voidsd_bus_slot_get_current_userdata(sd_bus_slot *slot);
 
 /* Message object */
 
@@ -272,27 +279,27 @@ int sd_bus_message_seal(sd_bus_message *m, uint64_t cookie, uint64_t timeout_use
 int sd_bus_message_get_type(sd_bus_message *m, uint8_t *type);
 int sd_bus_message_get_cookie(sd_bus_message *m, uint64_t *cookie);
 int sd_bus_message_get_reply_cookie(sd_bus_message *m, uint64_t *cookie);
-int sd_bus_message_get_priority(sd_bus_message *m, int64_t *priority);
+int sd_bus_message_get_priority(sd_bus_message *m, int64_t *priority) _sd_deprecated_;
 
 int sd_bus_message_get_expect_reply(sd_bus_message *m);
 int sd_bus_message_get_auto_start(sd_bus_message *m);
 int sd_bus_message_get_allow_interactive_authorization(sd_bus_message *m);
 
-const char *sd_bus_message_get_signature(sd_bus_message *m, int complete);
-const char *sd_bus_message_get_path(sd_bus_message *m);
-const char *sd_bus_message_get_interface(sd_bus_message *m);
-const char *sd_bus_message_get_member(sd_bus_message *m);
-const char *sd_bus_message_get_destination(sd_bus_message *m);
-const char *sd_bus_message_get_sender(sd_bus_message *m);
-const sd_bus_error *sd_bus_message_get_error(sd_bus_message *m);
+const charsd_bus_message_get_signature(sd_bus_message *m, int complete);
+const charsd_bus_message_get_path(sd_bus_message *m);
+const charsd_bus_message_get_interface(sd_bus_message *m);
+const charsd_bus_message_get_member(sd_bus_message *m);
+const charsd_bus_message_get_destination(sd_bus_message *m);
+const charsd_bus_message_get_sender(sd_bus_message *m);
+const sd_bus_errorsd_bus_message_get_error(sd_bus_message *m);
 int sd_bus_message_get_errno(sd_bus_message *m);
 
 int sd_bus_message_get_monotonic_usec(sd_bus_message *m, uint64_t *usec);
 int sd_bus_message_get_realtime_usec(sd_bus_message *m, uint64_t *usec);
-int sd_bus_message_get_seqnum(sd_bus_message *m, uint64_tseqnum);
+int sd_bus_message_get_seqnum(sd_bus_message *m, uint64_t *seqnum);
 
 sd_bus* sd_bus_message_get_bus(sd_bus_message *m);
-sd_bus_creds *sd_bus_message_get_creds(sd_bus_message *m); /* do not unref the result */
+sd_bus_credssd_bus_message_get_creds(sd_bus_message *m); /* do not unref the result */
 
 int sd_bus_message_is_signal(sd_bus_message *m, const char *interface, const char *member);
 int sd_bus_message_is_method_call(sd_bus_message *m, const char *interface, const char *member);
@@ -306,7 +313,7 @@ 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_set_priority(sd_bus_message *m, int64_t priority) _sd_deprecated_;
 
 int sd_bus_message_append(sd_bus_message *m, const char *types, ...);
 int sd_bus_message_appendv(sd_bus_message *m, const char *types, va_list ap);
@@ -352,20 +359,27 @@ int sd_bus_get_name_machine_id(sd_bus *bus, const char *name, sd_id128_t *machin
 
 /* Convenience calls */
 
+int sd_bus_call_methodv(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, sd_bus_message **reply, const char *types, va_list ap);
 int sd_bus_call_method(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, sd_bus_message **reply, const char *types, ...);
+int sd_bus_call_method_asyncv(sd_bus *bus, sd_bus_slot **slot, const char *destination, const char *path, const char *interface, const char *member, sd_bus_message_handler_t callback, void *userdata, const char *types, va_list ap);
 int sd_bus_call_method_async(sd_bus *bus, sd_bus_slot **slot, const char *destination, const char *path, const char *interface, const char *member, sd_bus_message_handler_t callback, void *userdata, const char *types, ...);
 int sd_bus_get_property(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, sd_bus_message **reply, const char *type);
 int sd_bus_get_property_trivial(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, char type, void *ret_ptr);
 int sd_bus_get_property_string(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, char **ret); /* free the result! */
 int sd_bus_get_property_strv(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, char ***ret); /* free the result! */
+int sd_bus_set_propertyv(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, const char *type, va_list ap);
 int sd_bus_set_property(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, const char *type, ...);
 
+int sd_bus_reply_method_returnv(sd_bus_message *call, const char *types, va_list ap);
 int sd_bus_reply_method_return(sd_bus_message *call, const char *types, ...);
 int sd_bus_reply_method_error(sd_bus_message *call, const sd_bus_error *e);
+int sd_bus_reply_method_errorfv(sd_bus_message *call, const char *name, const char *format, va_list ap) _sd_printf_(3, 0);
 int sd_bus_reply_method_errorf(sd_bus_message *call, const char *name, const char *format, ...) _sd_printf_(3, 4);
 int sd_bus_reply_method_errno(sd_bus_message *call, int error, const sd_bus_error *e);
+int sd_bus_reply_method_errnofv(sd_bus_message *call, int error, const char *format, va_list ap) _sd_printf_(3, 0);
 int sd_bus_reply_method_errnof(sd_bus_message *call, int error, const char *format, ...) _sd_printf_(3, 4);
 
+int sd_bus_emit_signalv(sd_bus *bus, const char *path, const char *interface, const char *member, const char *types, va_list ap);
 int sd_bus_emit_signal(sd_bus *bus, const char *path, const char *interface, const char *member, const char *types, ...);
 
 int sd_bus_emit_properties_changed_strv(sd_bus *bus, const char *path, const char *interface, char **names);
@@ -378,8 +392,8 @@ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *inte
 int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces);
 int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) _sd_sentinel_;
 
-int sd_bus_query_sender_creds(sd_bus_message *call, uint64_t mask, sd_bus_creds **creds);
-int sd_bus_query_sender_privilege(sd_bus_message *call, int capability);
+int sd_bus_query_sender_creds(sd_bus_message *m, uint64_t mask, sd_bus_creds **creds);
+int sd_bus_query_sender_privilege(sd_bus_message *m, 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);
@@ -387,8 +401,8 @@ int sd_bus_match_signal_async(sd_bus *bus, sd_bus_slot **ret, const char *sender
 /* Credential handling */
 
 int sd_bus_creds_new_from_pid(sd_bus_creds **ret, pid_t pid, uint64_t creds_mask);
-sd_bus_creds *sd_bus_creds_ref(sd_bus_creds *c);
-sd_bus_creds *sd_bus_creds_unref(sd_bus_creds *c);
+sd_bus_credssd_bus_creds_ref(sd_bus_creds *c);
+sd_bus_credssd_bus_creds_unref(sd_bus_creds *c);
 uint64_t sd_bus_creds_get_mask(const sd_bus_creds *c);
 uint64_t sd_bus_creds_get_augmented_mask(const sd_bus_creds *c);
 
@@ -486,8 +500,8 @@ sd_bus_track* sd_bus_track_ref(sd_bus_track *track);
 sd_bus_track* sd_bus_track_unref(sd_bus_track *track);
 
 sd_bus* sd_bus_track_get_bus(sd_bus_track *track);
-void *sd_bus_track_get_userdata(sd_bus_track *track);
-void *sd_bus_track_set_userdata(sd_bus_track *track, void *userdata);
+voidsd_bus_track_get_userdata(sd_bus_track *track);
+voidsd_bus_track_set_userdata(sd_bus_track *track, void *userdata);
 
 int sd_bus_track_add_sender(sd_bus_track *track, sd_bus_message *m);
 int sd_bus_track_remove_sender(sd_bus_track *track, sd_bus_message *m);
index 62b0f723c7a1a316c0d496822c5b29608645e87a..b47b15a445a9693aa7238b9eebedf4861ef7c4b5 100644 (file)
@@ -286,6 +286,19 @@ int sd_pid_notifyf(pid_t pid, int unset_environment, const char *format, ...) _s
 */
 int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char *state, const int *fds, unsigned n_fds);
 
+/*
+  Returns > 0 if synchronization with systemd succeeded.  Returns < 0
+  on error. Returns 0 if $NOTIFY_SOCKET was not set. Note that the
+  timeout parameter of this function call takes the timeout in µs, and
+  will be passed to ppoll(2), hence the behaviour will be similar to
+  ppoll(2). This function can be called after sending a status message
+  to systemd, if one needs to synchronize against reception of the
+  status messages sent before this call is made. Therefore, this
+  cannot be used to know if the status message was processed
+  successfully, but to only synchronize against its consumption.
+*/
+int sd_notify_barrier(int unset_environment, uint64_t timeout);
+
 /*
   Returns > 0 if the system was booted with systemd. Returns < 0 on
   error. Returns 0 if the system was not booted with systemd. Note
index 0002ea0e5952d658eae3da8e259f234fcdf61b16..ac3b5b369c11319b3224c1a98cc9a872c58dec84 100644 (file)
@@ -48,6 +48,7 @@ enum {
         SD_DHCP_OPTION_TIME_OFFSET                 = 2,
         SD_DHCP_OPTION_ROUTER                      = 3,
         SD_DHCP_OPTION_DOMAIN_NAME_SERVER          = 6,
+        SD_DHCP_OPTION_LPR_SERVER                  = 9,
         SD_DHCP_OPTION_HOST_NAME                   = 12,
         SD_DHCP_OPTION_BOOT_FILE_SIZE              = 13,
         SD_DHCP_OPTION_DOMAIN_NAME                 = 15,
@@ -83,6 +84,8 @@ enum {
         SD_DHCP_OPTION_REBINDING_T2_TIME           = 59,
         SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER     = 60,
         SD_DHCP_OPTION_CLIENT_IDENTIFIER           = 61,
+        SD_DHCP_OPTION_SMTP_SERVER                 = 69,
+        SD_DHCP_OPTION_POP3_SERVER                 = 70,
         SD_DHCP_OPTION_USER_CLASS                  = 77,
         SD_DHCP_OPTION_FQDN                        = 81,
         SD_DHCP_OPTION_NEW_POSIX_TIMEZONE          = 100,
@@ -90,6 +93,7 @@ enum {
         SD_DHCP_OPTION_DOMAIN_SEARCH_LIST          = 119,
         SD_DHCP_OPTION_SIP_SERVER                  = 120,
         SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE      = 121,
+        SD_DHCP_OPTION_MUD_URL                     = 161,
         SD_DHCP_OPTION_PRIVATE_BASE                = 224,
        /* Windows 10 option to send when Anonymize=true */
         SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE = 249,
@@ -169,6 +173,9 @@ int sd_dhcp_client_set_hostname(
 int sd_dhcp_client_set_vendor_class_identifier(
                 sd_dhcp_client *client,
                 const char *vci);
+int sd_dhcp_client_set_mud_url(
+                sd_dhcp_client *client,
+                const char *mudurl);
 int sd_dhcp_client_set_user_class(
                 sd_dhcp_client *client,
                 const char* const *user_class);
@@ -178,8 +185,12 @@ int sd_dhcp_client_get_lease(
 int sd_dhcp_client_set_service_type(
                 sd_dhcp_client *client,
                 int type);
+int sd_dhcp_client_set_fallback_lease_lifetime(
+                sd_dhcp_client *client,
+                uint32_t fallback_lease_lifetime);
 
-int sd_dhcp_client_set_dhcp_option(sd_dhcp_client *client, sd_dhcp_option *v);
+int sd_dhcp_client_add_option(sd_dhcp_client *client, sd_dhcp_option *v);
+int sd_dhcp_client_add_vendor_option(sd_dhcp_client *client, sd_dhcp_option *v);
 
 int sd_dhcp_client_stop(sd_dhcp_client *client);
 int sd_dhcp_client_start(sd_dhcp_client *client);
@@ -194,6 +205,8 @@ sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client);
  * options when using RFC7844 Anonymity Profiles */
 int sd_dhcp_client_new(sd_dhcp_client **ret, int anonymize);
 
+int sd_dhcp_client_id_to_string(const void *data, size_t len, char **ret);
+
 int sd_dhcp_client_attach_event(
                 sd_dhcp_client *client,
                 sd_event *event,
index b80d607fea63dcd239ea7c7da8d1812b7d8c01de..17bd491819ff324d01f42eae1c80f62e3a238dfc 100644 (file)
@@ -33,6 +33,17 @@ typedef struct sd_dhcp_route sd_dhcp_route;
 sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease);
 sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease);
 
+typedef enum sd_dhcp_lease_server_type {
+        SD_DHCP_LEASE_DNS,
+        SD_DHCP_LEASE_NTP,
+        SD_DHCP_LEASE_SIP,
+        SD_DHCP_LEASE_POP3,
+        SD_DHCP_LEASE_SMTP,
+        SD_DHCP_LEASE_LPR,
+        _SD_DHCP_LEASE_SERVER_TYPE_MAX,
+        _SD_DHCP_LEASE_SERVER_TYPE_INVALID = -1,
+} sd_dhcp_lease_server_type;
+
 int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr);
 int sd_dhcp_lease_get_lifetime(sd_dhcp_lease *lease, uint32_t *lifetime);
 int sd_dhcp_lease_get_t1(sd_dhcp_lease *lease, uint32_t *t1);
@@ -42,9 +53,13 @@ int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr);
 int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, const struct in_addr **addr);
 int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr);
 int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *addr);
+int sd_dhcp_lease_get_servers(sd_dhcp_lease *lease, sd_dhcp_lease_server_type what, const struct in_addr **addr);
 int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr);
 int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, const struct in_addr **addr);
 int sd_dhcp_lease_get_sip(sd_dhcp_lease *lease, const struct in_addr **addr);
+int sd_dhcp_lease_get_pop3(sd_dhcp_lease *lease, const struct in_addr **addr);
+int sd_dhcp_lease_get_smtp(sd_dhcp_lease *lease, const struct in_addr **addr);
+int sd_dhcp_lease_get_lpr(sd_dhcp_lease *lease, const struct in_addr **addr);
 int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu);
 int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname);
 int sd_dhcp_lease_get_search_domains(sd_dhcp_lease *lease, char ***domains);
index 5950506c9bc5caad9ae2f9e6d5b312c5f3274915..b6a5e9db84742a5b1f8abf9ab3842af3e422e961 100644 (file)
@@ -21,6 +21,7 @@
 #include <inttypes.h>
 #include <netinet/in.h>
 
+#include "sd-dhcp-lease.h"
 #include "sd-dhcp-option.h"
 #include "sd-event.h"
 
@@ -30,6 +31,10 @@ _SD_BEGIN_DECLARATIONS;
 
 typedef struct sd_dhcp_server sd_dhcp_server;
 
+enum {
+        SD_DHCP_SERVER_EVENT_LEASE_CHANGED                      = 1 << 0,
+};
+
 int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex);
 
 sd_dhcp_server *sd_dhcp_server_ref(sd_dhcp_server *server);
@@ -39,20 +44,35 @@ int sd_dhcp_server_attach_event(sd_dhcp_server *client, sd_event *event, int64_t
 int sd_dhcp_server_detach_event(sd_dhcp_server *client);
 sd_event *sd_dhcp_server_get_event(sd_dhcp_server *client);
 
+typedef void (*sd_dhcp_server_callback_t)(sd_dhcp_server *server, uint64_t event, void *userdata);
+
+int sd_dhcp_server_set_callback(sd_dhcp_server *server, sd_dhcp_server_callback_t cb, void *userdata);
+
 int sd_dhcp_server_is_running(sd_dhcp_server *server);
 
 int sd_dhcp_server_start(sd_dhcp_server *server);
 int sd_dhcp_server_stop(sd_dhcp_server *server);
 
-int sd_dhcp_server_configure_pool(sd_dhcp_server *server, struct in_addr *address, unsigned char prefixlen, uint32_t offset, uint32_t size);
+int sd_dhcp_server_configure_pool(sd_dhcp_server *server, const struct in_addr *address, unsigned char prefixlen, uint32_t offset, uint32_t size);
 
 int sd_dhcp_server_set_timezone(sd_dhcp_server *server, const char *timezone);
-int sd_dhcp_server_set_dns(sd_dhcp_server *server, const struct in_addr dns[], unsigned n);
-int sd_dhcp_server_set_ntp(sd_dhcp_server *server, const struct in_addr ntp[], unsigned n);
-int sd_dhcp_server_set_sip(sd_dhcp_server *server, const struct in_addr sip[], unsigned n);
 int sd_dhcp_server_set_emit_router(sd_dhcp_server *server, int enabled);
 
+int sd_dhcp_server_set_servers(
+                sd_dhcp_server *server,
+                sd_dhcp_lease_server_type what,
+                const struct in_addr addresses[],
+                size_t n_addresses);
+
+int sd_dhcp_server_set_lpr(sd_dhcp_server *server, const struct in_addr lpr[], size_t n);
+int sd_dhcp_server_set_dns(sd_dhcp_server *server, const struct in_addr dns[], size_t n);
+int sd_dhcp_server_set_ntp(sd_dhcp_server *server, const struct in_addr ntp[], size_t n);
+int sd_dhcp_server_set_sip(sd_dhcp_server *server, const struct in_addr sip[], size_t n);
+int sd_dhcp_server_set_pop3(sd_dhcp_server *server, const struct in_addr pop3[], size_t n);
+int sd_dhcp_server_set_smtp(sd_dhcp_server *server, const struct in_addr smtp[], size_t n);
+
 int sd_dhcp_server_add_option(sd_dhcp_server *server, sd_dhcp_option *v);
+int sd_dhcp_server_add_vendor_option(sd_dhcp_server *server, sd_dhcp_option *v);
 
 int sd_dhcp_server_set_max_lease_time(sd_dhcp_server *server, uint32_t t);
 int sd_dhcp_server_set_default_lease_time(sd_dhcp_server *server, uint32_t t);
index be34d43e748153f4bb1c37e10b1c56fcf6aa0bd1..2b0d63a527d94236241e7a7c2daef7e6084a1f02 100644 (file)
@@ -24,6 +24,7 @@
 #include <sys/types.h>
 
 #include "sd-dhcp6-lease.h"
+#include "sd-dhcp6-option.h"
 #include "sd-event.h"
 
 #include "_sd-common.h"
@@ -73,6 +74,7 @@ enum {
         SD_DHCP6_OPTION_FQDN                       = 39,  /* RFC 4704 */
 
         SD_DHCP6_OPTION_NTP_SERVER                 = 56,  /* RFC 5908 */
+        SD_DHCP6_OPTION_MUD_URL                    = 112, /* RFC 8250 */
 
         /* option codes 89-142 are unassigned */
         /* option codes 144-65535 are unassigned */
@@ -108,6 +110,12 @@ int sd_dhcp6_client_set_duid_llt(
 int sd_dhcp6_client_set_iaid(
                 sd_dhcp6_client *client,
                 uint32_t iaid);
+int sd_dhcp6_client_get_iaid(
+                sd_dhcp6_client *client,
+                uint32_t *iaid);
+int sd_dhcp6_client_duid_as_string(
+                sd_dhcp6_client *client,
+                char **duid);
 int sd_dhcp6_client_set_fqdn(
                 sd_dhcp6_client *client,
                 const char *fqdn);
@@ -120,6 +128,15 @@ 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_request_mud_url(
+                sd_dhcp6_client *client,
+                const char *mudurl);
+int sd_dhcp6_client_set_request_user_class(
+                sd_dhcp6_client *client,
+                char** user_class);
+int sd_dhcp6_client_set_request_vendor_class(
+                sd_dhcp6_client *client,
+                char** vendor_class);
 int sd_dhcp6_client_set_prefix_delegation_hint(
                 sd_dhcp6_client *client,
                 uint8_t prefixlen,
@@ -132,12 +149,17 @@ int sd_dhcp6_client_get_address_request(sd_dhcp6_client *client,
                                         int *request);
 int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client,
                                         int request);
-int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id);
+int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client *client,
+                                       uint32_t transaction_id);
+int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client,
+                                      sd_dhcp6_option *v);
 
 int sd_dhcp6_client_get_lease(
                 sd_dhcp6_client *client,
                 sd_dhcp6_lease **ret);
 
+int sd_dhcp6_client_add_option(sd_dhcp6_client *client, sd_dhcp6_option *v);
+
 int sd_dhcp6_client_stop(sd_dhcp6_client *client);
 int sd_dhcp6_client_start(sd_dhcp6_client *client);
 int sd_dhcp6_client_is_running(sd_dhcp6_client *client);
index 33a32a6dc54b6df22adc28e19c0533000b8d6c00..4301c6db878b4e5e556507a05f5d76124145e371 100644 (file)
@@ -39,10 +39,9 @@ int sd_dhcp6_lease_get_pd(sd_dhcp6_lease *lease, struct in6_addr *prefix,
                           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_dns(sd_dhcp6_lease *lease, const struct in6_addr **addrs);
 int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains);
-int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease,
-                                 struct in6_addr **addrs);
+int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **addrs);
 int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn);
 
 sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease);
diff --git a/src/systemd/sd-dhcp6-option.h b/src/systemd/sd-dhcp6-option.h
new file mode 100644 (file)
index 0000000..88a4986
--- /dev/null
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#ifndef foosddhcp6optionhfoo
+#define foosddhcp6optionhfoo
+
+/***
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General 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 <inttypes.h>
+#include <sys/types.h>
+
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
+
+typedef struct sd_dhcp6_option sd_dhcp6_option;
+
+int sd_dhcp6_option_new(uint16_t option, const void *data, size_t length, uint32_t enterprise_identifier, sd_dhcp6_option **ret);
+sd_dhcp6_option *sd_dhcp6_option_ref(sd_dhcp6_option *ra);
+sd_dhcp6_option *sd_dhcp6_option_unref(sd_dhcp6_option *ra);
+
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp6_option, sd_dhcp6_option_unref);
+
+_SD_END_DECLARATIONS;
+
+#endif
index 29ff40ab2618f5f965039832e76fad1f234ca1bd..d220f21aa29f518578d22d619f19aeefa250cfe8 100644 (file)
@@ -72,7 +72,7 @@ enum {
         SD_JOURNAL_ALL_NAMESPACES            = 1 << 5, /* Show all namespaces, not just the default or specified one */
         SD_JOURNAL_INCLUDE_DEFAULT_NAMESPACE = 1 << 6, /* Show default namespace in addition to specified one */
 
-        SD_JOURNAL_SYSTEM_ONLY _sd_deprecated_ = SD_JOURNAL_SYSTEM /* deprecated name */
+        SD_JOURNAL_SYSTEM_ONLY _sd_deprecated_ = SD_JOURNAL_SYSTEM /* old name */
 };
 
 /* Wakeup event types */
@@ -88,7 +88,7 @@ int sd_journal_open_directory(sd_journal **ret, const char *path, int flags);
 int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags);
 int sd_journal_open_files(sd_journal **ret, const char **paths, int flags);
 int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fds, int flags);
-int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) _sd_deprecated_; /* deprecated */
+int sd_journal_open_container(sd_journal **ret, const char *machine, int flags) _sd_deprecated_;
 void sd_journal_close(sd_journal *j);
 
 int sd_journal_previous(sd_journal *j);
@@ -105,6 +105,7 @@ int sd_journal_get_data_threshold(sd_journal *j, size_t *sz);
 
 int sd_journal_get_data(sd_journal *j, const char *field, const void **data, size_t *l);
 int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t *l);
+int sd_journal_enumerate_available_data(sd_journal *j, const void **data, size_t *l);
 void sd_journal_restart_data(sd_journal *j);
 
 int sd_journal_add_match(sd_journal *j, const void *data, size_t size);
@@ -128,6 +129,7 @@ int sd_journal_get_usage(sd_journal *j, uint64_t *bytes);
 
 int sd_journal_query_unique(sd_journal *j, const char *field);
 int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_t *l);
+int sd_journal_enumerate_available_unique(sd_journal *j, const void **data, size_t *l);
 void sd_journal_restart_unique(sd_journal *j);
 
 int sd_journal_enumerate_fields(sd_journal *j, const char **field);
@@ -156,13 +158,13 @@ int sd_journal_has_persistent_files(sd_journal *j);
         if (sd_journal_seek_tail(j) < 0) { }                            \
         else while (sd_journal_previous(j) > 0)
 
-/* Iterate through the data fields of the current journal entry */
+/* Iterate through all available data fields of the current journal entry */
 #define SD_JOURNAL_FOREACH_DATA(j, data, l)                             \
-        for (sd_journal_restart_data(j); sd_journal_enumerate_data((j), &(data), &(l)) > 0; )
+        for (sd_journal_restart_data(j); sd_journal_enumerate_available_data((j), &(data), &(l)) > 0; )
 
-/* Iterate through the all known values of a specific field */
+/* Iterate through all available values of a specific field */
 #define SD_JOURNAL_FOREACH_UNIQUE(j, data, l)                           \
-        for (sd_journal_restart_unique(j); sd_journal_enumerate_unique((j), &(data), &(l)) > 0; )
+        for (sd_journal_restart_unique(j); sd_journal_enumerate_available_unique((j), &(data), &(l)) > 0; )
 
 /* Iterate through all known field names */
 #define SD_JOURNAL_FOREACH_FIELD(j, field) \
index bf3afadcec54e1c237f647f0d8b424d33d02c256..c2abc201216e2980b89940c937bcff3730c138bb 100644 (file)
@@ -96,6 +96,9 @@ enum {
 #define SD_LLDP_OUI_802_1 (uint8_t[]) { 0x00, 0x80, 0xc2 }
 #define SD_LLDP_OUI_802_3 (uint8_t[]) { 0x00, 0x12, 0x0f }
 
+#define SD_LLDP_OUI_MUD   (uint8_t[]) { 0x00, 0x00, 0x5E }
+#define SD_LLDP_OUI_SUBTYPE_MUD_USAGE_DESCRIPTION  0x01
+
 /* IEEE 802.1AB-2009 Annex E */
 enum {
         SD_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID          = 1,
@@ -169,10 +172,11 @@ int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec);
 int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret);
 int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret);
 int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret);
+int sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor *n, const char **ret);
 int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret);
 int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret);
 
-/* Low-level, iterative TLV access. This is for evertyhing else, it iteratively goes through all available TLVs
+/* Low-level, iterative TLV access. This is for everything else, it iteratively goes through all available TLVs
  * (including the ones covered with the calls above), and allows multiple TLVs for the same fields. */
 int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n);
 int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n);
index 50be5433db4b468acb8ff042e9c7172edfa24ee5..e18f01bb671de665892c01c4e333a80a34dccf01 100644 (file)
@@ -183,7 +183,7 @@ int sd_seat_get_active(const char *seat, char **session, uid_t *uid);
 int sd_seat_get_sessions(const char *seat, char ***sessions, uid_t **uid, unsigned *n_uids);
 
 /* Return whether the seat is multi-session capable */
-int sd_seat_can_multi_session(const char *seat);
+int sd_seat_can_multi_session(const char *seat) _sd_deprecated_;
 
 /* Return whether the seat is TTY capable, i.e. suitable for showing console UIs */
 int sd_seat_can_tty(const char *seat);
index 162b650e649b22eb87ed05fec33d34ae2dcbd4b5..a4e9680dc11ce4c8cf493a022878bd308edfda9e 100644 (file)
@@ -161,6 +161,20 @@ _SD_BEGIN_DECLARATIONS;
 #define SD_MESSAGE_UNSAFE_USER_NAME       SD_ID128_MAKE(b6,1f,da,c6,12,e9,4b,91,82,28,5b,99,88,43,06,1f)
 #define SD_MESSAGE_UNSAFE_USER_NAME_STR   SD_ID128_MAKE_STR(b6,1f,da,c6,12,e9,4b,91,82,28,5b,99,88,43,06,1f)
 
+#define SD_MESSAGE_MOUNT_POINT_PATH_NOT_SUITABLE \
+                                          SD_ID128_MAKE(1b,3b,b9,40,37,f0,4b,bf,81,02,8e,13,5a,12,d2,93)
+#define SD_MESSAGE_MOUNT_POINT_PATH_NOT_SUITABLE_STR \
+                                          SD_ID128_MAKE_STR(1b,3b,b9,40,37,f0,4b,bf,81,02,8e,13,5a,12,d2,93)
+
+#define SD_MESSAGE_NOBODY_USER_UNSUITABLE SD_ID128_MAKE(b4,80,32,5f,9c,39,4a,7b,80,2c,23,1e,51,a2,75,2c)
+#define SD_MESSAGE_NOBODY_USER_UNSUITABLE_STR \
+                                          SD_ID128_MAKE_STR(b4,80,32,5f,9c,39,4a,7b,80,2c,23,1e,51,a2,75,2c)
+
+#define SD_MESSAGE_SYSTEMD_UDEV_SETTLE_DEPRECATED \
+                                          SD_ID128_MAKE(1c,04,54,c1,bd,22,41,e0,ac,6f,ef,b4,bc,63,14,33)
+#define SD_MESSAGE_SYSTEMD_UDEV_SETTLE_DEPRECATED_STR \
+                                          SD_ID128_MAKE_STR(1c,04,54,c1,bd,22,41,e0,ac,6f,ef,b4,bc,63,14,33)
+
 _SD_END_DECLARATIONS;
 
 #endif
index d1bee343a2a8b26a8d787620509fc3f9b1c6a2fd..3ddfc8cb6d2f2c9aabbbf9a970ad5e5cc1c27b8c 100644 (file)
@@ -30,7 +30,7 @@
 
 _SD_BEGIN_DECLARATIONS;
 
-/* Neightbor Discovery Options, RFC 4861, Section 4.6 and
+/* Neighbor Discovery Options, RFC 4861, Section 4.6 and
  * https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml#icmpv6-parameters-5 */
 enum {
         SD_NDISC_OPTION_SOURCE_LL_ADDRESS  = 1,
index 644d462b65eb0e539f3f6287f16110b44c836805..f9196491d6d8bbaec06251f24206d031adce0e72 100644 (file)
@@ -86,6 +86,10 @@ int sd_netlink_message_append_u8(sd_netlink_message *m, unsigned short type, uin
 int sd_netlink_message_append_u16(sd_netlink_message *m, unsigned short type, uint16_t data);
 int sd_netlink_message_append_u32(sd_netlink_message *m, unsigned short type, uint32_t data);
 int sd_netlink_message_append_u64(sd_netlink_message *m, unsigned short type, uint64_t data);
+int sd_netlink_message_append_s8(sd_netlink_message *m, unsigned short type, int8_t data);
+int sd_netlink_message_append_s16(sd_netlink_message *m, unsigned short type, int16_t data);
+int sd_netlink_message_append_s32(sd_netlink_message *m, unsigned short type, int32_t data);
+int sd_netlink_message_append_s64(sd_netlink_message *m, unsigned short type, int64_t data);
 int sd_netlink_message_append_data(sd_netlink_message *m, unsigned short type, const void *data, size_t len);
 int sd_netlink_message_append_in_addr(sd_netlink_message *m, unsigned short type, const struct in_addr *data);
 int sd_netlink_message_append_in6_addr(sd_netlink_message *m, unsigned short type, const struct in6_addr *data);
@@ -208,6 +212,10 @@ int sd_rtnl_message_new_qdisc(sd_netlink *rtnl, sd_netlink_message **ret, uint16
 int sd_rtnl_message_set_qdisc_parent(sd_netlink_message *m, uint32_t parent);
 int sd_rtnl_message_set_qdisc_handle(sd_netlink_message *m, uint32_t handle);
 
+int sd_rtnl_message_new_tclass(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int tcm_family, int tcm_ifindex);
+int sd_rtnl_message_set_tclass_parent(sd_netlink_message *m, uint32_t parent);
+int sd_rtnl_message_set_tclass_handle(sd_netlink_message *m, uint32_t handle);
+
 /* 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);
index d0b432274c7762425f08fd12de0415d2a16c5f38..42bcd74b745885b2f0875d35a65e5cf208e545d1 100644 (file)
@@ -110,14 +110,14 @@ int sd_network_link_get_network_file(int ifindex, char **filename);
  * IP addresses */
 int sd_network_link_get_dns(int ifindex, char ***ret);
 
-/* Get DHCP4 address for a given link. This is string representations of
- * IPv4 address */
-int sd_network_link_get_dhcp4_address(int ifindex, char **ret);
-
 /* Get NTP entries for a given link. These are domain names or string
  * representations of IP addresses */
 int sd_network_link_get_ntp(int ifindex, char ***ret);
 
+/* Get SIP entries for a given link. These are string
+ * representations of IP addresses */
+int sd_network_link_get_sip(int ifindex, char ***ret);
+
 /* Indicates whether or not LLMNR should be enabled for the link
  * Possible levels of support: yes, no, resolve
  * Possible return codes:
@@ -160,9 +160,6 @@ int sd_network_link_get_search_domains(int ifindex, char ***domains);
 /* Get the route DNS domain names for a given link. */
 int sd_network_link_get_route_domains(int ifindex, char ***domains);
 
-/* Get the sip servers for a given link. */
-int sd_network_link_get_sip_servers(int ifindex, char ***sip);
-
 /* Get whether this link shall be used as 'default route' for DNS queries */
 int sd_network_link_get_dns_default_route(int ifindex);
 
@@ -172,8 +169,11 @@ int sd_network_link_get_carrier_bound_to(int ifindex, int **ifindexes);
 /* Get the CARRIERS that are bound to current link. */
 int sd_network_link_get_carrier_bound_by(int ifindex, int **ifindexes);
 
-/* Get the timezone that was learnt on a specific link. */
-int sd_network_link_get_timezone(int ifindex, char **timezone);
+/* Get DHCPv6 client IAID for a given link. */
+int sd_network_link_get_dhcp6_client_iaid_string(int ifindex, char **iaid);
+
+/* Get DHCPv6 client DUID for a given link. */
+int sd_network_link_get_dhcp6_client_duid_string(int ifindex, char **duid);
 
 /* Monitor object */
 typedef struct sd_network_monitor sd_network_monitor;
index 16379876eb69999638532370c7b28d4710140cba..e13e67db8fc4904e71e52c27141420912e4e05f2 100644 (file)
@@ -78,11 +78,43 @@ enum {
         SD_PATH_SEARCH_STATE_FACTORY,
         SD_PATH_SEARCH_CONFIGURATION,
 
+        /* Various systemd paths, generally mirroring systemd.pc — Except we drop the "dir" suffix (and
+         * replaces "path" by "search"), since this API is about dirs/paths anyway, and contains "path"
+         * already in the prefix */
+        SD_PATH_SYSTEMD_UTIL,
+        SD_PATH_SYSTEMD_SYSTEM_UNIT,
+        SD_PATH_SYSTEMD_SYSTEM_PRESET,
+        SD_PATH_SYSTEMD_SYSTEM_CONF,
+        SD_PATH_SYSTEMD_USER_UNIT,
+        SD_PATH_SYSTEMD_USER_PRESET,
+        SD_PATH_SYSTEMD_USER_CONF,
+
+        SD_PATH_SYSTEMD_SEARCH_SYSTEM_UNIT,
+        SD_PATH_SYSTEMD_SEARCH_USER_UNIT,
+
+        SD_PATH_SYSTEMD_SYSTEM_GENERATOR,
+        SD_PATH_SYSTEMD_USER_GENERATOR,
+        SD_PATH_SYSTEMD_SEARCH_SYSTEM_GENERATOR,
+        SD_PATH_SYSTEMD_SEARCH_USER_GENERATOR,
+
+        SD_PATH_SYSTEMD_SLEEP,
+        SD_PATH_SYSTEMD_SHUTDOWN,
+
+        SD_PATH_TMPFILES,
+        SD_PATH_SYSUSERS,
+        SD_PATH_SYSCTL,
+        SD_PATH_BINFMT,
+        SD_PATH_MODULES_LOAD,
+        SD_PATH_CATALOG,
+
+        /* systemd-networkd search paths */
+        SD_PATH_SYSTEMD_SEARCH_NETWORK,
+
         _SD_PATH_MAX,
 };
 
-int sd_path_home(uint64_t type, const char *suffix, char **path);
-int sd_path_search(uint64_t type, const char *suffix, char ***paths);
+int sd_path_lookup(uint64_t type, const char *suffix, char **path);
+int sd_path_lookup_strv(uint64_t type, const char *suffix, char ***paths);
 
 _SD_END_DECLARATIONS;
 
index f0852319347be3620cb213c65055b06b3f9d7885..0f1437829d03f11a4e9483e21e6ffd03b7a1b41b 100644 (file)
@@ -50,6 +50,7 @@ sd_event *sd_radv_get_event(sd_radv *ra);
 
 int sd_radv_start(sd_radv *ra);
 int sd_radv_stop(sd_radv *ra);
+int sd_radv_is_running(sd_radv *ra);
 
 int sd_radv_set_ifindex(sd_radv *ra, int interface_index);
 int sd_radv_set_mac(sd_radv *ra, const struct ether_addr *mac_addr);
@@ -74,6 +75,8 @@ sd_radv_prefix *sd_radv_prefix_unref(sd_radv_prefix *ra);
 
 int sd_radv_prefix_set_prefix(sd_radv_prefix *p, const struct in6_addr *in6_addr,
                               unsigned char prefixlen);
+int sd_radv_prefix_get_prefix(sd_radv_prefix *p, struct in6_addr *ret_in6_addr,
+                              unsigned char *ret_prefixlen);
 int sd_radv_prefix_set_onlink(sd_radv_prefix *p, int onlink);
 int sd_radv_prefix_set_address_autoconfiguration(sd_radv_prefix *p,
                                                  int address_autoconfiguration);
index fc0855d611351b6d5ef9816976f43abcdbaced4d..b5e7e08eee969c34c4617b6599ff12e0ac921da2 100644 (file)
@@ -345,28 +345,6 @@ static int putsgent_with_members(const struct sgrp *sg, FILE *gshadow) {
 }
 #endif
 
-static int sync_rights(FILE *from, const char *to) {
-        struct stat st;
-
-        if (fstat(fileno(from), &st) < 0)
-                return -errno;
-
-        return chmod_and_chown_unsafe(to, st.st_mode & 07777, st.st_uid, st.st_gid);
-}
-
-static int rename_and_apply_smack(const char *temp_path, const char *dest_path) {
-        int r = 0;
-        if (rename(temp_path, dest_path) < 0)
-                return -errno;
-
-#ifdef SMACK_RUN_LABEL
-        r = mac_smack_apply(dest_path, SMACK_ATTR_ACCESS, SMACK_FLOOR_LABEL);
-        if (r < 0)
-                return r;
-#endif
-        return r;
-}
-
 static const char* default_shell(uid_t uid) {
         return uid == 0 ? "/bin/sh" : NOLOGIN;
 }
@@ -389,7 +367,7 @@ static int write_temporary_passwd(const char *passwd_path, FILE **tmpfile, char
         original = fopen(passwd_path, "re");
         if (original) {
 
-                r = sync_rights(original, passwd_tmp);
+                r = sync_rights(fileno(original), fileno(passwd));
                 if (r < 0)
                         return r;
 
@@ -491,7 +469,7 @@ static int write_temporary_shadow(const char *shadow_path, FILE **tmpfile, char
         original = fopen(shadow_path, "re");
         if (original) {
 
-                r = sync_rights(original, shadow_tmp);
+                r = sync_rights(fileno(original), fileno(shadow));
                 if (r < 0)
                         return r;
 
@@ -588,7 +566,7 @@ static int write_temporary_group(const char *group_path, FILE **tmpfile, char **
         original = fopen(group_path, "re");
         if (original) {
 
-                r = sync_rights(original, group_tmp);
+                r = sync_rights(fileno(original), fileno(group));
                 if (r < 0)
                         return r;
 
@@ -687,7 +665,7 @@ static int write_temporary_gshadow(const char * gshadow_path, FILE **tmpfile, ch
         if (original) {
                 struct sgrp *sg;
 
-                r = sync_rights(original, gshadow_tmp);
+                r = sync_rights(fileno(original), fileno(gshadow));
                 if (r < 0)
                         return r;
 
@@ -794,14 +772,14 @@ static int write_files(void) {
 
         /* And make the new files count */
         if (group) {
-                r = rename_and_apply_smack(group_tmp, group_path);
+                r = rename_and_apply_smack_floor_label(group_tmp, group_path);
                 if (r < 0)
                         return r;
 
                 group_tmp = mfree(group_tmp);
         }
         if (gshadow) {
-                r = rename_and_apply_smack(gshadow_tmp, gshadow_path);
+                r = rename_and_apply_smack_floor_label(gshadow_tmp, gshadow_path);
                 if (r < 0)
                         return r;
 
@@ -809,14 +787,14 @@ static int write_files(void) {
         }
 
         if (passwd) {
-                r = rename_and_apply_smack(passwd_tmp, passwd_path);
+                r = rename_and_apply_smack_floor_label(passwd_tmp, passwd_path);
                 if (r < 0)
                         return r;
 
                 passwd_tmp = mfree(passwd_tmp);
         }
         if (shadow) {
-                r = rename_and_apply_smack(shadow_tmp, shadow_path);
+                r = rename_and_apply_smack_floor_label(shadow_tmp, shadow_path);
                 if (r < 0)
                         return r;
 
@@ -1389,12 +1367,18 @@ static bool item_equal(Item *a, Item *b) {
 static int parse_line(const char *fname, unsigned line, const char *buffer) {
 
         static const Specifier specifier_table[] = {
-                { 'm', specifier_machine_id,     NULL },
-                { 'b', specifier_boot_id,        NULL },
-                { 'H', specifier_host_name,      NULL },
-                { 'v', specifier_kernel_release, NULL },
-                { 'T', specifier_tmp_dir,        NULL },
-                { 'V', specifier_var_tmp_dir,    NULL },
+                { 'm', specifier_machine_id,      NULL },
+                { 'b', specifier_boot_id,         NULL },
+                { 'H', specifier_host_name,       NULL },
+                { 'l', specifier_short_host_name, NULL },
+                { 'v', specifier_kernel_release,  NULL },
+                { 'a', specifier_architecture,    NULL },
+                { 'o', specifier_os_id,           NULL },
+                { 'w', specifier_os_version_id,   NULL },
+                { 'B', specifier_os_build_id,     NULL },
+                { 'W', specifier_os_variant_id,   NULL },
+                { 'T', specifier_tmp_dir,         NULL },
+                { 'V', specifier_var_tmp_dir,     NULL },
                 {}
         };
 
@@ -1808,7 +1792,7 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_ROOT:
-                        r = parse_path_argument_and_warn(optarg, true, &arg_root);
+                        r = parse_path_argument_and_warn(optarg, /* suppress_root= */ false, &arg_root);
                         if (r < 0)
                                 return r;
                         break;
@@ -1914,7 +1898,7 @@ static int run(int argc, char *argv[]) {
 
         r = mac_selinux_init();
         if (r < 0)
-                return log_error_errno(r, "SELinux setup failed: %m");
+                return r;
 
         /* If command line arguments are specified along with --replace, read all
          * configuration files and insert the positional arguments at the specified
index 5df5743823d522c0562938193b8c00b5f41deac7..a2c72d1009d720b5cf6f66d3a94e5c0f0688490c 100644 (file)
@@ -788,19 +788,25 @@ static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) {
                         if (!fpath)
                                 return log_oom();
 
-                        service = new0(SysvStub, 1);
+                        log_warning("SysV service '%s' lacks a native systemd unit file. "
+                                    "Automatically generating a unit file for compatibility. "
+                                    "Please update package to include a native systemd unit file, in order to make it more safe and robust.", fpath);
+
+                        service = new(SysvStub, 1);
                         if (!service)
                                 return log_oom();
 
-                        service->sysv_start_priority = -1;
-                        service->name = TAKE_PTR(name);
-                        service->path = TAKE_PTR(fpath);
+                        *service = (SysvStub) {
+                                .sysv_start_priority = -1,
+                                .name = TAKE_PTR(name),
+                                .path = TAKE_PTR(fpath),
+                        };
 
                         r = hashmap_put(all_services, service->name, service);
                         if (r < 0)
                                 return log_oom();
 
-                        service = NULL;
+                        TAKE_PTR(service);
                 }
         }
 
@@ -811,7 +817,6 @@ static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_servic
         Set *runlevel_services[ELEMENTSOF(rcnd_table)] = {};
         _cleanup_strv_free_ char **sysvrcnd_path = NULL;
         SysvStub *service;
-        unsigned i;
         Iterator j;
         char **p;
         int r;
@@ -822,9 +827,8 @@ static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_servic
         if (r < 0)
                 return r;
 
-        STRV_FOREACH(p, sysvrcnd_path) {
-                for (i = 0; i < ELEMENTSOF(rcnd_table); i ++) {
-
+        STRV_FOREACH(p, sysvrcnd_path)
+                for (unsigned i = 0; i < ELEMENTSOF(rcnd_table); i ++) {
                         _cleanup_closedir_ DIR *d = NULL;
                         _cleanup_free_ char *path = NULL;
                         struct dirent *de;
@@ -843,7 +847,7 @@ static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_servic
                                 continue;
                         }
 
-                        FOREACH_DIRENT(de, d, log_error_errno(errno, "Failed to enumerate directory %s, ignoring: %m", path)) {
+                        FOREACH_DIRENT(de, d, log_warning_errno(errno, "Failed to enumerate directory %s, ignoring: %m", path)) {
                                 _cleanup_free_ char *name = NULL, *fpath = NULL;
                                 int a, b;
 
@@ -879,22 +883,15 @@ static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_servic
 
                                 service->sysv_start_priority = MAX(a*10 + b, service->sysv_start_priority);
 
-                                r = set_ensure_allocated(&runlevel_services[i], NULL);
-                                if (r < 0) {
-                                        log_oom();
-                                        goto finish;
-                                }
-
-                                r = set_put(runlevel_services[i], service);
+                                r = set_ensure_put(&runlevel_services[i], NULL, service);
                                 if (r < 0) {
                                         log_oom();
                                         goto finish;
                                 }
                         }
                 }
-        }
 
-        for (i = 0; i < ELEMENTSOF(rcnd_table); i ++)
+        for (unsigned i = 0; i < ELEMENTSOF(rcnd_table); i++)
                 SET_FOREACH(service, runlevel_services[i], j) {
                         r = strv_extend(&service->before, rcnd_table[i].target);
                         if (r < 0) {
@@ -911,7 +908,7 @@ static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_servic
         r = 0;
 
 finish:
-        for (i = 0; i < ELEMENTSOF(rcnd_table); i++)
+        for (unsigned i = 0; i < ELEMENTSOF(rcnd_table); i++)
                 set_free(runlevel_services[i]);
 
         return r;
index a150ee106be7d460538d6d025e4cc92c85c46238..132989f197eabc4845cfdcf16c5ce043e7f50ad8 100644 (file)
@@ -301,6 +301,12 @@ tests += [
          [],
          []],
 
+        [['src/test/test-offline-passwd.c',
+          'src/shared/offline-passwd.c',
+          'src/shared/offline-passwd.h'],
+         [],
+         []],
+
         [['src/test/test-escape.c'],
          [],
          []],
@@ -470,7 +476,7 @@ tests += [
          '', 'timeout=90'],
 
         [['src/test/test-set.c'],
-         [],
+         [libbasic],
          []],
 
         [['src/test/test-ordered-set.c'],
@@ -588,14 +594,17 @@ tests += [
          [],
          []],
 
+        [['src/test/test-coredump-util.c'],
+         [],
+         []],
+
         [['src/test/test-daemon.c'],
          [],
          []],
 
         [['src/test/test-cgroup.c'],
          [],
-         [],
-         '', 'manual'],
+         []],
 
         [['src/test/test-cgroup-cpu.c'],
          [libcore,
@@ -662,7 +671,8 @@ tests += [
           libseccomp,
           libselinux,
           libmount,
-          libblkid]],
+          libblkid],
+         '', 'timeout=120'],
 
         [['src/test/test-execute.c'],
          [libcore,
@@ -779,6 +789,10 @@ tests += [
          [],
          []],
 
+        [['src/test/test-sd-path.c'],
+         [],
+         []],
+
         [['src/test/test-local-addresses.c'],
          [],
          []],
@@ -886,12 +900,14 @@ tests += [
          [libjournal_core,
           libshared],
          [liblz4,
+          libzstd,
           libxz]],
 
         [['src/journal/test-compress-benchmark.c'],
          [libjournal_core,
           libshared],
          [liblz4,
+          libzstd,
           libxz],
          '', 'timeout=90'],
 
@@ -1023,10 +1039,7 @@ tests += [
 
 ]
 
-# test-bus-vtable-cc.cc is a symlink and symlinks get lost in containers on FuzzBuzz.
-# The issue has been reported to the developers of FuzzBuzz and hopefully will be fixed soon.
-# In the meantime, let's just skip the symlink there.
-if cxx_cmd != '' and not want_fuzzbuzz
+if cxx_cmd != ''
         tests += [
                 [['src/libsystemd/sd-bus/test-bus-vtable-cc.cc'],
                  [],
@@ -1137,3 +1150,13 @@ tests += [
           libshared],
          [threads]],
 ]
+
+############################################################
+
+tests += [
+        [['src/test/test-xdg-autostart.c',
+          'src/xdg-autostart-generator/xdg-autostart-service.c',
+          'src/xdg-autostart-generator/xdg-autostart-service.h',],
+         [],
+         []],
+]
index 1322af48122397ad5486c0d8ed9496624dc70f74..d2740bca73012938dd750fa929c6efe721e2c02d 100644 (file)
@@ -24,7 +24,7 @@ static void test_policy_closed(const char *cgroup_path, BPFProgram **installed_p
         r = bpf_devices_cgroup_init(&prog, CGROUP_DEVICE_POLICY_CLOSED, true);
         assert_se(r >= 0);
 
-        r = bpf_devices_whitelist_static(prog, cgroup_path);
+        r = bpf_devices_allow_list_static(prog, cgroup_path);
         assert_se(r >= 0);
 
         r = bpf_devices_apply_policy(prog, CGROUP_DEVICE_POLICY_CLOSED, true, cgroup_path, installed_prog);
@@ -62,13 +62,13 @@ static void test_policy_strict(const char *cgroup_path, BPFProgram **installed_p
         r = bpf_devices_cgroup_init(&prog, CGROUP_DEVICE_POLICY_STRICT, true);
         assert_se(r >= 0);
 
-        r = bpf_devices_whitelist_device(prog, cgroup_path, "/dev/null", "rw");
+        r = bpf_devices_allow_list_device(prog, cgroup_path, "/dev/null", "rw");
         assert_se(r >= 0);
 
-        r = bpf_devices_whitelist_device(prog, cgroup_path, "/dev/random", "r");
+        r = bpf_devices_allow_list_device(prog, cgroup_path, "/dev/random", "r");
         assert_se(r >= 0);
 
-        r = bpf_devices_whitelist_device(prog, cgroup_path, "/dev/zero", "w");
+        r = bpf_devices_allow_list_device(prog, cgroup_path, "/dev/zero", "w");
         assert_se(r >= 0);
 
         r = bpf_devices_apply_policy(prog, CGROUP_DEVICE_POLICY_STRICT, true, cgroup_path, installed_prog);
@@ -129,7 +129,7 @@ static void test_policy_strict(const char *cgroup_path, BPFProgram **installed_p
         assert_se(wrong == 0);
 }
 
-static void test_policy_whitelist_major(const char *pattern, const char *cgroup_path, BPFProgram **installed_prog) {
+static void test_policy_allow_list_major(const char *pattern, const char *cgroup_path, BPFProgram **installed_prog) {
         _cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL;
         unsigned wrong = 0;
         int r;
@@ -139,7 +139,7 @@ static void test_policy_whitelist_major(const char *pattern, const char *cgroup_
         r = bpf_devices_cgroup_init(&prog, CGROUP_DEVICE_POLICY_STRICT, true);
         assert_se(r >= 0);
 
-        r = bpf_devices_whitelist_major(prog, cgroup_path, pattern, 'c', "rw");
+        r = bpf_devices_allow_list_major(prog, cgroup_path, pattern, 'c', "rw");
         assert_se(r >= 0);
 
         r = bpf_devices_apply_policy(prog, CGROUP_DEVICE_POLICY_STRICT, true, cgroup_path, installed_prog);
@@ -188,7 +188,7 @@ static void test_policy_whitelist_major(const char *pattern, const char *cgroup_
         assert_se(wrong == 0);
 }
 
-static void test_policy_whitelist_major_star(char type, const char *cgroup_path, BPFProgram **installed_prog) {
+static void test_policy_allow_list_major_star(char type, const char *cgroup_path, BPFProgram **installed_prog) {
         _cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL;
         unsigned wrong = 0;
         int r;
@@ -198,7 +198,7 @@ static void test_policy_whitelist_major_star(char type, const char *cgroup_path,
         r = bpf_devices_cgroup_init(&prog, CGROUP_DEVICE_POLICY_STRICT, true);
         assert_se(r >= 0);
 
-        r = bpf_devices_whitelist_major(prog, cgroup_path, "*", type, "rw");
+        r = bpf_devices_allow_list_major(prog, cgroup_path, "*", type, "rw");
         assert_se(r >= 0);
 
         r = bpf_devices_apply_policy(prog, CGROUP_DEVICE_POLICY_STRICT, true, cgroup_path, installed_prog);
@@ -230,7 +230,7 @@ static void test_policy_empty(bool add_mismatched, const char *cgroup_path, BPFP
         assert_se(r >= 0);
 
         if (add_mismatched) {
-                r = bpf_devices_whitelist_major(prog, cgroup_path, "foobarxxx", 'c', "rw");
+                r = bpf_devices_allow_list_major(prog, cgroup_path, "foobarxxx", 'c', "rw");
                 assert_se(r < 0);
         }
 
@@ -287,11 +287,11 @@ int main(int argc, char *argv[]) {
         test_policy_closed(cgroup, &prog);
         test_policy_strict(cgroup, &prog);
 
-        test_policy_whitelist_major("mem", cgroup, &prog);
-        test_policy_whitelist_major("1", cgroup, &prog);
+        test_policy_allow_list_major("mem", cgroup, &prog);
+        test_policy_allow_list_major("1", cgroup, &prog);
 
-        test_policy_whitelist_major_star('c', cgroup, &prog);
-        test_policy_whitelist_major_star('b', cgroup, &prog);
+        test_policy_allow_list_major_star('c', cgroup, &prog);
+        test_policy_allow_list_major_star('b', cgroup, &prog);
 
         test_policy_empty(false, cgroup, &prog);
         test_policy_empty(true, cgroup, &prog);
index fbaa349b450e240ed7d3311bdb6d59de7690aac4..71aed125583d34ad4a42588a19c7672275ef86ab 100644 (file)
@@ -48,7 +48,9 @@ int main(int argc, char *argv[]) {
         if (r == -ENOMEDIUM)
                 return log_tests_skipped("cgroupfs not available");
 
-        assert_se(set_unit_path(get_testdata_dir()) >= 0);
+        _cleanup_free_ char *unit_dir = NULL;
+        assert_se(get_testdata_dir("units", &unit_dir) >= 0);
+        assert_se(set_unit_path(unit_dir) >= 0);
         assert_se(runtime_dir = setup_fake_runtime_dir());
 
         r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, &p);
index 33dd2461c37c554d9a405144dcb864b9e15958e6..81d5c456d749b122a699f30c41c51517eeb968fa 100644 (file)
 
 /* verify the capability parser */
 static void test_cap_list(void) {
-        int i;
-
         assert_se(!capability_to_name(-1));
         assert_se(!capability_to_name(capability_list_length()));
 
-        for (i = 0; i < capability_list_length(); i++) {
+        for (int i = 0; i < capability_list_length(); i++) {
                 const char *n;
 
                 assert_se(n = capability_to_name(i));
@@ -35,7 +33,7 @@ static void test_cap_list(void) {
         assert_se(capability_from_name("64") == -EINVAL);
         assert_se(capability_from_name("-1") == -EINVAL);
 
-        for (i = 0; i < capability_list_length(); i++) {
+        for (int i = 0; i < capability_list_length(); i++) {
                 _cleanup_cap_free_charp_ char *a = NULL;
                 const char *b;
                 unsigned u;
index 74b27379ea47df4f388e71df72461394ad077028..249323f8cf78e59652bd0687545fd0df75cce817 100644 (file)
@@ -9,6 +9,7 @@
 
 #include "alloc-util.h"
 #include "capability-util.h"
+#include "errno-util.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "macro.h"
@@ -35,6 +36,8 @@ static void test_last_cap_file(void) {
         int r;
 
         r = read_one_line_file("/proc/sys/kernel/cap_last_cap", &content);
+        if (r == -ENOENT || ERRNO_IS_PRIVILEGE(r)) /* kernel pre 3.2 or no access */
+                return;
         assert_se(r >= 0);
 
         r = safe_atolu(content, &val);
@@ -230,7 +233,7 @@ static void test_ensure_cap_64bit(void) {
         int r;
 
         r = read_one_line_file("/proc/sys/kernel/cap_last_cap", &content);
-        if (r == -ENOENT) /* kernel pre 3.2 */
+        if (r == -ENOENT || ERRNO_IS_PRIVILEGE(r)) /* kernel pre 3.2 or no access */
                 return;
         assert_se(r >= 0);
 
@@ -240,7 +243,7 @@ static void test_ensure_cap_64bit(void) {
         assert_se(p <= 63);
 
         /* Also check for the header definition */
-        assert_se(CAP_LAST_CAP <= 63);
+        assert_cc(CAP_LAST_CAP <= 63);
 }
 
 int main(int argc, char *argv[]) {
index 02c8549b4b653bf9878efb56e57689fc64e64f67..daafba4ef63a327fc35d2004ef98bfd4b9e115a2 100644 (file)
@@ -38,7 +38,9 @@ static int test_cgroup_mask(void) {
                 return log_tests_skipped("cgroupfs not available");
 
         /* Prepare the manager. */
-        assert_se(set_unit_path(get_testdata_dir()) >= 0);
+        _cleanup_free_ char *unit_dir = NULL;
+        assert_se(get_testdata_dir("units", &unit_dir) >= 0);
+        assert_se(set_unit_path(unit_dir) >= 0);
         assert_se(runtime_dir = setup_fake_runtime_dir());
         r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m);
         if (IN_SET(r, -EPERM, -EACCES)) {
index 330631a9101f8d1db817c6cee10486742fe663a9..25fa0d75df0c0ee78ad008e971f2926c5df333c8 100644 (file)
@@ -1,8 +1,11 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
+#include <unistd.h>
+
 #include "alloc-util.h"
 #include "build.h"
 #include "cgroup-setup.h"
+#include "errno-util.h"
 #include "log.h"
 #include "proc-cmdline.h"
 #include "string-util.h"
@@ -59,6 +62,9 @@ static void test_is_wanted(void) {
 int main(void) {
         test_setup_logging(LOG_DEBUG);
 
+        if (access("/proc/cmdline", R_OK) < 0 && ERRNO_IS_PRIVILEGE(errno))
+                return log_tests_skipped("can't read /proc/cmdline");
+
         test_is_wanted_print(true);
         test_is_wanted_print(false); /* run twice to test caching */
         test_is_wanted();
index 1286f11e4eb35b4af2a48cfcac6c178f27cd2786..f4843374ea320fef3c5f77189b4d43b39e9aaeef 100644 (file)
@@ -22,7 +22,9 @@ static int test_default_memory_low(void) {
         if (r == -ENOMEDIUM)
                 return log_tests_skipped("cgroupfs not available");
 
-        assert_se(set_unit_path(get_testdata_dir()) >= 0);
+        _cleanup_free_ char *unit_dir = NULL;
+        assert_se(get_testdata_dir("units", &unit_dir) >= 0);
+        assert_se(set_unit_path(unit_dir) >= 0);
         assert_se(runtime_dir = setup_fake_runtime_dir());
         r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m);
         if (IN_SET(r, -EPERM, -EACCES)) {
@@ -70,7 +72,7 @@ static int test_default_memory_low(void) {
          *
          * 3. dml-discard.slice sets DefaultMemoryLow= with no rvalue. As such,
          *    dml-discard-empty.service should end up with a value of 0.
-         *    dml-discard-explicit-ml.service sets MemoryLow=70, and as such should have that override the
+         *    dml-discard-set-ml.service sets MemoryLow=15, and as such should have that override the
          *    reset DefaultMemoryLow value. dml-discard.slice should still have an eventual memory.low of 50.
          *
          *                           ┌───────────┐
index 83b5156c11eb640ecb684acdc4ff436274a9493e..eff586a2e1d5497f5e1e34e7204219dfce1a352b 100644 (file)
@@ -4,6 +4,7 @@
 #include "build.h"
 #include "cgroup-util.h"
 #include "dirent-util.h"
+#include "errno-util.h"
 #include "fd-util.h"
 #include "format-util.h"
 #include "parse-util.h"
@@ -22,7 +23,7 @@ static void check_p_d_u(const char *path, int code, const char *result) {
         int r;
 
         r = cg_path_decode_unit(path, &unit);
-        printf("%s: %s → %s %d expected %s %d\n", __func__, path, unit, r, result, code);
+        printf("%s: %s → %s %d expected %s %d\n", __func__, path, unit, r, strnull(result), code);
         assert_se(r == code);
         assert_se(streq_ptr(unit, result));
 }
@@ -44,7 +45,7 @@ static void check_p_g_u(const char *path, int code, const char *result) {
         int r;
 
         r = cg_path_get_unit(path, &unit);
-        printf("%s: %s → %s %d expected %s %d\n", __func__, path, unit, r, result, code);
+        printf("%s: %s → %s %d expected %s %d\n", __func__, path, unit, r, strnull(result), code);
         assert_se(r == code);
         assert_se(streq_ptr(unit, result));
 }
@@ -68,7 +69,7 @@ static void check_p_g_u_u(const char *path, int code, const char *result) {
         int r;
 
         r = cg_path_get_user_unit(path, &unit);
-        printf("%s: %s → %s %d expected %s %d\n", __func__, path, unit, r, result, code);
+        printf("%s: %s → %s %d expected %s %d\n", __func__, path, unit, r, strnull(result), code);
         assert_se(r == code);
         assert_se(streq_ptr(unit, result));
 }
@@ -369,7 +370,7 @@ static void test_cg_get_keyed_attribute(void) {
         int i, r;
 
         r = cg_get_keyed_attribute("cpu", "/init.scope", "no_such_file", STRV_MAKE("no_such_attr"), &val);
-        if (r == -ENOMEDIUM) {
+        if (r == -ENOMEDIUM || ERRNO_IS_PRIVILEGE(r)) {
                 log_info_errno(r, "Skipping most of %s, /sys/fs/cgroup not accessible: %m", __func__);
                 return;
         }
@@ -383,22 +384,42 @@ static void test_cg_get_keyed_attribute(void) {
         }
 
         assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat", STRV_MAKE("no_such_attr"), &val) == -ENXIO);
+        assert_se(cg_get_keyed_attribute_graceful("cpu", "/init.scope", "cpu.stat", STRV_MAKE("no_such_attr"), &val) == 0);
         assert_se(val == NULL);
 
         assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat", STRV_MAKE("usage_usec"), &val) == 0);
+        val = mfree(val);
+
+        assert_se(cg_get_keyed_attribute_graceful("cpu", "/init.scope", "cpu.stat", STRV_MAKE("usage_usec"), &val) == 1);
         log_info("cpu /init.scope cpu.stat [usage_usec] → \"%s\"", val);
 
         assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat", STRV_MAKE("usage_usec", "no_such_attr"), vals3) == -ENXIO);
+        assert_se(cg_get_keyed_attribute_graceful("cpu", "/init.scope", "cpu.stat", STRV_MAKE("usage_usec", "no_such_attr"), vals3) == 1);
+        assert(vals3[0] && !vals3[1]);
+        free(vals3[0]);
 
         assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat", STRV_MAKE("usage_usec", "usage_usec"), vals3) == -ENXIO);
+        assert_se(cg_get_keyed_attribute_graceful("cpu", "/init.scope", "cpu.stat", STRV_MAKE("usage_usec", "usage_usec"), vals3) == 1);
+        assert(vals3[0] && !vals3[1]);
+        free(vals3[0]);
 
         assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat",
                                          STRV_MAKE("usage_usec", "user_usec", "system_usec"), vals3) == 0);
+        for (i = 0; i < 3; i++)
+                free(vals3[i]);
+
+        assert_se(cg_get_keyed_attribute_graceful("cpu", "/init.scope", "cpu.stat",
+                                         STRV_MAKE("usage_usec", "user_usec", "system_usec"), vals3) == 3);
         log_info("cpu /init.scope cpu.stat [usage_usec user_usec system_usec] → \"%s\", \"%s\", \"%s\"",
                  vals3[0], vals3[1], vals3[2]);
 
         assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat",
                                          STRV_MAKE("system_usec", "user_usec", "usage_usec"), vals3a) == 0);
+        for (i = 0; i < 3; i++)
+                free(vals3a[i]);
+
+        assert_se(cg_get_keyed_attribute_graceful("cpu", "/init.scope", "cpu.stat",
+                                         STRV_MAKE("system_usec", "user_usec", "usage_usec"), vals3a) == 3);
         log_info("cpu /init.scope cpu.stat [system_usec user_usec usage_usec] → \"%s\", \"%s\", \"%s\"",
                  vals3a[0], vals3a[1], vals3a[2]);
 
index f16cb291f222f8527d10c0515b2233db5641c2a7..4fbb186f52cfe622d1fe8af724d167fcf5c58f7c 100644 (file)
 
 #include "cgroup-setup.h"
 #include "cgroup-util.h"
+#include "errno-util.h"
 #include "path-util.h"
 #include "process-util.h"
 #include "string-util.h"
-#include "util.h"
+#include "tests.h"
 
-int main(int argc, char *argv[]) {
-        char *path;
+static void test_cg_split_spec(void) {
         char *c, *p;
 
-        assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, "/test-a") == 0);
-        assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, "/test-a") == 0);
-        assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, "/test-b") == 0);
-        assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, "/test-b/test-c") == 0);
-        assert_se(cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, "/test-b", 0) == 0);
+        log_info("/* %s */", __func__);
+
+        assert_se(cg_split_spec("foobar:/", &c, &p) == 0);
+        assert_se(streq(c, "foobar"));
+        assert_se(streq(p, "/"));
+        c = mfree(c);
+        p = mfree(p);
+
+        assert_se(cg_split_spec("foobar:", &c, &p) == 0);
+        c = mfree(c);
+        p = mfree(p);
+
+        assert_se(cg_split_spec("foobar:asdfd", &c, &p) < 0);
+        assert_se(cg_split_spec(":///", &c, &p) < 0);
+        assert_se(cg_split_spec(":", &c, &p) < 0);
+        assert_se(cg_split_spec("", &c, &p) < 0);
+        assert_se(cg_split_spec("fo/obar:/", &c, &p) < 0);
+
+        assert_se(cg_split_spec("/", &c, &p) >= 0);
+        assert_se(c == NULL);
+        assert_se(streq(p, "/"));
+        p = mfree(p);
+
+        assert_se(cg_split_spec("foo", &c, &p) >= 0);
+        assert_se(streq(c, "foo"));
+        assert_se(p == NULL);
+        c = mfree(c);
+}
+
+static void test_cg_create(void) {
+        log_info("/* %s */", __func__);
+        int r;
+
+        r = cg_unified_cached(false);
+        if (r < 0) {
+                log_info_errno(r, "Skipping %s: %m", __func__);
+                return;
+        }
+
+        _cleanup_free_ char *here = NULL;
+        assert_se(cg_pid_get_path_shifted(0, NULL, &here) >= 0);
+
+        const char *test_a = prefix_roota(here, "/test-a"),
+                   *test_b = prefix_roota(here, "/test-b"),
+                   *test_c = prefix_roota(here, "/test-b/test-c"),
+                   *test_d = prefix_roota(here, "/test-b/test-d");
+        char *path;
+
+        log_info("Paths for test:\n%s\n%s", test_a, test_b);
+
+        r = cg_create(SYSTEMD_CGROUP_CONTROLLER, test_a);
+        if (IN_SET(r, -EPERM, -EACCES, -EROFS)) {
+                log_info_errno(r, "Skipping %s: %m", __func__);
+                return;
+        }
+
+        assert_se(r == 1);
+        assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, test_a) == 0);
+        assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, test_b) == 1);
+        assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, test_c) == 1);
+        assert_se(cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, test_b, 0) == 0);
 
         assert_se(cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, getpid_cached(), &path) == 0);
-        assert_se(streq(path, "/test-b"));
+        assert_se(streq(path, test_b));
         free(path);
 
-        assert_se(cg_attach(SYSTEMD_CGROUP_CONTROLLER, "/test-a", 0) == 0);
+        assert_se(cg_attach(SYSTEMD_CGROUP_CONTROLLER, test_a, 0) == 0);
 
         assert_se(cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, getpid_cached(), &path) == 0);
-        assert_se(path_equal(path, "/test-a"));
+        assert_se(path_equal(path, test_a));
         free(path);
 
-        assert_se(cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, "/test-b/test-d", 0) == 0);
+        assert_se(cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, test_d, 0) == 1);
 
         assert_se(cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, getpid_cached(), &path) == 0);
-        assert_se(path_equal(path, "/test-b/test-d"));
+        assert_se(path_equal(path, test_d));
         free(path);
 
-        assert_se(cg_get_path(SYSTEMD_CGROUP_CONTROLLER, "/test-b/test-d", NULL, &path) == 0);
-        assert_se(path_equal(path, "/sys/fs/cgroup/systemd/test-b/test-d"));
+        assert_se(cg_get_path(SYSTEMD_CGROUP_CONTROLLER, test_d, NULL, &path) == 0);
+        log_debug("test_d: %s", path);
+        const char *full_d;
+        if (cg_all_unified())
+                full_d = strjoina("/sys/fs/cgroup", test_d);
+        else if (cg_hybrid_unified())
+                full_d = strjoina("/sys/fs/cgroup/unified", test_d);
+        else
+                full_d = strjoina("/sys/fs/cgroup/systemd", test_d);
+        assert_se(path_equal(path, full_d));
         free(path);
 
-        assert_se(cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, "/test-a") > 0);
-        assert_se(cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, "/test-b") > 0);
-        assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a") > 0);
-        assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b") == 0);
+        assert_se(cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, test_a) > 0);
+        assert_se(cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, test_b) > 0);
+        assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, test_a) > 0);
+        assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, test_b) == 0);
 
-        assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a", 0, 0, NULL, NULL, NULL) == 0);
-        assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", 0, 0, NULL, NULL, NULL) > 0);
+        assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, test_a, 0, 0, NULL, NULL, NULL) == 0);
+        assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, test_b, 0, 0, NULL, NULL, NULL) > 0);
 
-        assert_se(cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", SYSTEMD_CGROUP_CONTROLLER, "/test-a", 0) > 0);
+        assert_se(cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, test_b, SYSTEMD_CGROUP_CONTROLLER, test_a, 0) > 0);
 
-        assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a") == 0);
-        assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b") > 0);
+        assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, test_a) == 0);
+        assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, test_b) > 0);
 
-        assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a", 0, 0, NULL, NULL, NULL) > 0);
-        assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", 0, 0, NULL, NULL, NULL) == 0);
+        assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, test_a, 0, 0, NULL, NULL, NULL) > 0);
+        assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, test_b, 0, 0, NULL, NULL, NULL) == 0);
 
-        cg_trim(SYSTEMD_CGROUP_CONTROLLER, "/", false);
+        cg_trim(SYSTEMD_CGROUP_CONTROLLER, test_b, false);
 
-        assert_se(cg_rmdir(SYSTEMD_CGROUP_CONTROLLER, "/test-b") < 0);
-        assert_se(cg_rmdir(SYSTEMD_CGROUP_CONTROLLER, "/test-a") >= 0);
-
-        assert_se(cg_split_spec("foobar:/", &c, &p) == 0);
-        assert_se(streq(c, "foobar"));
-        assert_se(streq(p, "/"));
-        free(c);
-        free(p);
-
-        assert_se(cg_split_spec("foobar:", &c, &p) < 0);
-        assert_se(cg_split_spec("foobar:asdfd", &c, &p) < 0);
-        assert_se(cg_split_spec(":///", &c, &p) < 0);
-        assert_se(cg_split_spec(":", &c, &p) < 0);
-        assert_se(cg_split_spec("", &c, &p) < 0);
-        assert_se(cg_split_spec("fo/obar:/", &c, &p) < 0);
+        assert_se(cg_rmdir(SYSTEMD_CGROUP_CONTROLLER, test_b) == 0);
+        assert_se(cg_rmdir(SYSTEMD_CGROUP_CONTROLLER, test_a) < 0);
+        assert_se(cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, test_a, SYSTEMD_CGROUP_CONTROLLER, here, 0) > 0);
+        assert_se(cg_rmdir(SYSTEMD_CGROUP_CONTROLLER, test_a) == 0);
+}
 
-        assert_se(cg_split_spec("/", &c, &p) >= 0);
-        assert_se(c == NULL);
-        assert_se(streq(p, "/"));
-        free(p);
+int main(int argc, char *argv[]) {
+        test_setup_logging(LOG_DEBUG);
 
-        assert_se(cg_split_spec("foo", &c, &p) >= 0);
-        assert_se(streq(c, "foo"));
-        assert_se(p == NULL);
-        free(c);
+        test_cg_split_spec();
+        test_cg_create();
 
         return 0;
 }
index 0b7dd8764f0d941e40df4c4bcc0b6d636d146932..f7b3dd5e00509b873bd0d397e43eab7211747b41 100644 (file)
@@ -84,9 +84,7 @@ static int parse_argv(int argc, char *argv[]) {
 static int run(int argc, char **argv) {
         int r;
 
-        log_show_color(true);
-        log_parse_environment();
-        log_open();
+        log_setup_cli();
 
         r = parse_argv(argc, argv);
         if (r <= 0)
index dc310939a5aeaa0907e3523089abac84d517bc66..271d4655463a183b97addc37b04aa671ee21b073 100644 (file)
@@ -60,14 +60,14 @@ static void test_clock_is_localtime_system(void) {
         int r;
         r = clock_is_localtime(NULL);
 
-        if (access("/etc/adjtime", F_OK) == 0) {
-                log_info("/etc/adjtime exists, clock_is_localtime() == %i", r);
+        if (access("/etc/adjtime", R_OK) == 0) {
+                log_info("/etc/adjtime is readable, clock_is_localtime() == %i", r);
                 /* if /etc/adjtime exists we expect some answer, no error or
                  * crash */
                 assert_se(IN_SET(r, 0, 1));
         } else
                 /* default is UTC if there is no /etc/adjtime */
-                assert_se(r == 0);
+                assert_se(r == 0 || ERRNO_IS_PRIVILEGE(r));
 }
 
 int main(int argc, char *argv[]) {
index 28b5b780c35b75677ee038d6fc8dfd340418f6f0..ddf2e669c03c4f98fa3e795beb1c9069e1cd02bb 100644 (file)
@@ -15,6 +15,7 @@
 #include "condition.h"
 #include "cpu-set-util.h"
 #include "efi-loader.h"
+#include "errno-util.h"
 #include "hostname-util.h"
 #include "id128-util.h"
 #include "ima-util.h"
@@ -38,82 +39,87 @@ static void test_condition_test_path(void) {
 
         condition = condition_new(CONDITION_PATH_EXISTS, "/bin/sh", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition));
+        assert_se(condition_test(condition, environ));
         condition_free(condition);
 
         condition = condition_new(CONDITION_PATH_EXISTS, "/bin/s?", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) == 0);
+        assert_se(condition_test(condition, environ) == 0);
         condition_free(condition);
 
         condition = condition_new(CONDITION_PATH_EXISTS_GLOB, "/bin/s?", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) > 0);
+        assert_se(condition_test(condition, environ) > 0);
         condition_free(condition);
 
         condition = condition_new(CONDITION_PATH_EXISTS_GLOB, "/bin/s?", false, true);
         assert_se(condition);
-        assert_se(condition_test(condition) == 0);
+        assert_se(condition_test(condition, environ) == 0);
         condition_free(condition);
 
         condition = condition_new(CONDITION_PATH_EXISTS, "/thiscertainlywontexist", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) == 0);
+        assert_se(condition_test(condition, environ) == 0);
         condition_free(condition);
 
         condition = condition_new(CONDITION_PATH_EXISTS, "/thiscertainlywontexist", false, true);
         assert_se(condition);
-        assert_se(condition_test(condition) > 0);
+        assert_se(condition_test(condition, environ) > 0);
         condition_free(condition);
 
         condition = condition_new(CONDITION_PATH_IS_DIRECTORY, "/bin", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) > 0);
+        assert_se(condition_test(condition, environ) > 0);
         condition_free(condition);
 
         condition = condition_new(CONDITION_DIRECTORY_NOT_EMPTY, "/bin", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) > 0);
+        assert_se(condition_test(condition, environ) > 0);
         condition_free(condition);
 
         condition = condition_new(CONDITION_FILE_NOT_EMPTY, "/bin/sh", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) > 0);
+        assert_se(condition_test(condition, environ) > 0);
         condition_free(condition);
 
         condition = condition_new(CONDITION_FILE_IS_EXECUTABLE, "/bin/sh", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) > 0);
+        assert_se(condition_test(condition, environ) > 0);
         condition_free(condition);
 
         condition = condition_new(CONDITION_FILE_IS_EXECUTABLE, "/etc/passwd", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) == 0);
+        assert_se(condition_test(condition, environ) == 0);
         condition_free(condition);
 
         condition = condition_new(CONDITION_PATH_IS_MOUNT_POINT, "/proc", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) > 0);
+        assert_se(condition_test(condition, environ) > 0);
         condition_free(condition);
 
         condition = condition_new(CONDITION_PATH_IS_MOUNT_POINT, "/", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) > 0);
+        assert_se(condition_test(condition, environ) > 0);
         condition_free(condition);
 
         condition = condition_new(CONDITION_PATH_IS_MOUNT_POINT, "/bin", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) == 0);
+        assert_se(condition_test(condition, environ) == 0);
         condition_free(condition);
 
         condition = condition_new(CONDITION_PATH_IS_READ_WRITE, "/tmp", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) > 0);
+        assert_se(condition_test(condition, environ) > 0);
+        condition_free(condition);
+
+        condition = condition_new(CONDITION_PATH_IS_ENCRYPTED, "/sys", false, false);
+        assert_se(condition);
+        assert_se(condition_test(condition, environ) == 0);
         condition_free(condition);
 
         condition = condition_new(CONDITION_PATH_IS_SYMBOLIC_LINK, "/dev/stdout", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) > 0);
+        assert_se(condition_test(condition, environ) > 0);
         condition_free(condition);
 }
 
@@ -133,12 +139,12 @@ static void test_condition_test_control_group_controller(void) {
         /* Invalid controllers are ignored */
         condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, "thisisnotarealcontroller", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) > 0);
+        assert_se(condition_test(condition, environ) > 0);
         condition_free(condition);
 
         condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, "thisisnotarealcontroller", false, true);
         assert_se(condition);
-        assert_se(condition_test(condition) == 0);
+        assert_se(condition_test(condition, environ) == 0);
         condition_free(condition);
 
         assert_se(cg_mask_supported(&system_mask) >= 0);
@@ -151,23 +157,23 @@ static void test_condition_test_control_group_controller(void) {
                         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) > 0);
+                        assert_se(condition_test(condition, environ) > 0);
                         condition_free(condition);
 
                         condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, local_controller_name, false, true);
                         assert_se(condition);
-                        assert_se(condition_test(condition) == 0);
+                        assert_se(condition_test(condition, environ) == 0);
                         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) == 0);
+                        assert_se(condition_test(condition, environ) == 0);
                         condition_free(condition);
 
                         condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, local_controller_name, false, true);
                         assert_se(condition);
-                        assert_se(condition_test(condition) > 0);
+                        assert_se(condition_test(condition, environ) > 0);
                         condition_free(condition);
                 }
         }
@@ -177,12 +183,12 @@ static void test_condition_test_control_group_controller(void) {
 
         condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, strempty(controller_name), false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) > 0);
+        assert_se(condition_test(condition, environ) > 0);
         condition_free(condition);
 
         condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, strempty(controller_name), false, true);
         assert_se(condition);
-        assert_se(condition_test(condition) == 0);
+        assert_se(condition_test(condition, environ) == 0);
         condition_free(condition);
 }
 
@@ -191,17 +197,17 @@ static void test_condition_test_ac_power(void) {
 
         condition = condition_new(CONDITION_AC_POWER, "true", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) == on_ac_power());
+        assert_se(condition_test(condition, environ) == on_ac_power());
         condition_free(condition);
 
         condition = condition_new(CONDITION_AC_POWER, "false", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) != on_ac_power());
+        assert_se(condition_test(condition, environ) != on_ac_power());
         condition_free(condition);
 
         condition = condition_new(CONDITION_AC_POWER, "false", false, true);
         assert_se(condition);
-        assert_se(condition_test(condition) == on_ac_power());
+        assert_se(condition_test(condition, environ) == on_ac_power());
         condition_free(condition);
 }
 
@@ -218,17 +224,17 @@ static void test_condition_test_host(void) {
 
         condition = condition_new(CONDITION_HOST, sid, false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) > 0);
+        assert_se(condition_test(condition, environ) > 0);
         condition_free(condition);
 
         condition = condition_new(CONDITION_HOST, "garbage value jjjjjjjjjjjjjj", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) == 0);
+        assert_se(condition_test(condition, environ) == 0);
         condition_free(condition);
 
         condition = condition_new(CONDITION_HOST, sid, false, true);
         assert_se(condition);
-        assert_se(condition_test(condition) == 0);
+        assert_se(condition_test(condition, environ) == 0);
         condition_free(condition);
 
         hostname = gethostname_malloc();
@@ -240,7 +246,7 @@ static void test_condition_test_host(void) {
         else {
                 condition = condition_new(CONDITION_HOST, hostname, false, false);
                 assert_se(condition);
-                assert_se(condition_test(condition) > 0);
+                assert_se(condition_test(condition, environ) > 0);
                 condition_free(condition);
         }
 }
@@ -258,31 +264,35 @@ static void test_condition_test_architecture(void) {
 
         condition = condition_new(CONDITION_ARCHITECTURE, sa, false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) > 0);
+        assert_se(condition_test(condition, environ) > 0);
         condition_free(condition);
 
         condition = condition_new(CONDITION_ARCHITECTURE, "garbage value", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) == 0);
+        assert_se(condition_test(condition, environ) == 0);
         condition_free(condition);
 
         condition = condition_new(CONDITION_ARCHITECTURE, sa, false, true);
         assert_se(condition);
-        assert_se(condition_test(condition) == 0);
+        assert_se(condition_test(condition, environ) == 0);
         condition_free(condition);
 }
 
 static void test_condition_test_kernel_command_line(void) {
         Condition *condition;
+        int r;
 
         condition = condition_new(CONDITION_KERNEL_COMMAND_LINE, "thisreallyshouldntbeonthekernelcommandline", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) == 0);
+        r = condition_test(condition, environ);
+        if (ERRNO_IS_PRIVILEGE(r))
+                return;
+        assert_se(r == 0);
         condition_free(condition);
 
         condition = condition_new(CONDITION_KERNEL_COMMAND_LINE, "andthis=neither", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) == 0);
+        assert_se(condition_test(condition, environ) == 0);
         condition_free(condition);
 }
 
@@ -293,26 +303,26 @@ static void test_condition_test_kernel_version(void) {
 
         condition = condition_new(CONDITION_KERNEL_VERSION, "*thisreallyshouldntbeinthekernelversion*", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) == 0);
+        assert_se(condition_test(condition, environ) == 0);
         condition_free(condition);
 
         condition = condition_new(CONDITION_KERNEL_VERSION, "*", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) > 0);
+        assert_se(condition_test(condition, environ) > 0);
         condition_free(condition);
 
         /* An artificially empty condition. It evaluates to true, but normally
          * such condition cannot be created, because the condition list is reset instead. */
         condition = condition_new(CONDITION_KERNEL_VERSION, "", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) > 0);
+        assert_se(condition_test(condition, environ) > 0);
         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) > 0);
+        assert_se(condition_test(condition, environ) > 0);
         condition_free(condition);
 
         strshorten(u.release, 4);
@@ -320,79 +330,79 @@ static void test_condition_test_kernel_version(void) {
 
         condition = condition_new(CONDITION_KERNEL_VERSION, u.release, false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) > 0);
+        assert_se(condition_test(condition, environ) > 0);
         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) > 0);
+        assert_se(condition_test(condition, environ) > 0);
         condition_free(condition);
 
         condition = condition_new(CONDITION_KERNEL_VERSION, ">0.1.2", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) > 0);
+        assert_se(condition_test(condition, environ) > 0);
         condition_free(condition);
 
         condition = condition_new(CONDITION_KERNEL_VERSION, "'>0.1.2' '<9.0.0'", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) > 0);
+        assert_se(condition_test(condition, environ) > 0);
         condition_free(condition);
 
         condition = condition_new(CONDITION_KERNEL_VERSION, "> 0.1.2 < 9.0.0", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) == -EINVAL);
+        assert_se(condition_test(condition, environ) == -EINVAL);
         condition_free(condition);
 
         condition = condition_new(CONDITION_KERNEL_VERSION, ">", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) == -EINVAL);
+        assert_se(condition_test(condition, environ) == -EINVAL);
         condition_free(condition);
 
         condition = condition_new(CONDITION_KERNEL_VERSION, ">= 0.1.2", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) > 0);
+        assert_se(condition_test(condition, environ) > 0);
         condition_free(condition);
 
         condition = condition_new(CONDITION_KERNEL_VERSION, "< 0.1.2", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) == 0);
+        assert_se(condition_test(condition, environ) == 0);
         condition_free(condition);
 
         condition = condition_new(CONDITION_KERNEL_VERSION, "<= 0.1.2", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) == 0);
+        assert_se(condition_test(condition, environ) == 0);
         condition_free(condition);
 
         condition = condition_new(CONDITION_KERNEL_VERSION, "= 0.1.2", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) == 0);
+        assert_se(condition_test(condition, environ) == 0);
         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) > 0);
+        assert_se(condition_test(condition, environ) > 0);
         condition_free(condition);
 
         condition = condition_new(CONDITION_KERNEL_VERSION, "<= 4711.8.15", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) > 0);
+        assert_se(condition_test(condition, environ) > 0);
         condition_free(condition);
 
         condition = condition_new(CONDITION_KERNEL_VERSION, "= 4711.8.15", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) == 0);
+        assert_se(condition_test(condition, environ) == 0);
         condition_free(condition);
 
         condition = condition_new(CONDITION_KERNEL_VERSION, "> 4711.8.15", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) == 0);
+        assert_se(condition_test(condition, environ) == 0);
         condition_free(condition);
 
         condition = condition_new(CONDITION_KERNEL_VERSION, ">= 4711.8.15", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) == 0);
+        assert_se(condition_test(condition, environ) == 0);
         condition_free(condition);
 
         assert_se(uname(&u) >= 0);
@@ -400,31 +410,31 @@ static void test_condition_test_kernel_version(void) {
         v = strjoina(">=", u.release);
         condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) > 0);
+        assert_se(condition_test(condition, environ) > 0);
         condition_free(condition);
 
         v = strjoina("=  ", u.release);
         condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) > 0);
+        assert_se(condition_test(condition, environ) > 0);
         condition_free(condition);
 
         v = strjoina("<=", u.release);
         condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) > 0);
+        assert_se(condition_test(condition, environ) > 0);
         condition_free(condition);
 
         v = strjoina("> ", u.release);
         condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) == 0);
+        assert_se(condition_test(condition, environ) == 0);
         condition_free(condition);
 
         v = strjoina("<   ", u.release);
         condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) == 0);
+        assert_se(condition_test(condition, environ) == 0);
         condition_free(condition);
 }
 
@@ -433,12 +443,12 @@ static void test_condition_test_null(void) {
 
         condition = condition_new(CONDITION_NULL, NULL, false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) > 0);
+        assert_se(condition_test(condition, environ) > 0);
         condition_free(condition);
 
         condition = condition_new(CONDITION_NULL, NULL, false, true);
         assert_se(condition);
-        assert_se(condition_test(condition) == 0);
+        assert_se(condition_test(condition, environ) == 0);
         condition_free(condition);
 }
 
@@ -447,42 +457,42 @@ static void test_condition_test_security(void) {
 
         condition = condition_new(CONDITION_SECURITY, "garbage oifdsjfoidsjoj", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) == 0);
+        assert_se(condition_test(condition, environ) == 0);
         condition_free(condition);
 
         condition = condition_new(CONDITION_SECURITY, "selinux", false, true);
         assert_se(condition);
-        assert_se(condition_test(condition) != mac_selinux_use());
+        assert_se(condition_test(condition, environ) != mac_selinux_use());
         condition_free(condition);
 
         condition = condition_new(CONDITION_SECURITY, "apparmor", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) == mac_apparmor_use());
+        assert_se(condition_test(condition, environ) == mac_apparmor_use());
         condition_free(condition);
 
         condition = condition_new(CONDITION_SECURITY, "tomoyo", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) == mac_tomoyo_use());
+        assert_se(condition_test(condition, environ) == mac_tomoyo_use());
         condition_free(condition);
 
         condition = condition_new(CONDITION_SECURITY, "ima", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) == use_ima());
+        assert_se(condition_test(condition, environ) == use_ima());
         condition_free(condition);
 
         condition = condition_new(CONDITION_SECURITY, "smack", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) == mac_smack_use());
+        assert_se(condition_test(condition, environ) == mac_smack_use());
         condition_free(condition);
 
         condition = condition_new(CONDITION_SECURITY, "audit", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) == use_audit());
+        assert_se(condition_test(condition, environ) == use_audit());
         condition_free(condition);
 
         condition = condition_new(CONDITION_SECURITY, "uefi-secureboot", false, false);
         assert_se(condition);
-        assert_se(condition_test(condition) == is_efi_secure_boot());
+        assert_se(condition_test(condition, environ) == is_efi_secure_boot());
         condition_free(condition);
 }
 
@@ -505,28 +515,30 @@ static void test_condition_test_virtualization(void) {
 
         condition = condition_new(CONDITION_VIRTUALIZATION, "garbage oifdsjfoidsjoj", false, false);
         assert_se(condition);
-        r = condition_test(condition);
+        r = condition_test(condition, environ);
+        if (ERRNO_IS_PRIVILEGE(r))
+                return;
         log_info("ConditionVirtualization=garbage → %i", r);
         assert_se(r == 0);
         condition_free(condition);
 
         condition = condition_new(CONDITION_VIRTUALIZATION, "container", false, false);
         assert_se(condition);
-        r = condition_test(condition);
+        r = condition_test(condition, environ);
         log_info("ConditionVirtualization=container → %i", r);
         assert_se(r == !!detect_container());
         condition_free(condition);
 
         condition = condition_new(CONDITION_VIRTUALIZATION, "vm", false, false);
         assert_se(condition);
-        r = condition_test(condition);
+        r = condition_test(condition, environ);
         log_info("ConditionVirtualization=vm → %i", r);
         assert_se(r == (detect_vm() && !detect_container()));
         condition_free(condition);
 
         condition = condition_new(CONDITION_VIRTUALIZATION, "private-users", false, false);
         assert_se(condition);
-        r = condition_test(condition);
+        r = condition_test(condition, environ);
         log_info("ConditionVirtualization=private-users → %i", r);
         assert_se(r == !!running_in_userns());
         condition_free(condition);
@@ -547,7 +559,7 @@ static void test_condition_test_virtualization(void) {
 
                 condition = condition_new(CONDITION_VIRTUALIZATION, virt, false, false);
                 assert_se(condition);
-                r = condition_test(condition);
+                r = condition_test(condition, environ);
                 log_info("ConditionVirtualization=%s → %i", virt, r);
                 assert_se(r >= 0);
                 condition_free(condition);
@@ -562,7 +574,7 @@ static void test_condition_test_user(void) {
 
         condition = condition_new(CONDITION_USER, "garbage oifdsjfoidsjoj", false, false);
         assert_se(condition);
-        r = condition_test(condition);
+        r = condition_test(condition, environ);
         log_info("ConditionUser=garbage → %i", r);
         assert_se(r == 0);
         condition_free(condition);
@@ -570,7 +582,7 @@ static void test_condition_test_user(void) {
         assert_se(asprintf(&uid, "%"PRIu32, UINT32_C(0xFFFF)) > 0);
         condition = condition_new(CONDITION_USER, uid, false, false);
         assert_se(condition);
-        r = condition_test(condition);
+        r = condition_test(condition, environ);
         log_info("ConditionUser=%s → %i", uid, r);
         assert_se(r == 0);
         condition_free(condition);
@@ -579,7 +591,7 @@ static void test_condition_test_user(void) {
         assert_se(asprintf(&uid, "%u", (unsigned)getuid()) > 0);
         condition = condition_new(CONDITION_USER, uid, false, false);
         assert_se(condition);
-        r = condition_test(condition);
+        r = condition_test(condition, environ);
         log_info("ConditionUser=%s → %i", uid, r);
         assert_se(r > 0);
         condition_free(condition);
@@ -588,7 +600,7 @@ static void test_condition_test_user(void) {
         assert_se(asprintf(&uid, "%u", (unsigned)getuid()+1) > 0);
         condition = condition_new(CONDITION_USER, uid, false, false);
         assert_se(condition);
-        r = condition_test(condition);
+        r = condition_test(condition, environ);
         log_info("ConditionUser=%s → %i", uid, r);
         assert_se(r == 0);
         condition_free(condition);
@@ -598,7 +610,7 @@ static void test_condition_test_user(void) {
         assert_se(username);
         condition = condition_new(CONDITION_USER, username, false, false);
         assert_se(condition);
-        r = condition_test(condition);
+        r = condition_test(condition, environ);
         log_info("ConditionUser=%s → %i", username, r);
         assert_se(r > 0);
         condition_free(condition);
@@ -607,14 +619,14 @@ static void test_condition_test_user(void) {
         username = (char*)(geteuid() == 0 ? NOBODY_USER_NAME : "root");
         condition = condition_new(CONDITION_USER, username, false, false);
         assert_se(condition);
-        r = condition_test(condition);
+        r = condition_test(condition, environ);
         log_info("ConditionUser=%s → %i", username, r);
         assert_se(r == 0);
         condition_free(condition);
 
         condition = condition_new(CONDITION_USER, "@system", false, false);
         assert_se(condition);
-        r = condition_test(condition);
+        r = condition_test(condition, environ);
         log_info("ConditionUser=@system → %i", r);
         if (uid_is_system(getuid()) || uid_is_system(geteuid()))
                 assert_se(r > 0);
@@ -633,7 +645,7 @@ static void test_condition_test_group(void) {
         assert_se(0 < asprintf(&gid, "%u", UINT32_C(0xFFFF)));
         condition = condition_new(CONDITION_GROUP, gid, false, false);
         assert_se(condition);
-        r = condition_test(condition);
+        r = condition_test(condition, environ);
         log_info("ConditionGroup=%s → %i", gid, r);
         assert_se(r == 0);
         condition_free(condition);
@@ -642,7 +654,7 @@ static void test_condition_test_group(void) {
         assert_se(0 < asprintf(&gid, "%u", getgid()));
         condition = condition_new(CONDITION_GROUP, gid, false, false);
         assert_se(condition);
-        r = condition_test(condition);
+        r = condition_test(condition, environ);
         log_info("ConditionGroup=%s → %i", gid, r);
         assert_se(r > 0);
         condition_free(condition);
@@ -661,7 +673,7 @@ static void test_condition_test_group(void) {
                 assert_se(0 < asprintf(&gid, "%u", gids[i]));
                 condition = condition_new(CONDITION_GROUP, gid, false, false);
                 assert_se(condition);
-                r = condition_test(condition);
+                r = condition_test(condition, environ);
                 log_info("ConditionGroup=%s → %i", gid, r);
                 assert_se(r > 0);
                 condition_free(condition);
@@ -672,7 +684,7 @@ static void test_condition_test_group(void) {
                 assert_se(groupname);
                 condition = condition_new(CONDITION_GROUP, groupname, false, false);
                 assert_se(condition);
-                r = condition_test(condition);
+                r = condition_test(condition, environ);
                 log_info("ConditionGroup=%s → %i", groupname, r);
                 assert_se(r > 0);
                 condition_free(condition);
@@ -683,7 +695,7 @@ static void test_condition_test_group(void) {
         assert_se(0 < asprintf(&gid, "%u", max_gid+1));
         condition = condition_new(CONDITION_GROUP, gid, false, false);
         assert_se(condition);
-        r = condition_test(condition);
+        r = condition_test(condition, environ);
         log_info("ConditionGroup=%s → %i", gid, r);
         assert_se(r == 0);
         condition_free(condition);
@@ -692,7 +704,7 @@ static void test_condition_test_group(void) {
         groupname = (char*)(getegid() == 0 ? NOBODY_GROUP_NAME : "root");
         condition = condition_new(CONDITION_GROUP, groupname, false, false);
         assert_se(condition);
-        r = condition_test(condition);
+        r = condition_test(condition, environ);
         log_info("ConditionGroup=%s → %i", groupname, r);
         assert_se(r == 0);
         condition_free(condition);
@@ -707,7 +719,7 @@ static void test_condition_test_cpus_one(const char *s, bool result) {
         condition = condition_new(CONDITION_CPUS, s, false, false);
         assert_se(condition);
 
-        r = condition_test(condition);
+        r = condition_test(condition, environ);
         assert_se(r >= 0);
         assert_se(r == result);
         condition_free(condition);
@@ -768,7 +780,7 @@ static void test_condition_test_memory_one(const char *s, bool result) {
         condition = condition_new(CONDITION_MEMORY, s, false, false);
         assert_se(condition);
 
-        r = condition_test(condition);
+        r = condition_test(condition, environ);
         assert_se(r >= 0);
         assert_se(r == result);
         condition_free(condition);
@@ -819,6 +831,34 @@ static void test_condition_test_memory(void) {
         t = mfree(t);
 }
 
+static void test_condition_test_environment_one(const char *s, bool result) {
+        Condition *condition;
+        int r;
+
+        log_debug("%s=%s", condition_type_to_string(CONDITION_ENVIRONMENT), s);
+
+        condition = condition_new(CONDITION_ENVIRONMENT, s, false, false);
+        assert_se(condition);
+
+        r = condition_test(condition, environ);
+        assert_se(r >= 0);
+        assert_se(r == result);
+        condition_free(condition);
+}
+
+static void test_condition_test_environment(void) {
+        assert_se(setenv("EXISTINGENVVAR", "foo", false) >= 0);
+
+        test_condition_test_environment_one("MISSINGENVVAR", false);
+        test_condition_test_environment_one("MISSINGENVVAR=foo", false);
+        test_condition_test_environment_one("MISSINGENVVAR=", false);
+
+        test_condition_test_environment_one("EXISTINGENVVAR", true);
+        test_condition_test_environment_one("EXISTINGENVVAR=foo", true);
+        test_condition_test_environment_one("EXISTINGENVVAR=bar", false);
+        test_condition_test_environment_one("EXISTINGENVVAR=", false);
+}
+
 int main(int argc, char *argv[]) {
         test_setup_logging(LOG_DEBUG);
 
@@ -837,6 +877,7 @@ int main(int argc, char *argv[]) {
         test_condition_test_control_group_controller();
         test_condition_test_cpus();
         test_condition_test_memory();
+        test_condition_test_environment();
 
         return 0;
 }
index 510b1de72d104aea2e38af2c6d5117b3098f3208..07edc17f92ef6d79b00540115f873f8e24f92ab8 100644 (file)
@@ -335,13 +335,17 @@ static void test_config_parse(unsigned i, const char *s) {
                          ConfigItemLookup lookup,
                          const void *table,
                          ConfigParseFlags flags,
-                         void *userdata)
+                         void *userdata,
+                         usec_t *ret_mtime)
         */
 
         r = config_parse(NULL, name, f,
-                         "Section\0-NoWarnSection\0",
+                         "Section\0"
+                         "-NoWarnSection\0",
                          config_item_table_lookup, items,
-                         CONFIG_PARSE_WARN, NULL);
+                         CONFIG_PARSE_WARN,
+                         NULL,
+                         NULL);
 
         switch (i) {
         case 0 ... 4:
index 68905c662d96ec5bde742c3582a557833b68eb7a..0e8d68795153bacae92515e4cf0a06a2665e9d81 100644 (file)
@@ -78,8 +78,8 @@ static void test_copy_file_fd(void) {
 }
 
 static void test_copy_tree(void) {
-        char original_dir[] = "/var/tmp/test-copy_tree/";
-        char copy_dir[] = "/var/tmp/test-copy_tree-copy/";
+        char original_dir[] = "/tmp/test-copy_tree/";
+        char copy_dir[] = "/tmp/test-copy_tree-copy/";
         char **files = STRV_MAKE("file", "dir1/file", "dir1/dir2/file", "dir1/dir2/dir3/dir4/dir5/file");
         char **links = STRV_MAKE("link", "file",
                                  "link2", "dir1/file");
@@ -270,7 +270,7 @@ static void test_copy_atomic(void) {
         q = strjoina(p, "/fstab");
 
         r = copy_file_atomic("/etc/fstab", q, 0644, 0, 0, COPY_REFLINK);
-        if (r == -ENOENT)
+        if (r == -ENOENT || ERRNO_IS_PRIVILEGE(r))
                 return;
 
         assert_se(copy_file_atomic("/etc/fstab", q, 0644, 0, 0, COPY_REFLINK) == -EEXIST);
diff --git a/src/test/test-coredump-util.c b/src/test/test-coredump-util.c
new file mode 100644 (file)
index 0000000..14a7800
--- /dev/null
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "alloc-util.h"
+#include "coredump-util.h"
+#include "macro.h"
+#include "tests.h"
+
+static void test_coredump_filter_to_from_string(void) {
+        log_info("/* %s */", __func__);
+
+        for (CoredumpFilter i = 0; i < _COREDUMP_FILTER_MAX; i++) {
+                const char *n;
+
+                assert_se(n = coredump_filter_to_string(i));
+                log_info("0x%x\t%s", 1<<i, n);
+                assert_se(coredump_filter_from_string(n) == i);
+
+                uint64_t f;
+                assert_se(coredump_filter_mask_from_string(n, &f) == 0);
+                assert_se(f == 1u << i);
+        }
+}
+
+static void test_coredump_filter_mask_from_string(void) {
+        log_info("/* %s */", __func__);
+
+        uint64_t f;
+        assert_se(coredump_filter_mask_from_string("default", &f) == 0);
+        assert_se(f == COREDUMP_FILTER_MASK_DEFAULT);
+
+        assert_se(coredump_filter_mask_from_string("  default\tdefault\tdefault  ", &f) == 0);
+        assert_se(f == COREDUMP_FILTER_MASK_DEFAULT);
+
+        assert_se(coredump_filter_mask_from_string("defaulta", &f) < 0);
+        assert_se(coredump_filter_mask_from_string("default defaulta default", &f) < 0);
+        assert_se(coredump_filter_mask_from_string("default default defaulta", &f) < 0);
+
+        assert_se(coredump_filter_mask_from_string("private-anonymous default", &f) == 0);
+        assert_se(f == COREDUMP_FILTER_MASK_DEFAULT);
+
+        assert_se(coredump_filter_mask_from_string("shared-file-backed shared-dax", &f) == 0);
+        assert_se(f == (1 << COREDUMP_FILTER_SHARED_FILE_BACKED |
+                        1 << COREDUMP_FILTER_SHARED_DAX));
+
+        assert_se(coredump_filter_mask_from_string("private-file-backed private-dax 0xF", &f) == 0);
+        assert_se(f == (1 << COREDUMP_FILTER_PRIVATE_FILE_BACKED |
+                        1 << COREDUMP_FILTER_PRIVATE_DAX |
+                        0xF));
+
+        assert_se(coredump_filter_mask_from_string("11", &f) == 0);
+        assert_se(f == 0x11);
+
+        assert_se(coredump_filter_mask_from_string("0x1101", &f) == 0);
+        assert_se(f == 0x1101);
+
+        assert_se(coredump_filter_mask_from_string("0", &f) == 0);
+        assert_se(f == 0);
+
+        assert_se(coredump_filter_mask_from_string("all", &f) == 0);
+        assert_se(FLAGS_SET(f, (1 << COREDUMP_FILTER_PRIVATE_ANONYMOUS |
+                                1 << COREDUMP_FILTER_SHARED_ANONYMOUS |
+                                1 << COREDUMP_FILTER_PRIVATE_FILE_BACKED |
+                                1 << COREDUMP_FILTER_SHARED_FILE_BACKED |
+                                1 << COREDUMP_FILTER_ELF_HEADERS |
+                                1 << COREDUMP_FILTER_PRIVATE_HUGE |
+                                1 << COREDUMP_FILTER_SHARED_HUGE |
+                                1 << COREDUMP_FILTER_PRIVATE_DAX |
+                                1 << COREDUMP_FILTER_SHARED_DAX)));
+}
+
+int main(int argc, char **argv) {
+        test_setup_logging(LOG_INFO);
+
+        test_coredump_filter_to_from_string();
+        test_coredump_filter_mask_from_string();
+
+        return 0;
+}
index d991fe52004df26415cb2891d90604fbfa1c8897..038484e475971821df3ccbafd0eea41b602ba731 100644 (file)
@@ -20,7 +20,6 @@ int main(int argc, char *argv[]) {
         f = prefix_roota(p, "/run");
         assert_se(mkdir(f, 0755) >= 0);
 
-        f = prefix_roota(p, "/run/systemd");
         assert_se(make_inaccessible_nodes(f, 1, 1) >= 0);
 
         f = prefix_roota(p, "/run/systemd/inaccessible/reg");
index a1ccf605b11c6580dcea23903da1badf4d41a37e..13ff8add5da3e970de3d0d0458b7e249e2722be1 100644 (file)
@@ -28,7 +28,7 @@ int main(int argc, char *argv[]) {
                 return EXIT_FAILURE;
         }
 
-        r = dissect_image(d->fd, NULL, 0, DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_RELAX_VAR_CHECK, &m);
+        r = dissect_image(d->fd, NULL, 0, NULL, DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_RELAX_VAR_CHECK, &m);
         if (r < 0) {
                 log_error_errno(r, "Failed to dissect image: %m");
                 return EXIT_FAILURE;
index b8351141fe2afc1a8719435ccc2d4026cdfa32b9..6465151b271d2511052348e836d97efbe1428e7f 100644 (file)
@@ -26,8 +26,11 @@ int main(int argc, char *argv[]) {
                 return log_tests_skipped("cgroupfs not available");
 
         /* prepare the test */
-        assert_se(set_unit_path(get_testdata_dir()) >= 0);
+        _cleanup_free_ char *unit_dir = NULL;
+        assert_se(get_testdata_dir("units", &unit_dir) >= 0);
+        assert_se(set_unit_path(unit_dir) >= 0);
         assert_se(runtime_dir = setup_fake_runtime_dir());
+
         r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m);
         if (manager_errno_skip_test(r))
                 return log_tests_skipped_errno(r, "manager_new");
index f6aae1eb1828d8432648cd043a1f036bc73127c6..699747fcc3cae4945d6c5b7b97d34cd650d4f345 100644 (file)
@@ -142,31 +142,42 @@ static void test_shell_maybe_quote_one(const char *s,
 static void test_shell_maybe_quote(void) {
 
         test_shell_maybe_quote_one("", ESCAPE_BACKSLASH, "");
+        test_shell_maybe_quote_one("", ESCAPE_BACKSLASH_ONELINE, "");
         test_shell_maybe_quote_one("", ESCAPE_POSIX, "");
         test_shell_maybe_quote_one("\\", ESCAPE_BACKSLASH, "\"\\\\\"");
+        test_shell_maybe_quote_one("\\", ESCAPE_BACKSLASH_ONELINE, "\"\\\\\"");
         test_shell_maybe_quote_one("\\", ESCAPE_POSIX, "$'\\\\'");
         test_shell_maybe_quote_one("\"", ESCAPE_BACKSLASH, "\"\\\"\"");
+        test_shell_maybe_quote_one("\"", ESCAPE_BACKSLASH_ONELINE, "\"\\\"\"");
         test_shell_maybe_quote_one("\"", ESCAPE_POSIX, "$'\"'");
         test_shell_maybe_quote_one("foobar", ESCAPE_BACKSLASH, "foobar");
+        test_shell_maybe_quote_one("foobar", ESCAPE_BACKSLASH_ONELINE, "foobar");
         test_shell_maybe_quote_one("foobar", ESCAPE_POSIX, "foobar");
         test_shell_maybe_quote_one("foo bar", ESCAPE_BACKSLASH, "\"foo bar\"");
+        test_shell_maybe_quote_one("foo bar", ESCAPE_BACKSLASH_ONELINE, "\"foo bar\"");
         test_shell_maybe_quote_one("foo bar", ESCAPE_POSIX, "$'foo bar'");
         test_shell_maybe_quote_one("foo\tbar", ESCAPE_BACKSLASH, "\"foo\tbar\"");
+        test_shell_maybe_quote_one("foo\tbar", ESCAPE_BACKSLASH_ONELINE, "\"foo\\tbar\"");
         test_shell_maybe_quote_one("foo\tbar", ESCAPE_POSIX, "$'foo\\tbar'");
         test_shell_maybe_quote_one("foo\nbar", ESCAPE_BACKSLASH, "\"foo\nbar\"");
+        test_shell_maybe_quote_one("foo\nbar", ESCAPE_BACKSLASH_ONELINE, "\"foo\\nbar\"");
         test_shell_maybe_quote_one("foo\nbar", ESCAPE_POSIX, "$'foo\\nbar'");
         test_shell_maybe_quote_one("foo \"bar\" waldo", ESCAPE_BACKSLASH, "\"foo \\\"bar\\\" waldo\"");
+        test_shell_maybe_quote_one("foo \"bar\" waldo", ESCAPE_BACKSLASH_ONELINE, "\"foo \\\"bar\\\" waldo\"");
         test_shell_maybe_quote_one("foo \"bar\" waldo", ESCAPE_POSIX, "$'foo \"bar\" waldo'");
         test_shell_maybe_quote_one("foo$bar", ESCAPE_BACKSLASH, "\"foo\\$bar\"");
+        test_shell_maybe_quote_one("foo$bar", ESCAPE_BACKSLASH_ONELINE, "\"foo\\$bar\"");
         test_shell_maybe_quote_one("foo$bar", ESCAPE_POSIX, "$'foo$bar'");
 
         /* Note that current users disallow control characters, so this "test"
          * is here merely to establish current behaviour. If control characters
          * were allowed, they should be quoted, i.e. \001 should become \\001. */
         test_shell_maybe_quote_one("a\nb\001", ESCAPE_BACKSLASH, "\"a\nb\001\"");
+        test_shell_maybe_quote_one("a\nb\001", ESCAPE_BACKSLASH_ONELINE, "\"a\\nb\001\"");
         test_shell_maybe_quote_one("a\nb\001", ESCAPE_POSIX, "$'a\\nb\001'");
 
         test_shell_maybe_quote_one("foo!bar", ESCAPE_BACKSLASH, "\"foo!bar\"");
+        test_shell_maybe_quote_one("foo!bar", ESCAPE_BACKSLASH_ONELINE, "\"foo!bar\"");
         test_shell_maybe_quote_one("foo!bar", ESCAPE_POSIX, "$'foo!bar'");
 }
 
index 3168411d8bf1cf8abc498a2d9ec0152f65c90620..f5d640a69037976e2e6513cb047faf2bcae05865 100644 (file)
@@ -115,6 +115,9 @@ static void test_execute_directory(bool gather_stdout) {
         assert_se(chmod(masked2e, 0755) == 0);
         assert_se(chmod(mask2e, 0755) == 0);
 
+        if (access(name, X_OK) < 0 && ERRNO_IS_PRIVILEGE(errno))
+                return;
+
         if (gather_stdout)
                 execute_directories(dirs, DEFAULT_TIMEOUT_USEC, ignore_stdout, ignore_stdout_args, NULL, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS);
         else
@@ -183,6 +186,9 @@ static void test_execution_order(void) {
         assert_se(chmod(override, 0755) == 0);
         assert_se(chmod(masked, 0755) == 0);
 
+        if (access(name, X_OK) < 0 && ERRNO_IS_PRIVILEGE(errno))
+                return;
+
         execute_directories(dirs, DEFAULT_TIMEOUT_USEC, ignore_stdout, ignore_stdout_args, NULL, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS);
 
         assert_se(read_full_file(output, &contents, NULL) >= 0);
@@ -265,6 +271,9 @@ static void test_stdout_gathering(void) {
         assert_se(chmod(name2, 0755) == 0);
         assert_se(chmod(name3, 0755) == 0);
 
+        if (access(name, X_OK) < 0 && ERRNO_IS_PRIVILEGE(errno))
+                return;
+
         r = execute_directories(dirs, DEFAULT_TIMEOUT_USEC, gather_stdout, args, NULL, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS);
         assert_se(r >= 0);
 
@@ -331,6 +340,9 @@ static void test_environment_gathering(void) {
         r = setenv("PATH", "no-sh-built-in-path", 1);
         assert_se(r >= 0);
 
+        if (access(name, X_OK) < 0 && ERRNO_IS_PRIVILEGE(errno))
+                return;
+
         r = execute_directories(dirs, DEFAULT_TIMEOUT_USEC, gather_environment, args, NULL, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS);
         assert_se(r >= 0);
 
@@ -395,6 +407,9 @@ static void test_error_catching(void) {
         assert_se(chmod(name2, 0755) == 0);
         assert_se(chmod(name3, 0755) == 0);
 
+        if (access(name, X_OK) < 0 && ERRNO_IS_PRIVILEGE(errno))
+                return;
+
         r = execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, NULL, NULL, EXEC_DIR_NONE);
 
         /* we should exit with the error code of the first script that failed */
index 4e0fd7d5b4b7ce3da24518358419869304f54d3e..9ca062021657219847b67fc5aa4685fb2c3cb539 100644 (file)
@@ -467,7 +467,7 @@ static void test_exec_restrictnamespaces(Manager *m) {
         test(__func__, m, "exec-restrictnamespaces-no.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
         test(__func__, m, "exec-restrictnamespaces-yes.service", 1, CLD_EXITED);
         test(__func__, m, "exec-restrictnamespaces-mnt.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
-        test(__func__, m, "exec-restrictnamespaces-mnt-blacklist.service", 1, CLD_EXITED);
+        test(__func__, m, "exec-restrictnamespaces-mnt-deny-list.service", 1, CLD_EXITED);
         test(__func__, m, "exec-restrictnamespaces-merge-and.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
         test(__func__, m, "exec-restrictnamespaces-merge-or.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
         test(__func__, m, "exec-restrictnamespaces-merge-all.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
@@ -806,7 +806,6 @@ static int run_tests(UnitFileScope scope, const test_entry tests[], char **patte
 
 int main(int argc, char *argv[]) {
         _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL;
-        _cleanup_free_ char *test_execute_path = NULL;
 
         static const test_entry user_tests[] = {
                 entry(test_exec_basic),
@@ -867,6 +866,7 @@ int main(int argc, char *argv[]) {
         (void) unsetenv("LOGNAME");
         (void) unsetenv("SHELL");
         (void) unsetenv("HOME");
+        (void) unsetenv("TMPDIR");
 
         can_unshare = have_namespaces();
 
@@ -878,9 +878,10 @@ int main(int argc, char *argv[]) {
         if (r == -ENOMEDIUM)
                 return log_tests_skipped("cgroupfs not available");
 
+        _cleanup_free_ char *unit_dir = NULL;
+        assert_se(get_testdata_dir("test-execute/", &unit_dir) >= 0);
+        assert_se(set_unit_path(unit_dir) >= 0);
         assert_se(runtime_dir = setup_fake_runtime_dir());
-        test_execute_path = path_join(get_testdata_dir(), "test-execute");
-        assert_se(set_unit_path(test_execute_path) >= 0);
 
         /* Unset VAR1, VAR2 and VAR3 which are used in the PassEnvironment test
          * cases, otherwise (and if they are present in the environment),
index 23c7d370d4eb1a86fae3e8e500482631ec412d47..95dcd4fdb180af9ef9c397d92e0e0f1719641b57 100644 (file)
@@ -15,6 +15,8 @@
 #include "io-util.h"
 #include "parse-util.h"
 #include "process-util.h"
+#include "rm-rf.h"
+#include "socket-util.h"
 #include "string-util.h"
 #include "strv.h"
 #include "tests.h"
@@ -444,20 +446,23 @@ static void test_write_string_file_verify(void) {
         _cleanup_free_ char *buf = NULL, *buf2 = NULL;
         int r;
 
-        assert_se(read_one_line_file("/proc/cmdline", &buf) >= 0);
+        r = read_one_line_file("/proc/version", &buf);
+        if (ERRNO_IS_PRIVILEGE(r))
+                return;
+        assert_se(r >= 0);
         assert_se(buf2 = strjoin(buf, "\n"));
 
-        r = write_string_file("/proc/cmdline", buf, 0);
+        r = write_string_file("/proc/version", buf, 0);
         assert_se(IN_SET(r, -EACCES, -EIO));
-        r = write_string_file("/proc/cmdline", buf2, 0);
+        r = write_string_file("/proc/version", buf2, 0);
         assert_se(IN_SET(r, -EACCES, -EIO));
 
-        assert_se(write_string_file("/proc/cmdline", buf, WRITE_STRING_FILE_VERIFY_ON_FAILURE) == 0);
-        assert_se(write_string_file("/proc/cmdline", buf2, WRITE_STRING_FILE_VERIFY_ON_FAILURE) == 0);
+        assert_se(write_string_file("/proc/version", buf, WRITE_STRING_FILE_VERIFY_ON_FAILURE) == 0);
+        assert_se(write_string_file("/proc/version", buf2, WRITE_STRING_FILE_VERIFY_ON_FAILURE) == 0);
 
-        r = write_string_file("/proc/cmdline", buf, WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_AVOID_NEWLINE);
+        r = write_string_file("/proc/version", buf, WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_AVOID_NEWLINE);
         assert_se(IN_SET(r, -EACCES, -EIO));
-        assert_se(write_string_file("/proc/cmdline", buf2, WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_AVOID_NEWLINE) == 0);
+        assert_se(write_string_file("/proc/version", buf2, WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_AVOID_NEWLINE) == 0);
 }
 
 static void test_load_env_file_pairs(void) {
@@ -630,8 +635,7 @@ static void test_tempfn(void) {
 static const char chars[] =
         "Aąę„”\n루\377";
 
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wtype-limits"
+DISABLE_WARNING_TYPE_LIMITS;
 
 static void test_fgetc(void) {
         _cleanup_fclose_ FILE *f = NULL;
@@ -662,7 +666,7 @@ static void test_fgetc(void) {
         assert_se(safe_fgetc(f, &c) == 0);
 }
 
-#pragma GCC diagnostic pop
+REENABLE_WARNING;
 
 static const char buffer[] =
         "Some test data\n"
@@ -757,7 +761,7 @@ static void test_read_line3(void) {
         _cleanup_free_ char *line = NULL;
         int r;
 
-        f = fopen("/proc/cmdline", "re");
+        f = fopen("/proc/uptime", "re");
         if (!f && IN_SET(errno, ENOENT, EPERM))
                 return;
         assert_se(f);
@@ -840,6 +844,53 @@ static void test_read_nul_string(void) {
         assert_se(read_nul_string(f, LONG_LINE_MAX, &s) == 0 && streq_ptr(s, ""));
 }
 
+static void test_read_full_file_socket(void) {
+        _cleanup_(rm_rf_physical_and_freep) char *z = NULL;
+        _cleanup_close_ int listener = -1;
+        _cleanup_free_ char *data = NULL;
+        union sockaddr_union sa;
+        const char *j;
+        size_t size;
+        pid_t pid;
+        int r;
+
+        log_info("/* %s */", __func__);
+
+        listener = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
+        assert_se(listener >= 0);
+
+        assert_se(mkdtemp_malloc(NULL, &z) >= 0);
+        j = strjoina(z, "/socket");
+
+        assert_se(sockaddr_un_set_path(&sa.un, j) >= 0);
+
+        assert_se(bind(listener, &sa.sa, SOCKADDR_UN_LEN(sa.un)) >= 0);
+        assert_se(listen(listener, 1) >= 0);
+
+        r = safe_fork("(server)", FORK_DEATHSIG|FORK_LOG, &pid);
+        assert_se(r >= 0);
+        if (r == 0) {
+                _cleanup_close_ int rfd = -1;
+                /* child */
+
+                rfd = accept4(listener, NULL, 0, SOCK_CLOEXEC);
+                assert_se(rfd >= 0);
+
+#define TEST_STR "This is a test\nreally."
+
+                assert_se(write(rfd, TEST_STR, strlen(TEST_STR)) == strlen(TEST_STR));
+                _exit(EXIT_SUCCESS);
+        }
+
+        assert_se(read_full_file_full(AT_FDCWD, j, 0, &data, &size) == -ENXIO);
+        assert_se(read_full_file_full(AT_FDCWD, j, READ_FULL_FILE_CONNECT_SOCKET, &data, &size) >= 0);
+        assert_se(size == strlen(TEST_STR));
+        assert_se(streq(data, TEST_STR));
+
+        assert_se(wait_for_terminate_and_check("(server)", pid, WAIT_LOG) >= 0);
+#undef TEST_STR
+}
+
 int main(int argc, char *argv[]) {
         test_setup_logging(LOG_DEBUG);
 
@@ -865,6 +916,7 @@ int main(int argc, char *argv[]) {
         test_read_line3();
         test_read_line4();
         test_read_nul_string();
+        test_read_full_file_socket();
 
         return 0;
 }
index d97ccfda3bcc7c59adaa01aef67fbe649b01a5a9..dfea70ca27390a4bcfa504f1d695ef8c2db2a175 100644 (file)
@@ -670,7 +670,7 @@ static void test_unlinkat_deallocate(void) {
         assert_se(st.st_blocks > 0);
         assert_se(st.st_nlink == 1);
 
-        assert_se(unlinkat_deallocate(AT_FDCWD, p, 0) >= 0);
+        assert_se(unlinkat_deallocate(AT_FDCWD, p, UNLINK_ERASE) >= 0);
 
         assert_se(fstat(fd, &st) >= 0);
         assert_se(IN_SET(st.st_size, 0, 6)); /* depending on whether hole punching worked the size will be 6
@@ -846,6 +846,37 @@ static void test_chmod_and_chown_unsafe(void) {
         assert_se(S_ISLNK(st.st_mode));
 }
 
+static void test_path_is_encrypted_one(const char *p, int expect) {
+        int r;
+
+        r = path_is_encrypted(p);
+        if (r == -ENOENT) /* This might fail, if btrfs is used and we run in a container. In that case we
+                           * cannot resolve the device node paths that BTRFS_IOC_DEV_INFO returns, because
+                           * the device nodes are unlikely to exist in the container. But if we can't stat()
+                           * them we cannot determine the dev_t of them, and thus cannot figure out if they
+                           * are enrypted. Hence let's just ignore ENOENT here. */
+                return;
+        assert_se(r >= 0);
+
+        log_info("%s encrypted: %s", p, yes_no(r));
+
+        assert_se(expect < 0 || ((r > 0) == (expect > 0)));
+}
+
+static void test_path_is_encrypted(void) {
+        int booted = sd_booted(); /* If this is run in build environments such as koji, /dev might be a
+                                   * reguar fs. Don't assume too much if not running under systemd. */
+
+        log_info("/* %s (sd_booted=%d)*/", __func__, booted);
+
+        test_path_is_encrypted_one("/home", -1);
+        test_path_is_encrypted_one("/var", -1);
+        test_path_is_encrypted_one("/", -1);
+        test_path_is_encrypted_one("/proc", false);
+        test_path_is_encrypted_one("/sys", false);
+        test_path_is_encrypted_one("/dev", booted > 0 ? false : -1);
+}
+
 int main(int argc, char *argv[]) {
         test_setup_logging(LOG_INFO);
 
@@ -864,6 +895,7 @@ int main(int argc, char *argv[]) {
         test_rename_noreplace();
         test_chmod_and_chown();
         test_chmod_and_chown_unsafe();
+        test_path_is_encrypted();
 
         return 0;
 }
index 57cf89ff53427b5845e77075dcb8f1e3c6d34b9a..09fe71f20556fe1713bec175e35376eef5b6ded7 100644 (file)
@@ -251,7 +251,7 @@ static void test_hashmap_put(void) {
 
         log_info("/* %s */", __func__);
 
-        assert_se(hashmap_ensure_allocated(&m, &string_hash_ops) >= 0);
+        assert_se(hashmap_ensure_allocated(&m, &string_hash_ops) == 1);
         assert_se(m);
 
         valid_hashmap_put = hashmap_put(m, "key 1", val1);
@@ -451,18 +451,20 @@ static void test_hashmap_remove_and_replace(void) {
 }
 
 static void test_hashmap_ensure_allocated(void) {
-        Hashmap *m;
-        int valid_hashmap;
+        _cleanup_hashmap_free_ Hashmap *m = NULL;
+        int r;
 
         log_info("/* %s */", __func__);
 
-        m = hashmap_new(&string_hash_ops);
+        r = hashmap_ensure_allocated(&m, &string_hash_ops);
+        assert_se(r == 1);
 
-        valid_hashmap = hashmap_ensure_allocated(&m, &string_hash_ops);
-        assert_se(valid_hashmap == 0);
+        r = hashmap_ensure_allocated(&m, &string_hash_ops);
+        assert_se(r == 0);
 
-        assert_se(m);
-        hashmap_free(m);
+        /* different hash ops shouldn't matter at this point */
+        r = hashmap_ensure_allocated(&m, &trivial_hash_ops);
+        assert_se(r == 0);
 }
 
 static void test_hashmap_foreach_key(void) {
@@ -557,8 +559,7 @@ static void test_hashmap_foreach(void) {
 }
 
 static void test_hashmap_merge(void) {
-        Hashmap *m;
-        Hashmap *n;
+        Hashmap *m, *n;
         char *val1, *val2, *val3, *val4, *r;
 
         log_info("/* %s */", __func__);
@@ -572,8 +573,8 @@ static void test_hashmap_merge(void) {
         val4 = strdup("my val4");
         assert_se(val4);
 
-        n = hashmap_new(&string_hash_ops);
         m = hashmap_new(&string_hash_ops);
+        n = hashmap_new(&string_hash_ops);
 
         hashmap_put(m, "Key 1", val1);
         hashmap_put(m, "Key 2", val2);
@@ -586,8 +587,8 @@ static void test_hashmap_merge(void) {
         r = hashmap_get(m, "Key 4");
         assert_se(r && streq(r, "my val4"));
 
-        assert_se(n);
         assert_se(m);
+        assert_se(n);
         hashmap_free(n);
         hashmap_free_free(m);
 }
index 1a6e8ffa58e98c2246c44333537e4cf191e17a4e..94dbbf157692f03329fb1aedcae1c36fb30e67ff 100644 (file)
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
 #include "hashmap.h"
+#include "string-util.h"
 #include "util.h"
 
 unsigned custom_counter = 0;
@@ -109,6 +110,58 @@ static void test_iterated_cache(void) {
         assert_se(iterated_cache_free(c) == NULL);
 }
 
+static void test_hashmap_put_strdup(void) {
+        _cleanup_hashmap_free_ Hashmap *m = NULL;
+        char *s;
+
+        /* We don't have ordered_hashmap_put_strdup() yet. If it is added,
+         * these tests should be moved to test-hashmap-plain.c. */
+
+        log_info("/* %s */", __func__);
+
+        assert_se(hashmap_put_strdup(&m, "foo", "bar") == 1);
+        assert_se(hashmap_put_strdup(&m, "foo", "bar") == 0);
+        assert_se(hashmap_put_strdup(&m, "foo", "BAR") == -EEXIST);
+        assert_se(hashmap_put_strdup(&m, "foo", "bar") == 0);
+        assert_se(hashmap_contains(m, "foo"));
+
+        s = hashmap_get(m, "foo");
+        assert_se(streq(s, "bar"));
+
+        assert_se(hashmap_put_strdup(&m, "xxx", "bar") == 1);
+        assert_se(hashmap_put_strdup(&m, "xxx", "bar") == 0);
+        assert_se(hashmap_put_strdup(&m, "xxx", "BAR") == -EEXIST);
+        assert_se(hashmap_put_strdup(&m, "xxx", "bar") == 0);
+        assert_se(hashmap_contains(m, "xxx"));
+
+        s = hashmap_get(m, "xxx");
+        assert_se(streq(s, "bar"));
+}
+
+static void test_hashmap_put_strdup_null(void) {
+        _cleanup_hashmap_free_ Hashmap *m = NULL;
+        char *s;
+
+        log_info("/* %s */", __func__);
+
+        assert_se(hashmap_put_strdup(&m, "foo", "bar") == 1);
+        assert_se(hashmap_put_strdup(&m, "foo", "bar") == 0);
+        assert_se(hashmap_put_strdup(&m, "foo", NULL) == -EEXIST);
+        assert_se(hashmap_put_strdup(&m, "foo", "bar") == 0);
+        assert_se(hashmap_contains(m, "foo"));
+
+        s = hashmap_get(m, "foo");
+        assert_se(streq(s, "bar"));
+
+        assert_se(hashmap_put_strdup(&m, "xxx", NULL) == 1);
+        assert_se(hashmap_put_strdup(&m, "xxx", "bar") == -EEXIST);
+        assert_se(hashmap_put_strdup(&m, "xxx", NULL) == 0);
+        assert_se(hashmap_contains(m, "xxx"));
+
+        s = hashmap_get(m, "xxx");
+        assert_se(s == NULL);
+}
+
 int main(int argc, const char *argv[]) {
         /* This file tests in test-hashmap-plain.c, and tests in test-hashmap-ordered.c, which is generated
          * from test-hashmap-plain.c. Hashmap tests should be added to test-hashmap-plain.c, and here only if
@@ -127,6 +180,8 @@ int main(int argc, const char *argv[]) {
         test_trivial_compare_func();
         test_string_compare_func();
         test_iterated_cache();
+        test_hashmap_put_strdup();
+        test_hashmap_put_strdup_null();
 
         return 0;
 }
index ec34f9cd716b5622199de9b9894dfff1b73c8e2b..5ab82bba618e7162a172f5eefb0c0d608338d8fe 100644 (file)
@@ -140,6 +140,23 @@ static void test_read_etc_hostname(void) {
         unlink(path);
 }
 
+static void test_hostname_malloc(void) {
+        _cleanup_free_ char *h = NULL, *l = NULL;
+
+        assert_se(h = gethostname_malloc());
+        log_info("hostname_malloc: \"%s\"", h);
+
+        assert_se(l = gethostname_short_malloc());
+        log_info("hostname_short_malloc: \"%s\"", l);
+}
+
+static void test_fallback_hostname(void) {
+        if (!hostname_is_valid(FALLBACK_HOSTNAME, false)) {
+                log_error("Configured fallback hostname \"%s\" is not valid.", FALLBACK_HOSTNAME);
+                exit(EXIT_FAILURE);
+        }
+}
+
 int main(int argc, char *argv[]) {
         log_parse_environment();
         log_open();
@@ -147,6 +164,9 @@ int main(int argc, char *argv[]) {
         test_hostname_is_valid();
         test_hostname_cleanup();
         test_read_etc_hostname();
+        test_hostname_malloc();
+
+        test_fallback_hostname();
 
         return 0;
 }
index 25498916f1136ee586f51090bad15e2a851e0a4b..f309160889798e410250776f70e29112360bda2f 100644 (file)
@@ -4,6 +4,7 @@
 
 #include "alloc-util.h"
 #include "fileio.h"
+#include "fs-util.h"
 #include "install.h"
 #include "mkdir.h"
 #include "rm-rf.h"
@@ -23,6 +24,7 @@ static void test_basic_mask_and_enable(const char *root) {
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", NULL) == -ENOENT);
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", NULL) == -ENOENT);
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", NULL) == -ENOENT);
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "e.service", NULL) == -ENOENT);
 
         p = strjoina(root, "/usr/lib/systemd/system/a.service");
         assert_se(write_string_file(p,
@@ -36,26 +38,26 @@ static void test_basic_mask_and_enable(const char *root) {
         assert_se(symlink("a.service", p) >= 0);
 
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", NULL) >= 0);
-        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
 
         p = strjoina(root, "/usr/lib/systemd/system/c.service");
         assert_se(symlink("/usr/lib/systemd/system/a.service", p) >= 0);
 
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", NULL) >= 0);
-        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
 
         p = strjoina(root, "/usr/lib/systemd/system/d.service");
         assert_se(symlink("c.service", p) >= 0);
 
         /* This one is interesting, as d follows a relative, then an absolute symlink */
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", NULL) >= 0);
-        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
 
         assert_se(unit_file_mask(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0);
         assert_se(n_changes == 1);
         assert_se(changes[0].type == UNIT_FILE_SYMLINK);
         assert_se(streq(changes[0].source, "/dev/null"));
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/a.service");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/a.service");
         assert_se(streq(changes[0].path, p));
 
         unit_file_changes_free(changes, n_changes);
@@ -74,7 +76,7 @@ static void test_basic_mask_and_enable(const char *root) {
         assert_se(unit_file_unmask(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0);
         assert_se(n_changes == 1);
         assert_se(changes[0].type == UNIT_FILE_UNLINK);
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/a.service");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/a.service");
         assert_se(streq(changes[0].path, p));
         unit_file_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
@@ -83,15 +85,15 @@ static void test_basic_mask_and_enable(const char *root) {
         assert_se(n_changes == 1);
         assert_se(changes[0].type == UNIT_FILE_SYMLINK);
         assert_se(streq(changes[0].source, "/usr/lib/systemd/system/a.service"));
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/a.service");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/a.service");
         assert_se(streq(changes[0].path, p));
         unit_file_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
 
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
-        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
-        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
-        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
 
         /* Enabling it again should succeed but be a NOP */
         assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0);
@@ -102,15 +104,15 @@ static void test_basic_mask_and_enable(const char *root) {
         assert_se(unit_file_disable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0);
         assert_se(n_changes == 1);
         assert_se(changes[0].type == UNIT_FILE_UNLINK);
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/a.service");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/a.service");
         assert_se(streq(changes[0].path, p));
         unit_file_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
 
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
-        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
-        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
-        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
 
         /* Disabling a disabled unit must succeed but be a NOP */
         assert_se(unit_file_disable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0);
@@ -123,22 +125,22 @@ static void test_basic_mask_and_enable(const char *root) {
         assert_se(n_changes == 1);
         assert_se(changes[0].type == UNIT_FILE_SYMLINK);
         assert_se(streq(changes[0].source, "/usr/lib/systemd/system/a.service"));
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/a.service");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/a.service");
         assert_se(streq(changes[0].path, p));
         unit_file_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
 
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
-        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
-        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
-        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
 
         /* Let's try to reenable */
 
         assert_se(unit_file_reenable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("b.service"), &changes, &n_changes) >= 0);
         assert_se(n_changes == 2);
         assert_se(changes[0].type == UNIT_FILE_UNLINK);
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/a.service");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/a.service");
         assert_se(streq(changes[0].path, p));
         assert_se(changes[1].type == UNIT_FILE_SYMLINK);
         assert_se(streq(changes[1].source, "/usr/lib/systemd/system/a.service"));
@@ -147,9 +149,25 @@ static void test_basic_mask_and_enable(const char *root) {
         changes = NULL; n_changes = 0;
 
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
-        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
-        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
-        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
+
+        /* Test masking with relative symlinks */
+
+        p = strjoina(root, "/usr/lib/systemd/system/e.service");
+        assert_se(symlink("../../../../../../dev/null", p) >= 0);
+
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "e.service", NULL) >= 0);
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "e.service", &state) >= 0 && state == UNIT_FILE_MASKED);
+
+        assert_se(unlink(p) == 0);
+        assert_se(symlink("/usr/../dev/null", p) >= 0);
+
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "e.service", NULL) >= 0);
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "e.service", &state) >= 0 && state == UNIT_FILE_MASKED);
+
+        assert_se(unlink(p) == 0);
 }
 
 static void test_linked_units(const char *root) {
@@ -196,7 +214,7 @@ static void test_linked_units(const char *root) {
         p = strjoina(root, "/usr/lib/systemd/system/linked2.service");
         assert_se(symlink("/opt/linked2.service", p) >= 0);
 
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked3.service");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/linked3.service");
         assert_se(symlink("/opt/linked3.service", p) >= 0);
 
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", &state) == -ENOENT);
@@ -208,7 +226,7 @@ static void test_linked_units(const char *root) {
         assert_se(n_changes == 1);
         assert_se(changes[0].type == UNIT_FILE_SYMLINK);
         assert_se(streq(changes[0].source, "/opt/linked.service"));
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked.service");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/linked.service");
         assert_se(streq(changes[0].path, p));
         unit_file_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
@@ -219,7 +237,7 @@ static void test_linked_units(const char *root) {
         assert_se(unit_file_disable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("linked.service"), &changes, &n_changes) >= 0);
         assert_se(n_changes == 1);
         assert_se(changes[0].type == UNIT_FILE_UNLINK);
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked.service");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/linked.service");
         assert_se(streq(changes[0].path, p));
         unit_file_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
@@ -229,8 +247,8 @@ static void test_linked_units(const char *root) {
         /* Now, let's not just link it, but also enable it */
         assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("/opt/linked.service"), &changes, &n_changes) >= 0);
         assert_se(n_changes == 2);
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/linked.service");
-        q = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked.service");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/linked.service");
+        q = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/linked.service");
         for (i = 0 ; i < n_changes; i++) {
                 assert_se(changes[i].type == UNIT_FILE_SYMLINK);
                 assert_se(streq(changes[i].source, "/opt/linked.service"));
@@ -251,8 +269,8 @@ static void test_linked_units(const char *root) {
         /* And let's unlink it again */
         assert_se(unit_file_disable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("linked.service"), &changes, &n_changes) >= 0);
         assert_se(n_changes == 2);
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/linked.service");
-        q = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked.service");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/linked.service");
+        q = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/linked.service");
         for (i = 0; i < n_changes; i++) {
                 assert_se(changes[i].type == UNIT_FILE_UNLINK);
 
@@ -271,8 +289,8 @@ static void test_linked_units(const char *root) {
 
         assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("linked2.service"), &changes, &n_changes) >= 0);
         assert_se(n_changes == 2);
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/linked2.service");
-        q = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/linked2.service");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/linked2.service");
+        q = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/linked2.service");
         for (i = 0 ; i < n_changes; i++) {
                 assert_se(changes[i].type == UNIT_FILE_SYMLINK);
                 assert_se(streq(changes[i].source, "/opt/linked2.service"));
@@ -325,7 +343,7 @@ static void test_default(const char *root) {
         assert_se(n_changes == 1);
         assert_se(changes[0].type == UNIT_FILE_SYMLINK);
         assert_se(streq(changes[0].source, "/usr/lib/systemd/system/test-default-real.target"));
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH "/" SPECIAL_DEFAULT_TARGET);
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR "/" SPECIAL_DEFAULT_TARGET);
         assert_se(streq(changes[0].path, p));
         unit_file_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
@@ -355,7 +373,7 @@ static void test_add_dependency(const char *root) {
         assert_se(n_changes == 1);
         assert_se(changes[0].type == UNIT_FILE_SYMLINK);
         assert_se(streq(changes[0].source, "/usr/lib/systemd/system/real-add-dependency-test-service.service"));
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/real-add-dependency-test-target.target.wants/real-add-dependency-test-service.service");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/real-add-dependency-test-target.target.wants/real-add-dependency-test-service.service");
         assert_se(streq(changes[0].path, p));
         unit_file_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
@@ -386,7 +404,7 @@ static void test_template_enable(const char *root) {
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
-        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
 
@@ -396,7 +414,7 @@ static void test_template_enable(const char *root) {
         assert_se(n_changes == 1);
         assert_se(changes[0].type == UNIT_FILE_SYMLINK);
         assert_se(streq(changes[0].source, "/usr/lib/systemd/system/template@.service"));
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/template@def.service");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/template@def.service");
         assert_se(streq(changes[0].path, p));
         unit_file_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
@@ -404,7 +422,7 @@ static void test_template_enable(const char *root) {
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
-        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
 
@@ -418,7 +436,7 @@ static void test_template_enable(const char *root) {
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
-        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
 
@@ -427,7 +445,7 @@ static void test_template_enable(const char *root) {
         assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("template@foo.service"), &changes, &n_changes) >= 0);
         assert_se(changes[0].type == UNIT_FILE_SYMLINK);
         assert_se(streq(changes[0].source, "/usr/lib/systemd/system/template@.service"));
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/template@foo.service");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/template@foo.service");
         assert_se(streq(changes[0].path, p));
         unit_file_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
@@ -450,7 +468,7 @@ static void test_template_enable(const char *root) {
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@quux.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
-        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@quux.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
@@ -460,7 +478,7 @@ static void test_template_enable(const char *root) {
         assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("template-symlink@quux.service"), &changes, &n_changes) >= 0);
         assert_se(changes[0].type == UNIT_FILE_SYMLINK);
         assert_se(streq(changes[0].source, "/usr/lib/systemd/system/template@.service"));
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/template@quux.service");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/template@quux.service");
         assert_se(streq(changes[0].path, p));
         unit_file_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
@@ -469,7 +487,7 @@ static void test_template_enable(const char *root) {
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@quux.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
-        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_INDIRECT);
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@quux.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
@@ -500,25 +518,25 @@ static void test_indirect(const char *root) {
 
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirecta.service", &state) >= 0 && state == UNIT_FILE_INDIRECT);
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectb.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
-        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectc.service", &state) >= 0 && state == UNIT_FILE_INDIRECT);
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectc.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
 
         assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("indirectc.service"), &changes, &n_changes) >= 0);
         assert_se(n_changes == 1);
         assert_se(changes[0].type == UNIT_FILE_SYMLINK);
         assert_se(streq(changes[0].source, "/usr/lib/systemd/system/indirectb.service"));
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/indirectb.service");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/indirectb.service");
         assert_se(streq(changes[0].path, p));
         unit_file_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
 
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirecta.service", &state) >= 0 && state == UNIT_FILE_INDIRECT);
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectb.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
-        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectc.service", &state) >= 0 && state == UNIT_FILE_INDIRECT);
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectc.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
 
         assert_se(unit_file_disable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("indirectc.service"), &changes, &n_changes) >= 0);
         assert_se(n_changes == 1);
         assert_se(changes[0].type == UNIT_FILE_UNLINK);
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/indirectb.service");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/indirectb.service");
         assert_se(streq(changes[0].path, p));
         unit_file_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
@@ -559,7 +577,7 @@ static void test_preset_and_list(const char *root) {
         assert_se(n_changes == 1);
         assert_se(changes[0].type == UNIT_FILE_SYMLINK);
         assert_se(streq(changes[0].source, "/usr/lib/systemd/system/preset-yes.service"));
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/preset-yes.service");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/preset-yes.service");
         assert_se(streq(changes[0].path, p));
         unit_file_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
@@ -570,7 +588,7 @@ static void test_preset_and_list(const char *root) {
         assert_se(unit_file_disable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("preset-yes.service"), &changes, &n_changes) >= 0);
         assert_se(n_changes == 1);
         assert_se(changes[0].type == UNIT_FILE_UNLINK);
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/preset-yes.service");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/preset-yes.service");
         assert_se(streq(changes[0].path, p));
         unit_file_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
@@ -590,7 +608,7 @@ static void test_preset_and_list(const char *root) {
 
         assert_se(n_changes > 0);
 
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/preset-yes.service");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/preset-yes.service");
 
         for (i = 0; i < n_changes; i++) {
 
@@ -624,7 +642,7 @@ static void test_preset_and_list(const char *root) {
                         got_no = true;
                         assert_se(fl->state == UNIT_FILE_DISABLED);
                 } else
-                        assert_se(IN_SET(fl->state, UNIT_FILE_DISABLED, UNIT_FILE_STATIC, UNIT_FILE_INDIRECT));
+                        assert_se(IN_SET(fl->state, UNIT_FILE_DISABLED, UNIT_FILE_STATIC, UNIT_FILE_INDIRECT, UNIT_FILE_ALIAS));
         }
 
         unit_file_list_free(h);
@@ -655,7 +673,7 @@ static void test_revert(const char *root) {
         unit_file_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
 
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/xx.service");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/xx.service");
         assert_se(write_string_file(p, "# Empty override\n", WRITE_STRING_FILE_CREATE) >= 0);
 
         /* Revert the override file */
@@ -666,7 +684,7 @@ static void test_revert(const char *root) {
         unit_file_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
 
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/xx.service.d/dropin.conf");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/xx.service.d/dropin.conf");
         assert_se(mkdir_parents(p, 0755) >= 0);
         assert_se(write_string_file(p, "# Empty dropin\n", WRITE_STRING_FILE_CREATE) >= 0);
 
@@ -676,7 +694,7 @@ static void test_revert(const char *root) {
         assert_se(changes[0].type == UNIT_FILE_UNLINK);
         assert_se(streq(changes[0].path, p));
 
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/xx.service.d");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/xx.service.d");
         assert_se(changes[1].type == UNIT_FILE_UNLINK);
         assert_se(streq(changes[1].path, p));
         unit_file_changes_free(changes, n_changes);
@@ -715,7 +733,7 @@ static void test_preset_order(const char *root) {
         assert_se(n_changes == 1);
         assert_se(changes[0].type == UNIT_FILE_SYMLINK);
         assert_se(streq(changes[0].source, "/usr/lib/systemd/system/prefix-1.service"));
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/prefix-1.service");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/prefix-1.service");
         assert_se(streq(changes[0].path, p));
         unit_file_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
@@ -777,7 +795,7 @@ static void test_with_dropin(const char *root) {
 
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-1.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
 
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/with-dropin-2.service");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/with-dropin-2.service");
         assert_se(write_string_file(p,
                                     "[Install]\n"
                                     "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
@@ -795,7 +813,7 @@ static void test_with_dropin(const char *root) {
                                     "[Install]\n"
                                     "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
 
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/with-dropin-3.service.d/dropin.conf");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/with-dropin-3.service.d/dropin.conf");
         assert_se(mkdir_parents(p, 0755) >= 0);
         assert_se(write_string_file(p,
                                     "[Install]\n"
@@ -808,7 +826,7 @@ static void test_with_dropin(const char *root) {
                                     "[Install]\n"
                                     "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
 
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/with-dropin-4a.service.d/dropin.conf");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/with-dropin-4a.service.d/dropin.conf");
         assert_se(mkdir_parents(p, 0755) >= 0);
         assert_se(write_string_file(p,
                                     "[Install]\n"
@@ -829,9 +847,9 @@ static void test_with_dropin(const char *root) {
         assert_se(changes[1].type == UNIT_FILE_SYMLINK);
         assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-1.service"));
         assert_se(streq(changes[1].source, "/usr/lib/systemd/system/with-dropin-1.service"));
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/with-dropin-1.service");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-1.service");
         assert_se(streq(changes[0].path, p));
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/graphical.target.wants/with-dropin-1.service");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/graphical.target.wants/with-dropin-1.service");
         assert_se(streq(changes[1].path, p));
         unit_file_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
@@ -841,11 +859,11 @@ static void test_with_dropin(const char *root) {
         assert_se(n_changes == 2);
         assert_se(changes[0].type == UNIT_FILE_SYMLINK);
         assert_se(changes[1].type == UNIT_FILE_SYMLINK);
-        assert_se(streq(changes[0].source, SYSTEM_CONFIG_UNIT_PATH"/with-dropin-2.service"));
-        assert_se(streq(changes[1].source, SYSTEM_CONFIG_UNIT_PATH"/with-dropin-2.service"));
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/with-dropin-2.service");
+        assert_se(streq(changes[0].source, SYSTEM_CONFIG_UNIT_DIR"/with-dropin-2.service"));
+        assert_se(streq(changes[1].source, SYSTEM_CONFIG_UNIT_DIR"/with-dropin-2.service"));
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-2.service");
         assert_se(streq(changes[0].path, p));
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/graphical.target.wants/with-dropin-2.service");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/graphical.target.wants/with-dropin-2.service");
         assert_se(streq(changes[1].path, p));
         unit_file_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
@@ -857,9 +875,9 @@ static void test_with_dropin(const char *root) {
         assert_se(changes[1].type == UNIT_FILE_SYMLINK);
         assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-3.service"));
         assert_se(streq(changes[1].source, "/usr/lib/systemd/system/with-dropin-3.service"));
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/with-dropin-3.service");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-3.service");
         assert_se(streq(changes[0].path, p));
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/graphical.target.wants/with-dropin-3.service");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/graphical.target.wants/with-dropin-3.service");
         assert_se(streq(changes[1].path, p));
         unit_file_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
@@ -871,9 +889,9 @@ static void test_with_dropin(const char *root) {
         assert_se(changes[1].type == UNIT_FILE_SYMLINK);
         assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-4a.service"));
         assert_se(streq(changes[1].source, "/usr/lib/systemd/system/with-dropin-4b.service"));
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/with-dropin-4a.service");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-4a.service");
         assert_se(streq(changes[0].path, p));
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/with-dropin-4b.service");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-4b.service");
         assert_se(streq(changes[1].path, p));
         unit_file_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
@@ -941,9 +959,9 @@ static void test_with_dropin_template(const char *root) {
         assert_se(changes[1].type == UNIT_FILE_SYMLINK);
         assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-1@.service"));
         assert_se(streq(changes[1].source, "/usr/lib/systemd/system/with-dropin-1@.service"));
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/with-dropin-1@instance-1.service");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-1@instance-1.service");
         assert_se(streq(changes[0].path, p));
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/graphical.target.wants/with-dropin-1@instance-1.service");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/graphical.target.wants/with-dropin-1@instance-1.service");
         assert_se(streq(changes[1].path, p));
         unit_file_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
@@ -954,9 +972,9 @@ static void test_with_dropin_template(const char *root) {
         assert_se(changes[1].type == UNIT_FILE_SYMLINK);
         assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-2@.service"));
         assert_se(streq(changes[1].source, "/usr/lib/systemd/system/with-dropin-2@.service"));
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/with-dropin-2@instance-1.service");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-2@instance-1.service");
         assert_se(streq(changes[0].path, p));
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/graphical.target.wants/with-dropin-2@instance-1.service");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/graphical.target.wants/with-dropin-2@instance-1.service");
         assert_se(streq(changes[1].path, p));
         unit_file_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
@@ -965,7 +983,7 @@ static void test_with_dropin_template(const char *root) {
         assert_se(n_changes == 1);
         assert_se(changes[0].type == UNIT_FILE_SYMLINK);
         assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-2@.service"));
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/with-dropin-2@instance-2.service");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-2@instance-2.service");
         assert_se(streq(changes[0].path, p));
         unit_file_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
@@ -974,7 +992,7 @@ static void test_with_dropin_template(const char *root) {
         assert_se(n_changes == 1);
         assert_se(changes[0].type == UNIT_FILE_SYMLINK);
         assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-3@.service"));
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/with-dropin-3@instance-2.service");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-3@instance-2.service");
         assert_se(streq(changes[0].path, p));
         unit_file_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
@@ -1014,7 +1032,7 @@ static void test_preset_multiple_instances(const char *root) {
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bar0.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
         assert_se(n_changes == 1);
         assert_se(changes[0].type == UNIT_FILE_SYMLINK);
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/foo@bar0.service");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/foo@bar0.service");
         assert_se(streq(changes[0].path, p));
         unit_file_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
@@ -1022,7 +1040,7 @@ static void test_preset_multiple_instances(const char *root) {
         assert_se(unit_file_disable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("foo@bar0.service"), &changes, &n_changes) >= 0);
         assert_se(n_changes == 1);
         assert_se(changes[0].type == UNIT_FILE_UNLINK);
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/foo@bar0.service");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/foo@bar0.service");
         assert_se(streq(changes[0].path, p));
         unit_file_changes_free(changes, n_changes);
         changes = NULL; n_changes = 0;
@@ -1058,7 +1076,8 @@ static void verify_one(
         r = unit_file_verify_alias(i, alias, &alias2);
         log_info_errno(r, "alias %s ← %s: %d/%m (expected %d)%s%s%s",
                        i->name, alias, r, expected,
-                       alias2 ? " [" : "", alias2 ?: "", alias2 ? "]" : "");
+                       alias2 ? " [" : "", strempty(alias2),
+                       alias2 ? "]" : "");
         assert(r == expected);
 
         /* This is is test for "instance propagation". This propagation matters mostly for WantedBy= and
@@ -1105,8 +1124,8 @@ static void test_verify_alias(void) {
         verify_one(&bare_template, "foo.target.wants/plain.service", -EXDEV, NULL);
         verify_one(&bare_template, "foo.target.wants/plain.socket", -EXDEV, NULL);
         verify_one(&bare_template, "foo.target.wants/plain@.service", -EXDEV, NULL);
-         /* Name mistmatch: we cannot allow this, because plain@foo.service would be pulled in by foo.taget,
-          * but would not be resolvable on its own, since systemd doesn't know how to load the fragment. */
+         /* Name mismatch: we cannot allow this, because plain@foo.service would be pulled in by foo.target,
+          * but would not be resolveable on its own, since systemd doesn't know how to load the fragment. */
         verify_one(&bare_template, "foo.target.wants/plain@foo.service", -EXDEV, NULL);
         verify_one(&bare_template, "foo.target.wants/template1@foo.service", 0, NULL);
         verify_one(&bare_template, "foo.target.wants/service", -EXDEV, NULL);
@@ -1116,7 +1135,7 @@ static void test_verify_alias(void) {
         verify_one(&bare_template, "foo.target.requires/template1@inst.service", 0, NULL);
         verify_one(&bare_template, "foo.target.requires/service", -EXDEV, NULL);
         verify_one(&bare_template, "foo.target.conf/plain.service", -EXDEV, NULL);
-        verify_one(&bare_template, "FOO@.target.requires/plain@.service", -EXDEV, NULL); /* template name mistatch */
+        verify_one(&bare_template, "FOO@.target.requires/plain@.service", -EXDEV, NULL); /* template name mismatch */
         verify_one(&bare_template, "FOO@inst.target.requires/plain@.service", -EXDEV, NULL);
         verify_one(&bare_template, "FOO@inst.target.requires/plain@inst.service", -EXDEV, NULL);
         verify_one(&bare_template, "FOO@.target.requires/template1@.service", 0, NULL); /* instance propagated */
@@ -1170,7 +1189,7 @@ static void test_verify_alias(void) {
         verify_one(&inst_template, "bar.target.requires/template3@inst.service", 0, NULL);
         verify_one(&inst_template, "bar.target.requires/service", -EXDEV, NULL);
         verify_one(&inst_template, "bar.target.conf/plain.service", -EXDEV, NULL);
-        verify_one(&inst_template, "BAR@.target.requires/plain@.service", -EXDEV, NULL); /* template name mistatch */
+        verify_one(&inst_template, "BAR@.target.requires/plain@.service", -EXDEV, NULL); /* template name mismatch */
         verify_one(&inst_template, "BAR@inst.target.requires/plain@.service", -EXDEV, NULL);
         verify_one(&inst_template, "BAR@inst.target.requires/plain@inst.service", -EXDEV, NULL);
         verify_one(&inst_template, "BAR@.target.requires/template3@.service", -EXDEV, NULL); /* instance missing */
@@ -1214,7 +1233,7 @@ int main(int argc, char *argv[]) {
         p = strjoina(root, "/usr/lib/systemd/system/");
         assert_se(mkdir_p(p, 0755) >= 0);
 
-        p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/");
+        p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/");
         assert_se(mkdir_p(p, 0755) >= 0);
 
         p = strjoina(root, "/run/systemd/system/");
index 7e898735c939e5259c57e43ebe1c1b9b9637a352..488335695618429ff73ff3c7a650f4087e14c3e2 100644 (file)
@@ -25,7 +25,7 @@ static void test_basic_parsing(void) {
         _cleanup_free_ char *journal_data_path = NULL;
         int r;
 
-        journal_data_path = path_join(get_testdata_dir(), "journal-data/journal-1.txt");
+        assert_se(get_testdata_dir("journal-data/journal-1.txt", &journal_data_path) >= 0);
         imp.fd = open(journal_data_path, O_RDONLY|O_CLOEXEC);
         assert_se(imp.fd >= 0);
 
@@ -56,7 +56,7 @@ static void test_bad_input(void) {
         _cleanup_free_ char *journal_data_path = NULL;
         int r;
 
-        journal_data_path = path_join(get_testdata_dir(), "journal-data/journal-2.txt");
+        assert_se(get_testdata_dir("journal-data/journal-1.txt", &journal_data_path) >= 0);
         imp.fd = open(journal_data_path, O_RDONLY|O_CLOEXEC);
         assert_se(imp.fd >= 0);
 
index c72cd7427432c54bcb0a3ee4c65b9b7f1903588d..032619a4252ad6bbb2cff6041590f6a4cc8c8288 100644 (file)
@@ -231,10 +231,9 @@ static void test_zeroes(JsonVariant *v) {
                 assert_se(json_variant_integer(w) == 0);
                 assert_se(json_variant_unsigned(w) == 0U);
 
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wfloat-equal"
+                DISABLE_WARNING_FLOAT_EQUAL;
                 assert_se(json_variant_real(w) == 0.0L);
-#pragma GCC diagnostic pop
+                REENABLE_WARNING;
 
                 assert_se(json_variant_is_integer(w));
                 assert_se(json_variant_is_unsigned(w));
index 7de286436d3d871c57ea51d8ddb11281978b5d5a..0293d1cd0f06280a6b396bac3f9981e07642b404 100644 (file)
@@ -27,6 +27,9 @@
 #include "tmpfile-util.h"
 #include "user-util.h"
 
+/* Nontrivial value serves as a placeholder to check that parsing function (didn't) change it */
+#define CGROUP_LIMIT_DUMMY      3
+
 static int test_unit_file_get_set(void) {
         int r;
         Hashmap *h;
@@ -773,6 +776,62 @@ static void test_unit_dump_config_items(void) {
         unit_dump_config_items(stdout);
 }
 
+static void test_config_parse_memory_limit(void) {
+        /* int config_parse_memory_limit(
+                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) */
+        CGroupContext c;
+        struct limit_test {
+                const char *limit;
+                const char *value;
+                uint64_t *result;
+                uint64_t expected;
+        } limit_tests[]= {
+                { "MemoryMin",  "",             &c.memory_min,  CGROUP_LIMIT_MIN },
+                { "MemoryMin",  "0",            &c.memory_min,  CGROUP_LIMIT_MIN },
+                { "MemoryMin",  "10",           &c.memory_min,  10 },
+                { "MemoryMin",  "infinity",     &c.memory_min,  CGROUP_LIMIT_MAX },
+                { "MemoryLow",  "",             &c.memory_low,  CGROUP_LIMIT_MIN },
+                { "MemoryLow",  "0",            &c.memory_low,  CGROUP_LIMIT_MIN },
+                { "MemoryLow",  "10",           &c.memory_low,  10 },
+                { "MemoryLow",  "infinity",     &c.memory_low,  CGROUP_LIMIT_MAX },
+                { "MemoryHigh", "",             &c.memory_high, CGROUP_LIMIT_MAX },
+                { "MemoryHigh", "0",            &c.memory_high, CGROUP_LIMIT_DUMMY },
+                { "MemoryHigh", "10",           &c.memory_high, 10 },
+                { "MemoryHigh", "infinity",     &c.memory_high, CGROUP_LIMIT_MAX },
+                { "MemoryMax",  "",             &c.memory_max,  CGROUP_LIMIT_MAX },
+                { "MemoryMax",  "0",            &c.memory_max,  CGROUP_LIMIT_DUMMY },
+                { "MemoryMax",  "10",           &c.memory_max,  10 },
+                { "MemoryMax",  "infinity",     &c.memory_max,  CGROUP_LIMIT_MAX },
+        };
+        size_t i;
+        int r;
+
+        for (i = 0; i < ELEMENTSOF(limit_tests); i++) {
+                c.memory_min = CGROUP_LIMIT_DUMMY;
+                c.memory_low = CGROUP_LIMIT_DUMMY;
+                c.memory_high = CGROUP_LIMIT_DUMMY;
+                c.memory_max = CGROUP_LIMIT_DUMMY;
+                r = config_parse_memory_limit(NULL, "fake", 1, "section", 1,
+                                              limit_tests[i].limit, 1,
+                                              limit_tests[i].value, &c, NULL);
+                log_info("%s=%s\t%"PRIu64"==%"PRIu64"\n",
+                         limit_tests[i].limit, limit_tests[i].value,
+                         *limit_tests[i].result, limit_tests[i].expected);
+                assert_se(r >= 0);
+                assert_se(*limit_tests[i].result == limit_tests[i].expected);
+        }
+
+}
+
 int main(int argc, char *argv[]) {
         _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL;
         int r;
@@ -793,6 +852,7 @@ int main(int argc, char *argv[]) {
         test_config_parse_pass_environ();
         TEST_REQ_RUNNING_SYSTEMD(test_install_printf());
         test_unit_dump_config_items();
+        test_config_parse_memory_limit();
 
         return r;
 }
index f49cc6371ef0a21bd7278b520edbbdde99512761..347982dd5240f7c9e310d9a4f6ed0c2259fa91ac 100644 (file)
@@ -36,6 +36,28 @@ static void test_locale_is_valid(void) {
         assert_se(!locale_is_valid("\x01gar\x02 bage\x03"));
 }
 
+static void test_locale_is_installed(void) {
+        log_info("/* %s */", __func__);
+
+        /* Always available */
+        assert_se(locale_is_installed("POSIX") > 0);
+        assert_se(locale_is_installed("C") > 0);
+
+        /* Might, or might not be installed. */
+        assert_se(locale_is_installed("en_EN.utf8") >= 0);
+        assert_se(locale_is_installed("fr_FR.utf8") >= 0);
+        assert_se(locale_is_installed("fr_FR@euro") >= 0);
+        assert_se(locale_is_installed("fi_FI") >= 0);
+
+        /* Definitely not valid */
+        assert_se(locale_is_installed("") == 0);
+        assert_se(locale_is_installed("/usr/bin/foo") == 0);
+        assert_se(locale_is_installed("\x01gar\x02 bage\x03") == 0);
+
+        /* Definitely not installed */
+        assert_se(locale_is_installed("zz_ZZ") == 0);
+}
+
 static void test_keymaps(void) {
         _cleanup_strv_free_ char **kmaps = NULL;
         char **p;
@@ -67,7 +89,7 @@ static void test_keymaps(void) {
 
 #define dump_glyph(x) log_info(STRINGIFY(x) ": %s", special_glyph(x))
 static void dump_special_glyphs(void) {
-        assert_cc(SPECIAL_GLYPH_DEPRESSED_SMILEY + 1 == _SPECIAL_GLYPH_MAX);
+        assert_cc(SPECIAL_GLYPH_TOUCH + 1 == _SPECIAL_GLYPH_MAX);
 
         log_info("/* %s */", __func__);
 
@@ -85,6 +107,7 @@ static void dump_special_glyphs(void) {
         dump_glyph(SPECIAL_GLYPH_MU);
         dump_glyph(SPECIAL_GLYPH_CHECK_MARK);
         dump_glyph(SPECIAL_GLYPH_CROSS_MARK);
+        dump_glyph(SPECIAL_GLYPH_EXTERNAL_LINK);
         dump_glyph(SPECIAL_GLYPH_ECSTATIC_SMILEY);
         dump_glyph(SPECIAL_GLYPH_HAPPY_SMILEY);
         dump_glyph(SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY);
@@ -92,11 +115,14 @@ static void dump_special_glyphs(void) {
         dump_glyph(SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY);
         dump_glyph(SPECIAL_GLYPH_UNHAPPY_SMILEY);
         dump_glyph(SPECIAL_GLYPH_DEPRESSED_SMILEY);
+        dump_glyph(SPECIAL_GLYPH_LOCK_AND_KEY);
+        dump_glyph(SPECIAL_GLYPH_TOUCH);
 }
 
 int main(int argc, char *argv[]) {
         test_get_locales();
         test_locale_is_valid();
+        test_locale_is_installed();
         test_keymaps();
 
         dump_special_glyphs();
index f2bfc6c62bc9c3f5724e725549cb2932a1bee767..a7ad4828370f4444d784732dcf761d58182b7c42 100644 (file)
 #include "util.h"
 #include "virt.h"
 
+static void test_namespace_cleanup_tmpdir(void) {
+        {
+                _cleanup_(namespace_cleanup_tmpdirp) char *dir;
+                assert_se(dir = strdup(RUN_SYSTEMD_EMPTY));
+        }
+
+        {
+                _cleanup_(namespace_cleanup_tmpdirp) char *dir;
+                assert_se(dir = strdup("/tmp/systemd-test-namespace.XXXXXX"));
+                assert_se(mkdtemp(dir));
+        }
+}
+
 static void test_tmpdir(const char *id, const char *A, const char *B) {
         _cleanup_free_ char *a, *b;
         struct stat x, y;
         char *c, *d;
 
         assert_se(setup_tmp_dirs(id, &a, &b) == 0);
-        assert_se(startswith(a, A));
-        assert_se(startswith(b, B));
 
         assert_se(stat(a, &x) >= 0);
         assert_se(stat(b, &y) >= 0);
@@ -29,26 +40,27 @@ static void test_tmpdir(const char *id, const char *A, const char *B) {
         assert_se(S_ISDIR(x.st_mode));
         assert_se(S_ISDIR(y.st_mode));
 
-        assert_se((x.st_mode & 01777) == 0700);
-        assert_se((y.st_mode & 01777) == 0700);
-
-        c = strjoina(a, "/tmp");
-        d = strjoina(b, "/tmp");
-
-        assert_se(stat(c, &x) >= 0);
-        assert_se(stat(d, &y) >= 0);
-
-        assert_se(S_ISDIR(x.st_mode));
-        assert_se(S_ISDIR(y.st_mode));
-
-        assert_se((x.st_mode & 01777) == 01777);
-        assert_se((y.st_mode & 01777) == 01777);
-
-        assert_se(rmdir(c) >= 0);
-        assert_se(rmdir(d) >= 0);
+        if (!streq(a, RUN_SYSTEMD_EMPTY)) {
+                assert_se(startswith(a, A));
+                assert_se((x.st_mode & 01777) == 0700);
+                c = strjoina(a, "/tmp");
+                assert_se(stat(c, &x) >= 0);
+                assert_se(S_ISDIR(x.st_mode));
+                assert_se((x.st_mode & 01777) == 01777);
+                assert_se(rmdir(c) >= 0);
+                assert_se(rmdir(a) >= 0);
+        }
 
-        assert_se(rmdir(a) >= 0);
-        assert_se(rmdir(b) >= 0);
+        if (!streq(b, RUN_SYSTEMD_EMPTY)) {
+                assert_se(startswith(b, B));
+                assert_se((y.st_mode & 01777) == 0700);
+                d = strjoina(b, "/tmp");
+                assert_se(stat(d, &y) >= 0);
+                assert_se(S_ISDIR(y.st_mode));
+                assert_se((y.st_mode & 01777) == 01777);
+                assert_se(rmdir(d) >= 0);
+                assert_se(rmdir(b) >= 0);
+        }
 }
 
 static void test_netns(void) {
@@ -152,6 +164,13 @@ static void test_protect_kernel_logs(void) {
                                     PROTECT_HOME_NO,
                                     PROTECT_SYSTEM_NO,
                                     0,
+                                    NULL,
+                                    0,
+                                    NULL,
+                                    NULL,
+                                    0,
+                                    NULL,
+                                    NULL,
                                     0,
                                     NULL);
                 assert_se(r == 0);
@@ -173,6 +192,8 @@ int main(int argc, char *argv[]) {
 
         test_setup_logging(LOG_INFO);
 
+        test_namespace_cleanup_tmpdir();
+
         if (!have_namespaces()) {
                 log_tests_skipped("Don't have namespace support");
                 return EXIT_TEST_SKIP;
index cf8b08ba9be55dd5dbbf2c255514e0c622beebc7..cbc41b7a385ef57a115f46207effc0dbb17d60f7 100644 (file)
@@ -76,6 +76,13 @@ int main(int argc, char *argv[]) {
                             PROTECT_HOME_NO,
                             PROTECT_SYSTEM_NO,
                             0,
+                            NULL,
+                            0,
+                            NULL,
+                            NULL,
+                            0,
+                            NULL,
+                            NULL,
                             0,
                             NULL);
         if (r < 0) {
diff --git a/src/test/test-offline-passwd.c b/src/test/test-offline-passwd.c
new file mode 100644 (file)
index 0000000..5933ec2
--- /dev/null
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <getopt.h>
+
+#include "offline-passwd.h"
+#include "user-util.h"
+#include "format-util.h"
+#include "tests.h"
+
+static char *arg_root = NULL;
+
+static void test_resolve_one(const char *name) {
+        bool relaxed = name || arg_root;
+
+        if (!name)
+                name = "root";
+
+        log_info("/* %s(\"%s\") */", __func__, name);
+
+        _cleanup_(hashmap_freep) Hashmap *uid_cache = NULL, *gid_cache = NULL;
+        uid_t uid = UID_INVALID;
+        gid_t gid = GID_INVALID;
+        int r;
+
+        r = name_to_uid_offline(arg_root, name, &uid, &uid_cache);
+        log_info_errno(r, "name_to_uid_offline: %s → "UID_FMT": %m", name, uid);
+        assert_se(relaxed || r == 0);
+
+        r = name_to_uid_offline(arg_root, name, &uid, &uid_cache);
+        log_info_errno(r, "name_to_uid_offline: %s → "UID_FMT": %m", name, uid);
+        assert_se(relaxed || r == 0);
+
+        r = name_to_gid_offline(arg_root, name, &gid, &gid_cache);
+        log_info_errno(r, "name_to_gid_offline: %s → "GID_FMT": %m", name, gid);
+        assert_se(relaxed || r == 0);
+
+        r = name_to_gid_offline(arg_root, name, &gid, &gid_cache);
+        log_info_errno(r, "name_to_gid_offline: %s → "GID_FMT": %m", name, gid);
+        assert_se(relaxed || r == 0);
+}
+
+static int parse_argv(int argc, char *argv[]) {
+        static const struct option options[] = {
+                { "root",           required_argument,   NULL, 'r' },
+                {}
+        };
+
+        int c;
+
+        assert(argc >= 0);
+        assert(argv);
+
+        while ((c = getopt_long(argc, argv, "r:", options, NULL)) >= 0)
+                switch(c) {
+                case 'r':
+                        arg_root = optarg;
+                        break;
+
+                case '?':
+                        return -EINVAL;
+
+                default:
+                        assert_not_reached("Unhandled option");
+                }
+
+        return 0;
+}
+
+int main(int argc, char **argv) {
+        int r;
+
+        test_setup_logging(LOG_DEBUG);
+
+        r = parse_argv(argc, argv);
+        if (r < 0)
+                return r;
+
+        if (optind >= argc)
+                test_resolve_one(NULL);
+        else
+                while (optind < argc)
+                        test_resolve_one(argv[optind++]);
+
+        return 0;
+}
index 0d29fcfad25baa8cb6d755f717040e4aaa99feaf..268c54fccc78ab54cc81cc52e5331135c63b3390 100644 (file)
@@ -7,6 +7,8 @@
 #include "strv.h"
 
 static void test_set_steal_first(void) {
+        log_info("/* %s */", __func__);
+
         _cleanup_ordered_set_free_ OrderedSet *m = NULL;
         int seen[3] = {};
         char *val;
@@ -42,12 +44,18 @@ DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(item_hash_ops, void, trivial_hash_
 static void test_set_free_with_hash_ops(void) {
         OrderedSet *m;
         struct Item items[4] = {};
-        unsigned i;
+
+        log_info("/* %s */", __func__);
 
         assert_se(m = ordered_set_new(&item_hash_ops));
-        for (i = 0; i < ELEMENTSOF(items) - 1; i++)
+
+        for (size_t i = 0; i < ELEMENTSOF(items) - 1; i++)
                 assert_se(ordered_set_put(m, items + i) == 1);
 
+        for (size_t i = 0; i < ELEMENTSOF(items) - 1; i++)
+                assert_se(ordered_set_put(m, items + i) == 0);  /* We get 0 here, because we use trivial hash
+                                                                 * ops. Also see below... */
+
         m = ordered_set_free(m);
         assert_se(items[0].seen == 1);
         assert_se(items[1].seen == 1);
@@ -57,7 +65,9 @@ static void test_set_free_with_hash_ops(void) {
 
 static void test_set_put(void) {
         _cleanup_ordered_set_free_ OrderedSet *m = NULL;
-        _cleanup_free_ char **t = NULL;
+        _cleanup_free_ char **t = NULL, *str = NULL;
+
+        log_info("/* %s */", __func__);
 
         m = ordered_set_new(&string_hash_ops);
         assert_se(m);
@@ -71,6 +81,10 @@ static void test_set_put(void) {
         assert_se(ordered_set_put(m, (void*) "333") == 0);
         assert_se(ordered_set_put(m, (void*) "22") == 0);
 
+        assert_se(str = strdup("333"));
+        assert_se(ordered_set_put(m, str) == -EEXIST); /* ... and we get -EEXIST here, because we use
+                                                        * non-trivial hash ops. */
+
         assert_se(t = ordered_set_get_strv(m));
         assert_se(streq(t[0], "1"));
         assert_se(streq(t[1], "22"));
@@ -86,6 +100,8 @@ static void test_set_put_string_set(void) {
         _cleanup_free_ char **final = NULL; /* "just free" because the strings are in the set */
         void *t;
 
+        log_info("/* %s */", __func__);
+
         m = ordered_set_new(&string_hash_ops);
         assert_se(m);
 
index 62ebc9c92385aca6b7779fef7629ad99c13a21d1..b9111e9259eff6c047e19a84ab63cde7d090f17b 100644 (file)
@@ -67,15 +67,54 @@ static void test_user_and_global_paths(void) {
                 log_info("+ %s", *p);
 }
 
-static void print_generator_binary_paths(UnitFileScope scope) {
-        _cleanup_strv_free_ char **paths;
+static void test_generator_binary_paths(UnitFileScope scope) {
+        char template[] = "/tmp/test-path-lookup.XXXXXXX";
+
+        _cleanup_strv_free_ char **gp_without_env = NULL;
+        _cleanup_strv_free_ char **env_gp_without_env = NULL;
+        _cleanup_strv_free_ char **gp_with_env = NULL;
+        _cleanup_strv_free_ char **env_gp_with_env = NULL;
+        char *systemd_generator_path = NULL;
+        char *systemd_env_generator_path = NULL;
         char **dir;
 
+        assert_se(mkdtemp(template));
+
+        assert_se(unsetenv("SYSTEMD_GENERATOR_PATH") == 0);
+        assert_se(unsetenv("SYSTEMD_ENVIRONMENT_GENERATOR_PATH") == 0);
+
+        gp_without_env = generator_binary_paths(scope);
+        env_gp_without_env = env_generator_binary_paths(scope == UNIT_FILE_SYSTEM ? true : false);
+
+        log_info("Generators dirs (%s):", scope == UNIT_FILE_SYSTEM ? "system" : "user");
+        STRV_FOREACH(dir, gp_without_env)
+                log_info("        %s", *dir);
+
+        log_info("Environment generators dirs (%s):", scope == UNIT_FILE_SYSTEM ? "system" : "user");
+        STRV_FOREACH(dir, env_gp_without_env)
+                log_info("        %s", *dir);
+
+        assert_se(!strv_isempty(gp_without_env));
+        assert_se(!strv_isempty(env_gp_without_env));
+
+        systemd_generator_path = strjoina(template, "/systemd-generator-path");
+        systemd_env_generator_path = strjoina(template, "/systemd-environment-generator-path");
+        assert_se(setenv("SYSTEMD_GENERATOR_PATH", systemd_generator_path, 1) == 0);
+        assert_se(setenv("SYSTEMD_ENVIRONMENT_GENERATOR_PATH", systemd_env_generator_path, 1) == 0);
+
+        gp_with_env = generator_binary_paths(scope);
+        env_gp_with_env = env_generator_binary_paths(scope == UNIT_FILE_SYSTEM ? true : false);
+
         log_info("Generators dirs (%s):", scope == UNIT_FILE_SYSTEM ? "system" : "user");
+        STRV_FOREACH(dir, gp_with_env)
+                log_info("        %s", *dir);
 
-        paths = generator_binary_paths(scope);
-        STRV_FOREACH(dir, paths)
+        log_info("Environment generators dirs (%s):", scope == UNIT_FILE_SYSTEM ? "system" : "user");
+        STRV_FOREACH(dir, env_gp_with_env)
                 log_info("        %s", *dir);
+
+        assert_se(strv_equal(gp_with_env, STRV_MAKE(systemd_generator_path)));
+        assert_se(strv_equal(env_gp_with_env, STRV_MAKE(systemd_env_generator_path)));
 }
 
 int main(int argc, char **argv) {
@@ -87,8 +126,8 @@ int main(int argc, char **argv) {
 
         test_user_and_global_paths();
 
-        print_generator_binary_paths(UNIT_FILE_SYSTEM);
-        print_generator_binary_paths(UNIT_FILE_USER);
+        test_generator_binary_paths(UNIT_FILE_SYSTEM);
+        test_generator_binary_paths(UNIT_FILE_USER);
 
         return EXIT_SUCCESS;
 }
index 6ad222b5f928d60cac1303ed78d199aeea695055..1075f31bc6c720c7b55ebdbee3217b4a63b9bf0f 100644 (file)
@@ -61,148 +61,244 @@ static void shutdown_test(Manager *m) {
         manager_free(m);
 }
 
-static void check_stop_unlink(Manager *m, Unit *unit, const char *test_path, const char *service_name) {
+static Service *service_for_path(Manager *m, Path *path, const char *service_name) {
         _cleanup_free_ char *tmp = NULL;
         Unit *service_unit = NULL;
-        Service *service = NULL;
-        usec_t ts;
-        usec_t timeout = 2 * USEC_PER_SEC;
 
         assert_se(m);
-        assert_se(unit);
-        assert_se(test_path);
+        assert_se(path);
 
         if (!service_name) {
-                assert_se(tmp = strreplace(unit->id, ".path", ".service"));
+                assert_se(tmp = strreplace(UNIT(path)->id, ".path", ".service"));
                 service_unit = manager_get_unit(m, tmp);
         } else
                 service_unit = manager_get_unit(m, service_name);
         assert_se(service_unit);
-        service = SERVICE(service_unit);
 
-        ts = now(CLOCK_MONOTONIC);
-        /* We process events until the service related to the path has been successfully started */
-        while (service->result != SERVICE_SUCCESS || service->state != SERVICE_START) {
-                usec_t n;
-                int r;
+        return SERVICE(service_unit);
+}
+
+static void check_states(Manager *m, Path *path, Service *service, PathState path_state, ServiceState service_state) {
+        assert_se(m);
+        assert_se(service);
 
-                r = sd_event_run(m->event, 100 * USEC_PER_MSEC);
-                assert_se(r >= 0);
+        usec_t end = now(CLOCK_MONOTONIC) + 30 * USEC_PER_SEC;
+
+        while (path->result != PATH_SUCCESS || service->result != SERVICE_SUCCESS ||
+               path->state != path_state || service->state != service_state) {
+
+                assert_se(sd_event_run(m->event, 100 * USEC_PER_MSEC) >= 0);
 
                 printf("%s: state = %s; result = %s \n",
-                                service_unit->id,
+                                UNIT(path)->id,
+                                path_state_to_string(path->state),
+                                path_result_to_string(path->result));
+                printf("%s: state = %s; result = %s \n",
+                                UNIT(service)->id,
                                 service_state_to_string(service->state),
                                 service_result_to_string(service->result));
 
-                /* But we timeout if the service has not been started in the allocated time */
-                n = now(CLOCK_MONOTONIC);
-                if (ts + timeout < n) {
-                        log_error("Test timeout when testing %s", unit->id);
+                if (now(CLOCK_MONOTONIC) >= end) {
+                        log_error("Test timeout when testing %s", UNIT(path)->id);
                         exit(EXIT_FAILURE);
                 }
         }
-
-        assert_se(unit_stop(unit) >= 0);
-        (void) rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL);
 }
 
 static void test_path_exists(Manager *m) {
         const char *test_path = "/tmp/test-path_exists";
         Unit *unit = NULL;
+        Path *path = NULL;
+        Service *service = NULL;
 
         assert_se(m);
 
         assert_se(manager_load_startable_unit_or_warn(m, "path-exists.path", NULL, &unit) >= 0);
+
+        path = PATH(unit);
+        service = service_for_path(m, path, NULL);
+
         assert_se(unit_start(unit) >= 0);
+        check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
 
         assert_se(touch(test_path) >= 0);
+        check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
+
+        /* Service restarts if file still exists */
+        assert_se(unit_stop(UNIT(service)) >= 0);
+        check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
 
-        check_stop_unlink(m, unit, test_path, NULL);
+        assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
+        assert_se(unit_stop(UNIT(service)) >= 0);
+        check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
+
+        assert_se(unit_stop(unit) >= 0);
 }
 
 static void test_path_existsglob(Manager *m) {
         const char *test_path = "/tmp/test-path_existsglobFOOBAR";
         Unit *unit = NULL;
+        Path *path = NULL;
+        Service *service = NULL;
 
         assert_se(m);
+
         assert_se(manager_load_startable_unit_or_warn(m, "path-existsglob.path", NULL, &unit) >= 0);
+
+        path = PATH(unit);
+        service = service_for_path(m, path, NULL);
+
         assert_se(unit_start(unit) >= 0);
+        check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
 
         assert_se(touch(test_path) >= 0);
+        check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
 
-        check_stop_unlink(m, unit, test_path, NULL);
+        /* Service restarts if file still exists */
+        assert_se(unit_stop(UNIT(service)) >= 0);
+        check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
+
+        assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
+        assert_se(unit_stop(UNIT(service)) >= 0);
+        check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
+
+        assert_se(unit_stop(unit) >= 0);
 }
 
 static void test_path_changed(Manager *m) {
         const char *test_path = "/tmp/test-path_changed";
         FILE *f;
         Unit *unit = NULL;
+        Path *path = NULL;
+        Service *service = NULL;
 
         assert_se(m);
 
-        assert_se(touch(test_path) >= 0);
-
         assert_se(manager_load_startable_unit_or_warn(m, "path-changed.path", NULL, &unit) >= 0);
+
+        path = PATH(unit);
+        service = service_for_path(m, path, NULL);
+
         assert_se(unit_start(unit) >= 0);
+        check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
+
+        assert_se(touch(test_path) >= 0);
+        check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
+
+        /* Service does not restart if file still exists */
+        assert_se(unit_stop(UNIT(service)) >= 0);
+        check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
 
         f = fopen(test_path, "w");
         assert_se(f);
         fclose(f);
 
-        check_stop_unlink(m, unit, test_path, NULL);
+        check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
+
+        assert_se(unit_stop(UNIT(service)) >= 0);
+        check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
+
+        (void) rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL);
+        assert_se(unit_stop(unit) >= 0);
 }
 
 static void test_path_modified(Manager *m) {
         _cleanup_fclose_ FILE *f = NULL;
         const char *test_path = "/tmp/test-path_modified";
         Unit *unit = NULL;
+        Path *path = NULL;
+        Service *service = NULL;
 
         assert_se(m);
 
-        assert_se(touch(test_path) >= 0);
-
         assert_se(manager_load_startable_unit_or_warn(m, "path-modified.path", NULL, &unit) >= 0);
+
+        path = PATH(unit);
+        service = service_for_path(m, path, NULL);
+
         assert_se(unit_start(unit) >= 0);
+        check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
+
+        assert_se(touch(test_path) >= 0);
+        check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
+
+        /* Service does not restart if file still exists */
+        assert_se(unit_stop(UNIT(service)) >= 0);
+        check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
 
         f = fopen(test_path, "w");
         assert_se(f);
         fputs("test", f);
 
-        check_stop_unlink(m, unit, test_path, NULL);
+        check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
+
+        assert_se(unit_stop(UNIT(service)) >= 0);
+        check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
+
+        (void) rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL);
+        assert_se(unit_stop(unit) >= 0);
 }
 
 static void test_path_unit(Manager *m) {
         const char *test_path = "/tmp/test-path_unit";
         Unit *unit = NULL;
+        Path *path = NULL;
+        Service *service = NULL;
 
         assert_se(m);
 
         assert_se(manager_load_startable_unit_or_warn(m, "path-unit.path", NULL, &unit) >= 0);
+
+        path = PATH(unit);
+        service = service_for_path(m, path, "path-mycustomunit.service");
+
         assert_se(unit_start(unit) >= 0);
+        check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
 
         assert_se(touch(test_path) >= 0);
+        check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
+
+        assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
+        assert_se(unit_stop(UNIT(service)) >= 0);
+        check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
 
-        check_stop_unlink(m, unit, test_path, "path-mycustomunit.service");
+        assert_se(unit_stop(unit) >= 0);
 }
 
 static void test_path_directorynotempty(Manager *m) {
         const char *test_path = "/tmp/test-path_directorynotempty/";
         Unit *unit = NULL;
+        Path *path = NULL;
+        Service *service = NULL;
 
         assert_se(m);
 
+        assert_se(manager_load_startable_unit_or_warn(m, "path-directorynotempty.path", NULL, &unit) >= 0);
+
+        path = PATH(unit);
+        service = service_for_path(m, path, NULL);
+
         assert_se(access(test_path, F_OK) < 0);
 
-        assert_se(manager_load_startable_unit_or_warn(m, "path-directorynotempty.path", NULL, &unit) >= 0);
         assert_se(unit_start(unit) >= 0);
+        check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
 
         /* MakeDirectory default to no */
         assert_se(access(test_path, F_OK) < 0);
 
         assert_se(mkdir_p(test_path, 0755) >= 0);
         assert_se(touch(strjoina(test_path, "test_file")) >= 0);
+        check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
 
-        check_stop_unlink(m, unit, test_path, NULL);
+        /* Service restarts if directory is still not empty */
+        assert_se(unit_stop(UNIT(service)) >= 0);
+        check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
+
+        assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
+        assert_se(unit_stop(UNIT(service)) >= 0);
+        check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
+
+        assert_se(unit_stop(unit) >= 0);
 }
 
 static void test_path_makedirectory_directorymode(Manager *m) {
@@ -212,9 +308,10 @@ static void test_path_makedirectory_directorymode(Manager *m) {
 
         assert_se(m);
 
+        assert_se(manager_load_startable_unit_or_warn(m, "path-makedirectory.path", NULL, &unit) >= 0);
+
         assert_se(access(test_path, F_OK) < 0);
 
-        assert_se(manager_load_startable_unit_or_warn(m, "path-makedirectory.path", NULL, &unit) >= 0);
         assert_se(unit_start(unit) >= 0);
 
         /* Check if the directory has been created */
@@ -242,20 +339,19 @@ int main(int argc, char *argv[]) {
                 NULL,
         };
 
-        _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL;
         _cleanup_free_ char *test_path = NULL;
-        const test_function_t *test = NULL;
-        Manager *m = NULL;
+        _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL;
 
         umask(022);
 
         test_setup_logging(LOG_INFO);
 
-        test_path = path_join(get_testdata_dir(), "test-path");
+        assert_se(get_testdata_dir("test-path", &test_path) >= 0);
         assert_se(set_unit_path(test_path) >= 0);
         assert_se(runtime_dir = setup_fake_runtime_dir());
 
-        for (test = tests; test && *test; test++) {
+        for (const test_function_t *test = tests; test && *test; test++) {
+                Manager *m = NULL;
                 int r;
 
                 /* We create a clean environment for each test */
index 21d5b44fbb69454c69f1fec7adbd5cf03ba0d634..50f66cb970b280f49d62e2d2bb768cbdd8f1bdff 100644 (file)
@@ -60,7 +60,7 @@ DEFINE_PRIVATE_HASH_OPS(test_hash_ops, struct test, test_hash, test_compare);
 
 static void test_struct(void) {
         _cleanup_(prioq_freep) Prioq *q = NULL;
-        _cleanup_(set_freep) Set *s = NULL;
+        _cleanup_set_free_ Set *s = NULL;
         unsigned previous = 0, i;
         struct test *t;
 
index bdcd1fc4c7ce63e01d28eb60add07fde49ab249b..4a9b111a20ab5bc0bfd56c60de3fa197ca813618 100644 (file)
@@ -2,6 +2,7 @@
 
 #include "alloc-util.h"
 #include "env-util.h"
+#include "errno-util.h"
 #include "log.h"
 #include "macro.h"
 #include "proc-cmdline.h"
@@ -30,7 +31,7 @@ static void test_proc_cmdline_override(void) {
         log_info("/* %s */", __func__);
 
         assert_se(putenv((char*) "SYSTEMD_PROC_CMDLINE=foo_bar=quux wuff-piep=tuet zumm some_arg_with_space='foo bar' and_one_more=\"zzz aaa\"") == 0);
-        assert_se(putenv((char*) "SYSTEMD_EFI_OPTIONS=differnt") == 0);
+        assert_se(putenv((char*) "SYSTEMD_EFI_OPTIONS=different") == 0);
 
         /* First test if the overrides for /proc/cmdline still work */
         _cleanup_free_ char *line = NULL, *value = NULL;
@@ -250,6 +251,9 @@ static void test_proc_cmdline_key_startswith(void) {
 int main(void) {
         test_setup_logging(LOG_INFO);
 
+        if (access("/proc/cmdline", R_OK) < 0 && ERRNO_IS_PRIVILEGE(errno))
+                return log_tests_skipped("can't read /proc/cmdline");
+
         test_proc_cmdline_parse();
         test_proc_cmdline_override();
         test_proc_cmdline_given(false);
index d78e0544a7a6236c0f9fcf233e8a4805f4625b4b..b00dd4a980df747ccdeecb13044cd760cf06b817 100644 (file)
@@ -636,7 +636,7 @@ static void test_setpriority_closest(void) {
                 q = getpriority(PRIO_PROCESS, 0);
                 assert_se(errno == 0 && p == q);
 
-                /* It should also be possible to to set the nice level to one higher */
+                /* It should also be possible to set the nice level to one higher */
                 if (p < PRIO_MAX-1) {
                         assert_se(setpriority_closest(++p) > 0);
 
@@ -645,7 +645,7 @@ static void test_setpriority_closest(void) {
                         assert_se(errno == 0 && p == q);
                 }
 
-                /* It should also be possible to to set the nice level to two higher */
+                /* It should also be possible to set the nice level to two higher */
                 if (p < PRIO_MAX-1) {
                         assert_se(setpriority_closest(++p) > 0);
 
index 662688e0f0d18c9f42b27dc514154c887c714fc2..61434578b0b87865a5cfd02d447552a1f8605c46 100644 (file)
@@ -2,9 +2,11 @@
 
 #include <errno.h>
 
+#include "errno-util.h"
 #include "format-util.h"
 #include "log.h"
 #include "procfs-util.h"
+#include "tests.h"
 
 int main(int argc, char *argv[]) {
         char buf[CONST_MAX(FORMAT_TIMESPAN_MAX, FORMAT_BYTES_MAX)];
@@ -24,7 +26,11 @@ int main(int argc, char *argv[]) {
         assert_se(procfs_tasks_get_current(&v) >= 0);
         log_info("Current number of tasks: %" PRIu64, v);
 
-        assert_se(procfs_tasks_get_limit(&v) >= 0);
+        r = procfs_tasks_get_limit(&v);
+        if (r == -ENOENT || ERRNO_IS_PRIVILEGE(r))
+                return log_tests_skipped("can't read /proc/sys/kernel/pid_max");
+
+        assert_se(r >= 0);
         log_info("Limit of tasks: %" PRIu64, v);
         assert_se(v > 0);
         assert_se(procfs_tasks_set_limit(v) >= 0);
index 94c431f7e6084ea88c649efada378ec91852c145..ad5bc72a4e138c0e1e3ca68155a3b47765130541 100644 (file)
@@ -58,6 +58,7 @@ int main(int argc, char **argv) {
         test_genuine_random_bytes(0);
         test_genuine_random_bytes(RANDOM_BLOCK);
         test_genuine_random_bytes(RANDOM_ALLOW_RDRAND);
+        test_genuine_random_bytes(RANDOM_ALLOW_INSECURE);
 
         test_pseudo_random_bytes();
 
index cd4553784734f7cc0dca01089df7b31905887e13..da6d2a21e66cf14f2a506df086542c7e4ec69e68 100644 (file)
@@ -25,8 +25,11 @@ int main(int argc, char *argv[]) {
                 return log_tests_skipped("cgroupfs not available");
 
         /* prepare the test */
-        assert_se(set_unit_path(get_testdata_dir()) >= 0);
+        _cleanup_free_ char *unit_dir = NULL;
+        assert_se(get_testdata_dir("units", &unit_dir) >= 0);
+        assert_se(set_unit_path(unit_dir) >= 0);
         assert_se(runtime_dir = setup_fake_runtime_dir());
+
         r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m);
         if (manager_errno_skip_test(r))
                 return log_tests_skipped_errno(r, "manager_new");
index 17ca6a0e279861e5305f595a6dfa1d41b16846df..eb34d8eab2d83cfaf286b5e7e25abedcd03085bd 100644 (file)
@@ -1,6 +1,7 @@
 #include "sd-hwdb.h"
 
 #include "alloc-util.h"
+#include "errno-util.h"
 #include "errno.h"
 #include "tests.h"
 
@@ -12,7 +13,7 @@ static int test_failed_enumerate(void) {
         log_info("/* %s */", __func__);
 
         r = sd_hwdb_new(&hwdb);
-        if (r == -ENOENT)
+        if (r == -ENOENT || ERRNO_IS_PRIVILEGE(r))
                 return r;
         assert_se(r == 0);
 
diff --git a/src/test/test-sd-path.c b/src/test/test-sd-path.c
new file mode 100644 (file)
index 0000000..9260db5
--- /dev/null
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "sd-path.h"
+
+#include "alloc-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "tests.h"
+
+static void test_sd_path_lookup(void) {
+        log_info("/* %s */", __func__);
+
+        for (uint64_t i = 0; i < _SD_PATH_MAX; i++) {
+                _cleanup_free_ char *t = NULL, *s = NULL;
+                int r;
+
+                r = sd_path_lookup(i, NULL, &t);
+                if (i == SD_PATH_USER_RUNTIME && r == -ENXIO)
+                        continue;
+                assert_se(r == 0);
+                assert_se(t);
+                log_info("%02"PRIu64": \"%s\"", i, t);
+
+                assert_se(sd_path_lookup(i, "suffix", &s) == 0);
+                assert_se(s);
+                log_info("%02"PRIu64": \"%s\"", i, s);
+                assert_se(endswith(s, "/suffix"));
+        }
+
+        char *tt;
+        assert_se(sd_path_lookup(_SD_PATH_MAX, NULL, &tt) == -EOPNOTSUPP);
+}
+
+static void test_sd_path_lookup_strv(void) {
+        log_info("/* %s */", __func__);
+
+        for (uint64_t i = 0; i < _SD_PATH_MAX; i++) {
+                _cleanup_strv_free_ char **t = NULL, **s = NULL;
+                char **item;
+                int r;
+
+                r = sd_path_lookup_strv(i, NULL, &t);
+                if (i == SD_PATH_USER_RUNTIME && r == -ENXIO)
+                        continue;
+                assert_se(r == 0);
+                assert_se(t);
+                log_info("%02"PRIu64":", i);
+                STRV_FOREACH(item, t)
+                        log_debug("  %s", *item);
+
+                assert_se(sd_path_lookup_strv(i, "suffix", &s) == 0);
+                assert_se(s);
+                log_info("%02"PRIu64":", i);
+                STRV_FOREACH(item, s) {
+                        assert_se(endswith(*item, "/suffix"));
+                        log_debug("  %s", *item);
+                }
+        }
+
+        char *tt;
+        assert_se(sd_path_lookup(_SD_PATH_MAX, NULL, &tt) == -EOPNOTSUPP);
+}
+
+int main(void) {
+        test_setup_logging(LOG_DEBUG);
+
+        test_sd_path_lookup();
+        test_sd_path_lookup_strv();
+}
index 67900d85e9ac04f692621174692fafafcda3f445..eec2779a9e2a69cec60f08365b0ab4a26213bdf1 100644 (file)
@@ -13,6 +13,7 @@
 
 #include "alloc-util.h"
 #include "fd-util.h"
+#include "fileio.h"
 #include "macro.h"
 #include "memory-util.h"
 #include "missing_sched.h"
@@ -122,7 +123,7 @@ static void test_filter_sets(void) {
                 if (pid == 0) { /* Child? */
                         int fd;
 
-                        /* If we look at the default set (or one that includes it), whitelist instead of blacklist */
+                        /* If we look at the default set (or one that includes it), allow-list instead of deny-list */
                         if (IN_SET(i, SYSCALL_FILTER_SET_DEFAULT, SYSCALL_FILTER_SET_SYSTEM_SERVICE))
                                 r = seccomp_load_syscall_filter_set(SCMP_ACT_ERRNO(EUCLEAN), syscall_filter_sets + i, SCMP_ACT_ALLOW, true);
                         else
@@ -283,6 +284,7 @@ static void test_restrict_namespace(void) {
 
 static void test_protect_sysctl(void) {
         pid_t pid;
+        _cleanup_free_ char *seccomp = NULL;
 
         log_info("/* %s */", __func__);
 
@@ -301,6 +303,10 @@ static void test_protect_sysctl(void) {
                 return;
         }
 
+        assert_se(get_proc_field("/proc/self/status", "Seccomp", WHITESPACE, &seccomp) == 0);
+        if (!streq(seccomp, "0"))
+                log_warning("Warning: seccomp filter detected, results may be unreliable for %s", __func__);
+
         pid = fork();
         assert_se(pid >= 0);
 
index b4e7a52fd964aef5713d42283526be2ab18106b3..d3e6de79789c685b3a4ef06914ecab3f96792dce 100644 (file)
@@ -3,6 +3,8 @@
 #include "set.h"
 #include "strv.h"
 
+const bool mempool_use_allowed = VALGRIND;
+
 static void test_set_steal_first(void) {
         _cleanup_set_free_ Set *m = NULL;
         int seen[3] = {};
@@ -86,11 +88,78 @@ static void test_set_put(void) {
         assert_se(strv_length(t) == 3);
 }
 
+static void test_set_put_strdup(void) {
+        _cleanup_set_free_ Set *m = NULL;
+
+        assert_se(set_put_strdup(&m, "aaa") == 1);
+        assert_se(set_put_strdup(&m, "aaa") == 0);
+        assert_se(set_put_strdup(&m, "bbb") == 1);
+        assert_se(set_put_strdup(&m, "bbb") == 0);
+        assert_se(set_put_strdup(&m, "aaa") == 0);
+        assert_se(set_size(m) == 2);
+}
+
+static void test_set_put_strdupv(void) {
+        _cleanup_set_free_ Set *m = NULL;
+
+        assert_se(set_put_strdupv(&m, STRV_MAKE("aaa", "aaa", "bbb", "bbb", "aaa")) == 2);
+        assert_se(set_put_strdupv(&m, STRV_MAKE("aaa", "aaa", "bbb", "bbb", "ccc")) == 1);
+        assert_se(set_size(m) == 3);
+}
+
+static void test_set_ensure_allocated(void) {
+        _cleanup_set_free_ Set *m = NULL;
+
+        assert_se(set_ensure_allocated(&m, &string_hash_ops) == 1);
+        assert_se(set_ensure_allocated(&m, &string_hash_ops) == 0);
+        assert_se(set_ensure_allocated(&m, NULL) == 0);
+        assert_se(set_size(m) == 0);
+}
+
+static void test_set_ensure_put(void) {
+        _cleanup_set_free_ Set *m = NULL;
+
+        assert_se(set_ensure_put(&m, &string_hash_ops, "a") == 1);
+        assert_se(set_ensure_put(&m, &string_hash_ops, "a") == 0);
+        assert_se(set_ensure_put(&m, NULL, "a") == 0);
+        assert_se(set_ensure_put(&m, &string_hash_ops, "b") == 1);
+        assert_se(set_ensure_put(&m, &string_hash_ops, "b") == 0);
+        assert_se(set_ensure_put(&m, &string_hash_ops, "a") == 0);
+        assert_se(set_size(m) == 2);
+}
+
+static void test_set_ensure_consume(void) {
+        _cleanup_set_free_ Set *m = NULL;
+        char *s, *t;
+
+        assert_se(s = strdup("a"));
+        assert_se(set_ensure_consume(&m, &string_hash_ops_free, s) == 1);
+
+        assert_se(t = strdup("a"));
+        assert_se(set_ensure_consume(&m, &string_hash_ops_free, t) == 0);
+
+        assert_se(t = strdup("a"));
+        assert_se(set_ensure_consume(&m, &string_hash_ops_free, t) == 0);
+
+        assert_se(t = strdup("b"));
+        assert_se(set_ensure_consume(&m, &string_hash_ops_free, t) == 1);
+
+        assert_se(t = strdup("b"));
+        assert_se(set_ensure_consume(&m, &string_hash_ops_free, t) == 0);
+
+        assert_se(set_size(m) == 2);
+}
+
 int main(int argc, const char *argv[]) {
         test_set_steal_first();
         test_set_free_with_destructor();
         test_set_free_with_hash_ops();
         test_set_put();
+        test_set_put_strdup();
+        test_set_put_strdupv();
+        test_set_ensure_allocated();
+        test_set_ensure_put();
+        test_set_ensure_consume();
 
         return 0;
 }
index 1020e0cb3153594bcd7f8781e129b82404afc8f2..b9d63d6b418f3c344aed8e96247b6bc4e96f43ba 100644 (file)
@@ -14,7 +14,7 @@
 /* Print information about various types. Useful when diagnosing
  * gcc diagnostics on an unfamiliar architecture. */
 
-#pragma GCC diagnostic ignored "-Wtype-limits"
+DISABLE_WARNING_TYPE_LIMITS;
 
 #define info(t)                                                         \
         printf("%s → %zu bits%s, %zu byte alignment\n", STRINGIFY(t),   \
index 2e63aace0201ab696fb5ba04af384dd2f6459d2a..8b4fa82640b443f660c149609fbd843c72f69828 100644 (file)
@@ -7,6 +7,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include "efivars.h"
 #include "errno-util.h"
 #include "fd-util.h"
 #include "log.h"
@@ -84,7 +85,9 @@ static void test_sleep(void) {
 
         log_info("/* %s */", __func__);
 
-        log_info("/= configuration =/");
+        printf("Secure boot: %sd\n", enable_disable(is_efi_secure_boot()));
+
+        log_info("/= individual sleep modes =/");
         log_info("Standby configured: %s", yes_no(can_sleep_state(standby) > 0));
         log_info("Suspend configured: %s", yes_no(can_sleep_state(mem) > 0));
         log_info("Hibernate configured: %s", yes_no(can_sleep_state(disk) > 0));
@@ -94,7 +97,7 @@ static void test_sleep(void) {
         log_info("Hibernate+Shutdown configured: %s", yes_no(can_sleep_disk(shutdown) > 0));
         log_info("Freeze configured: %s", yes_no(can_sleep_state(freeze) > 0));
 
-        log_info("/= running system =/");
+        log_info("/= high-level sleep verbs =/");
         r = can_sleep("suspend");
         log_info("Suspend configured and possible: %s", r >= 0 ? yes_no(r) : strerror_safe(r));
         r = can_sleep("hibernate");
index d36caaa71e73375637af4cbc61d09332625c3644..b007dd62764608bbff233dc1aff23f482037498c 100644 (file)
@@ -173,6 +173,45 @@ static void test_in_addr_prefix_next(void) {
         test_in_addr_prefix_next_one(AF_INET6, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00", 120, NULL);
 }
 
+static void test_in_addr_prefix_nth_one(unsigned f, const char *before, unsigned pl, uint64_t nth, const char *after) {
+        union in_addr_union ubefore, uafter, t;
+
+        assert_se(in_addr_from_string(f, before, &ubefore) >= 0);
+
+        t = ubefore;
+        assert_se((in_addr_prefix_nth(f, &t, pl, nth) > 0) == !!after);
+
+        if (after) {
+                assert_se(in_addr_from_string(f, after, &uafter) >= 0);
+                assert_se(in_addr_equal(f, &t, &uafter) > 0);
+        }
+}
+
+static void test_in_addr_prefix_nth(void) {
+        log_info("/* %s */", __func__);
+
+        test_in_addr_prefix_nth_one(AF_INET, "192.168.0.0", 24, 0, "192.168.0.0");
+        test_in_addr_prefix_nth_one(AF_INET, "192.168.0.0", 24, 1, "192.168.1.0");
+        test_in_addr_prefix_nth_one(AF_INET, "192.168.0.0", 24, 4, "192.168.4.0");
+        test_in_addr_prefix_nth_one(AF_INET, "192.168.0.0", 25, 1, "192.168.0.128");
+        test_in_addr_prefix_nth_one(AF_INET, "192.168.255.0", 25, 1, "192.168.255.128");
+        test_in_addr_prefix_nth_one(AF_INET, "192.168.255.0", 24, 0, "192.168.255.0");
+        test_in_addr_prefix_nth_one(AF_INET, "255.255.255.255", 32, 1, NULL);
+        test_in_addr_prefix_nth_one(AF_INET, "255.255.255.255", 0, 1, NULL);
+
+        test_in_addr_prefix_nth_one(AF_INET6, "4400::", 8, 1, "4500::");
+        test_in_addr_prefix_nth_one(AF_INET6, "4400::", 7, 1, "4600::");
+        test_in_addr_prefix_nth_one(AF_INET6, "4400::", 64, 1, "4400:0:0:1::");
+        test_in_addr_prefix_nth_one(AF_INET6, "4400::", 64, 2, "4400:0:0:2::");
+        test_in_addr_prefix_nth_one(AF_INET6, "4400::", 64, 0xbad, "4400:0:0:0bad::");
+        test_in_addr_prefix_nth_one(AF_INET6, "4400:0:0:ffff::", 64, 1, "4400:0:1::");
+        test_in_addr_prefix_nth_one(AF_INET6, "4400::", 56, ((uint64_t)1<<48) -1, "44ff:ffff:ffff:ff00::");
+        test_in_addr_prefix_nth_one(AF_INET6, "0000::", 8, 255, "ff00::");
+        test_in_addr_prefix_nth_one(AF_INET6, "0000::", 8, 256, NULL);
+        test_in_addr_prefix_nth_one(AF_INET6, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 128, 1, NULL);
+        test_in_addr_prefix_nth_one(AF_INET6, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 0, 1, NULL);
+}
+
 static void test_in_addr_to_string_one(int f, const char *addr) {
         union in_addr_union ua;
         _cleanup_free_ char *r = NULL;
@@ -245,6 +284,56 @@ static void test_in_addr_ifindex_from_string_auto(void) {
         assert_se(in_addr_ifindex_from_string_auto("fe80::19%thisinterfacecantexist", &family, &ua, &ifindex) == -ENODEV);
 }
 
+static void test_in_addr_ifindex_name_from_string_auto_one(const char *a, const char *expected) {
+        int family, ifindex;
+        union in_addr_union ua;
+        _cleanup_free_ char *server_name = NULL;
+
+        assert_se(in_addr_ifindex_name_from_string_auto(a, &family, &ua, &ifindex, &server_name) >= 0);
+        assert_se(streq_ptr(server_name, expected));
+}
+
+static void test_in_addr_ifindex_name_from_string_auto(void) {
+        log_info("/* %s */", __func__);
+
+        test_in_addr_ifindex_name_from_string_auto_one("192.168.0.1", NULL);
+        test_in_addr_ifindex_name_from_string_auto_one("192.168.0.1#test.com", "test.com");
+        test_in_addr_ifindex_name_from_string_auto_one("fe80::18%19", NULL);
+        test_in_addr_ifindex_name_from_string_auto_one("fe80::18%19#another.test.com", "another.test.com");
+}
+
+static void test_in_addr_port_ifindex_name_from_string_auto_one(const char *str, int family, uint16_t port, int ifindex, const char *server_name) {
+        _cleanup_free_ char *name = NULL, *x = NULL;
+        union in_addr_union a;
+        uint16_t p;
+        int f, i;
+
+        assert_se(in_addr_port_ifindex_name_from_string_auto(str, &f, &a, &p, &i, &name) >= 0);
+        assert_se(family == f);
+        assert_se(port == p);
+        assert_se(ifindex == i);
+        assert_se(streq_ptr(server_name, name));
+        assert_se(in_addr_port_ifindex_name_to_string(f, &a, p, i, name, &x) >= 0);
+        assert_se(streq(str, x));
+}
+
+static void test_in_addr_port_ifindex_name_from_string_auto(void) {
+        log_info("/* %s */", __func__);
+
+        test_in_addr_port_ifindex_name_from_string_auto_one("192.168.0.1", AF_INET, 0, 0, NULL);
+        test_in_addr_port_ifindex_name_from_string_auto_one("192.168.0.1#test.com", AF_INET, 0, 0, "test.com");
+        test_in_addr_port_ifindex_name_from_string_auto_one("192.168.0.1:53", AF_INET, 53, 0, NULL);
+        test_in_addr_port_ifindex_name_from_string_auto_one("192.168.0.1:53#example.com", AF_INET, 53, 0, "example.com");
+        test_in_addr_port_ifindex_name_from_string_auto_one("fe80::18", AF_INET6, 0, 0, NULL);
+        test_in_addr_port_ifindex_name_from_string_auto_one("fe80::18#hoge.com", AF_INET6, 0, 0, "hoge.com");
+        test_in_addr_port_ifindex_name_from_string_auto_one("fe80::18%19", AF_INET6, 0, 19, NULL);
+        test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53", AF_INET6, 53, 0, NULL);
+        test_in_addr_port_ifindex_name_from_string_auto_one("fe80::18%19#hoge.com", AF_INET6, 0, 19, "hoge.com");
+        test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53#hoge.com", AF_INET6, 53, 0, "hoge.com");
+        test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53%19", AF_INET6, 53, 19, NULL);
+        test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53%19#hoge.com", AF_INET6, 53, 19, "hoge.com");
+}
+
 static void test_sockaddr_equal(void) {
         union sockaddr_union a = {
                 .in.sin_family = AF_INET,
@@ -673,9 +762,12 @@ int main(int argc, char *argv[]) {
         test_in_addr_is_null();
         test_in_addr_prefix_intersect();
         test_in_addr_prefix_next();
+        test_in_addr_prefix_nth();
         test_in_addr_to_string();
         test_in_addr_ifindex_to_string();
         test_in_addr_ifindex_from_string_auto();
+        test_in_addr_ifindex_name_from_string_auto();
+        test_in_addr_port_ifindex_name_from_string_auto();
 
         test_sockaddr_equal();
 
index a0ffdf6cb6aa54c340cdbbe854d7a852d56c628b..e81b12b418a63fb0e170e62b5f8464cb29599bb5 100644 (file)
@@ -3,6 +3,7 @@
 #include "alloc-util.h"
 #include "log.h"
 #include "specifier.h"
+#include "stdio-util.h"
 #include "string-util.h"
 #include "strv.h"
 #include "tests.h"
@@ -15,6 +16,8 @@ static void test_specifier_escape_one(const char *a, const char *b) {
 }
 
 static void test_specifier_escape(void) {
+        log_info("/* %s */", __func__);
+
         test_specifier_escape_one(NULL, NULL);
         test_specifier_escape_one("", "");
         test_specifier_escape_one("%", "%%");
@@ -31,12 +34,54 @@ static void test_specifier_escape_strv_one(char **a, char **b) {
 }
 
 static void test_specifier_escape_strv(void) {
+        log_info("/* %s */", __func__);
+
         test_specifier_escape_strv_one(NULL, NULL);
         test_specifier_escape_strv_one(STRV_MAKE(NULL), STRV_MAKE(NULL));
         test_specifier_escape_strv_one(STRV_MAKE(""), STRV_MAKE(""));
         test_specifier_escape_strv_one(STRV_MAKE("foo"), STRV_MAKE("foo"));
         test_specifier_escape_strv_one(STRV_MAKE("%"), STRV_MAKE("%%"));
-        test_specifier_escape_strv_one(STRV_MAKE("foo", "%", "foo%", "%foo", "foo%foo", "quux", "%%%"), STRV_MAKE("foo", "%%", "foo%%", "%%foo", "foo%%foo", "quux", "%%%%%%"));
+        test_specifier_escape_strv_one(STRV_MAKE("foo", "%", "foo%", "%foo", "foo%foo", "quux", "%%%"),
+                                       STRV_MAKE("foo", "%%", "foo%%", "%%foo", "foo%%foo", "quux", "%%%%%%"));
+}
+
+/* Any specifier functions which don't need an argument. */
+static const Specifier specifier_table[] = {
+        { 'm', specifier_machine_id,      NULL },
+        { 'b', specifier_boot_id,         NULL },
+        { 'H', specifier_host_name,       NULL },
+        { 'l', specifier_short_host_name, NULL },
+        { 'v', specifier_kernel_release,  NULL },
+        { 'a', specifier_architecture,    NULL },
+        { 'o', specifier_os_id,           NULL },
+        { 'w', specifier_os_version_id,   NULL },
+        { 'B', specifier_os_build_id,     NULL },
+        { 'W', specifier_os_variant_id,   NULL },
+
+        { 'g', specifier_group_name,      NULL },
+        { 'G', specifier_group_id,        NULL },
+        { 'U', specifier_user_id,         NULL },
+        { 'u', specifier_user_name,       NULL },
+        { 'h', specifier_user_home,       NULL },
+
+        { 'T', specifier_tmp_dir,         NULL },
+        { 'V', specifier_var_tmp_dir,     NULL },
+        {}
+};
+
+static void test_specifiers(void) {
+        log_info("/* %s */", __func__);
+
+        for (const Specifier *s = specifier_table; s->specifier; s++) {
+                char spec[3];
+                _cleanup_free_ char *resolved = NULL;
+
+                xsprintf(spec, "%%%c", s->specifier);
+
+                assert_se(specifier_printf(spec, specifier_table, NULL, &resolved) >= 0);
+
+                log_info("%%%c → %s", s->specifier, resolved);
+        }
 }
 
 int main(int argc, char *argv[]) {
@@ -44,6 +89,7 @@ int main(int argc, char *argv[]) {
 
         test_specifier_escape();
         test_specifier_escape_strv();
+        test_specifiers();
 
         return 0;
 }
index e48d419f54003f10f9deebe99d38261afdc4478c..cba5441d4b3c0e0334da10449196b12200022679 100644 (file)
@@ -9,12 +9,17 @@
 
 static void test_specifier_printf(void) {
         static const Specifier table[] = {
-                { 'a', specifier_string, (char*) "AAAA" },
-                { 'b', specifier_string, (char*) "BBBB" },
-                { 'm', specifier_machine_id, NULL },
-                { 'B', specifier_boot_id, NULL },
-                { 'H', specifier_host_name, NULL },
+                { 'X', specifier_string,         (char*) "AAAA" },
+                { 'Y', specifier_string,         (char*) "BBBB" },
+                { 'm', specifier_machine_id,     NULL },
+                { 'b', specifier_boot_id,        NULL },
+                { 'H', specifier_host_name,      NULL },
                 { 'v', specifier_kernel_release, NULL },
+                { 'a', specifier_architecture,   NULL },
+                { 'o', specifier_os_id,          NULL },
+                { 'w', specifier_os_version_id,  NULL },
+                { 'B', specifier_os_build_id,    NULL },
+                { 'W', specifier_os_variant_id,  NULL },
                 {}
         };
 
@@ -23,7 +28,7 @@ static void test_specifier_printf(void) {
 
         log_info("/* %s */", __func__);
 
-        r = specifier_printf("xxx a=%a b=%b yyy", table, NULL, &w);
+        r = specifier_printf("xxx a=%X b=%Y yyy", table, NULL, &w);
         assert_se(r >= 0);
         assert_se(w);
 
@@ -31,10 +36,15 @@ static void test_specifier_printf(void) {
         assert_se(streq(w, "xxx a=AAAA b=BBBB yyy"));
 
         free(w);
-        r = specifier_printf("machine=%m, boot=%B, host=%H, version=%v", table, NULL, &w);
+        r = specifier_printf("machine=%m, boot=%b, host=%H, version=%v, arch=%a", table, NULL, &w);
         assert_se(r >= 0);
         assert_se(w);
         puts(w);
+
+        w = mfree(w);
+        specifier_printf("os=%o, os-version=%w, build=%B, variant=%W", table, NULL, &w);
+        if (w)
+                puts(w);
 }
 
 static void test_str_in_set(void) {
@@ -408,9 +418,8 @@ static void test_strv_split_newlines(void) {
         l = strv_split_newlines(str);
         assert_se(l);
 
-        STRV_FOREACH(s, l) {
+        STRV_FOREACH(s, l)
                 assert_se(streq(*s, input_table_multiple[i++]));
-        }
 }
 
 static void test_strv_split_nulstr(void) {
@@ -680,7 +689,7 @@ static void test_strv_push_prepend(void) {
 
         log_info("/* %s */", __func__);
 
-        a = strv_new("foo", "bar", "three");
+        assert_se(a = strv_new("foo", "bar", "three"));
 
         assert_se(strv_push_prepend(&a, strdup("first")) >= 0);
         assert_se(streq(a[0], "first"));
index 0e563f54970b8e5734d8d4eae4b703a1bd7bd553..52e651faefe4a5f9e3c11618d8eafb45b014c2ed 100644 (file)
@@ -88,7 +88,7 @@ static void test_colors(void) {
         test_one_color("green", ansi_green());
         test_one_color("yellow", ansi_yellow());
         test_one_color("blue", ansi_blue());
-        test_one_color("megenta", ansi_magenta());
+        test_one_color("magenta", ansi_magenta());
         test_one_color("grey", ansi_grey());
         test_one_color("highlight-red", ansi_highlight_red());
         test_one_color("highlight-green", ansi_highlight_green());
index e3b1f6f8ca07c0fe29f2010c1d1313c670a81004..8826956d10f7774371087f7e6180b9263e6faad6 100644 (file)
@@ -483,6 +483,38 @@ static void test_in_utc_timezone(void) {
         assert_se(unsetenv("TZ") >= 0);
 }
 
+static void test_map_clock_usec(void) {
+        usec_t nowr, x, y, z;
+
+        log_info("/* %s */", __func__);
+        nowr = now(CLOCK_REALTIME);
+
+        x = nowr; /* right now */
+        y = map_clock_usec(x, CLOCK_REALTIME, CLOCK_MONOTONIC);
+        z = map_clock_usec(y, CLOCK_MONOTONIC, CLOCK_REALTIME);
+        /* Converting forth and back will introduce inaccuracies, since we cannot query both clocks atomically, but it should be small. Even on the slowest CI smaller than 1h */
+
+        assert_se((z > x ? z - x : x - z) < USEC_PER_HOUR);
+
+        assert_se(nowr < USEC_INFINITY - USEC_PER_DAY*7); /* overflow check */
+        x = nowr + USEC_PER_DAY*7; /* 1 week from now */
+        y = map_clock_usec(x, CLOCK_REALTIME, CLOCK_MONOTONIC);
+        assert_se(y > 0 && y < USEC_INFINITY);
+        z = map_clock_usec(y, CLOCK_MONOTONIC, CLOCK_REALTIME);
+        assert_se(z > 0 && z < USEC_INFINITY);
+        assert_se((z > x ? z - x : x - z) < USEC_PER_HOUR);
+
+        assert_se(nowr > USEC_PER_DAY * 7); /* underflow check */
+        x = nowr - USEC_PER_DAY*7; /* 1 week ago */
+        y = map_clock_usec(x, CLOCK_REALTIME, CLOCK_MONOTONIC);
+        if (y != 0) { /* might underflow if machine is not up long enough for the monotonic clock to be beyond 1w */
+                assert_se(y < USEC_INFINITY);
+                z = map_clock_usec(y, CLOCK_MONOTONIC, CLOCK_REALTIME);
+                assert_se(z > 0 && z < USEC_INFINITY);
+                assert_se((z > x ? z - x : x - z) < USEC_PER_HOUR);
+        }
+}
+
 int main(int argc, char *argv[]) {
         test_setup_logging(LOG_INFO);
 
@@ -511,6 +543,7 @@ int main(int argc, char *argv[]) {
         test_deserialize_dual_timestamp();
         test_usec_shift_clock();
         test_in_utc_timezone();
+        test_map_clock_usec();
 
         /* Ensure time_t is signed */
         assert_cc((time_t) -1 < (time_t) 1);
index e87a8ec03405aff260103d8865ff571fbcc37fcd..c0b215dadc4020dc262cee426b56286e73651ca3 100644 (file)
@@ -17,6 +17,7 @@
 #include "log.h"
 #include "main-func.h"
 #include "mkdir.h"
+#include "namespace-util.h"
 #include "selinux-util.h"
 #include "signal-util.h"
 #include "string-util.h"
@@ -36,15 +37,13 @@ static int fake_filesystems(void) {
                 { "test/run",       "/etc/udev/rules.d",       "Failed to mount empty /etc/udev/rules.d",          true },
                 { "test/run",       UDEVLIBEXECDIR "/rules.d", "Failed to mount empty " UDEVLIBEXECDIR "/rules.d", true },
         };
-        unsigned i;
-
-        if (unshare(CLONE_NEWNS) < 0)
-                return log_error_errno(errno, "Failed to call unshare(): %m");
+        int r;
 
-        if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0)
-                return log_error_errno(errno, "Failed to mount / as private: %m");
+        r = detach_mount_namespace();
+        if (r < 0)
+                return log_error_errno(r, "Failed to detach mount namespace: %m");
 
-        for (i = 0; i < ELEMENTSOF(fakefss); i++)
+        for (size_t i = 0; i < ELEMENTSOF(fakefss); i++)
                 if (mount(fakefss[i].src, fakefss[i].target, NULL, MS_BIND, NULL) < 0) {
                         log_full_errno(fakefss[i].ignore_mount_error ? LOG_DEBUG : LOG_ERR, errno, "%s: %m", fakefss[i].error);
                         if (!fakefss[i].ignore_mount_error)
@@ -82,12 +81,15 @@ static int run(int argc, char *argv[]) {
         }
 
         log_debug("version %s", GIT_VERSION);
-        mac_selinux_init();
+
+        r = mac_selinux_init();
+        if (r < 0)
+                return r;
 
         action = argv[1];
         devpath = argv[2];
 
-        assert_se(udev_rules_new(&rules, RESOLVE_NAME_EARLY) == 0);
+        assert_se(udev_rules_load(&rules, RESOLVE_NAME_EARLY) == 0);
 
         const char *syspath = strjoina("/sys", devpath);
         r = device_new_from_synthetic_event(&dev, syspath, action);
@@ -122,8 +124,8 @@ static int run(int argc, char *argv[]) {
                 }
         }
 
-        udev_event_execute_rules(event, 3 * USEC_PER_SEC, NULL, rules);
-        udev_event_execute_run(event, 3 * USEC_PER_SEC);
+        udev_event_execute_rules(event, 3 * USEC_PER_SEC, SIGKILL, NULL, rules);
+        udev_event_execute_run(event, 3 * USEC_PER_SEC, SIGKILL);
 
         return 0;
 }
index 6ab5758edec6cd2110d7a2790a538bd294bce94f..02852bc089e2aa11f3541250bfc4387098bc429f 100644 (file)
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
 #include "alloc-util.h"
+#include "errno-util.h"
 #include "log.h"
 #include "path-util.h"
 #include "string-util.h"
@@ -15,8 +16,10 @@ static void test_mount_points_list(const char *fname) {
 
         log_info("/* %s(\"%s\") */", __func__, fname ?: "/proc/self/mountinfo");
 
-        if (fname)
-                fname = testdata_fname = path_join(get_testdata_dir(), fname);
+        if (fname) {
+                assert_se(get_testdata_dir(fname, &testdata_fname) >= 0);
+                fname = testdata_fname;
+        }
 
         LIST_HEAD_INIT(mp_list_head);
         assert_se(mount_points_list_get(fname, &mp_list_head) >= 0);
@@ -34,14 +37,20 @@ static void test_swap_list(const char *fname) {
         _cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, mp_list_head);
         _cleanup_free_ char *testdata_fname = NULL;
         MountPoint *m;
+        int r;
 
         log_info("/* %s(\"%s\") */", __func__, fname ?: "/proc/swaps");
 
-        if (fname)
-                fname = testdata_fname = path_join(get_testdata_dir(), fname);
+        if (fname) {
+                assert_se(get_testdata_dir(fname, &testdata_fname) >= 0);
+                fname = testdata_fname;
+        }
 
         LIST_HEAD_INIT(mp_list_head);
-        assert_se(swap_list_get(fname, &mp_list_head) >= 0);
+        r = swap_list_get(fname, &mp_list_head);
+        if (ERRNO_IS_PRIVILEGE(r))
+                return;
+        assert_se(r >= 0);
 
         LIST_FOREACH(mount_point, m, mp_list_head)
                 log_debug("path=%s o=%s f=0x%lx try-ro=%s dev=%u:%u",
index 6e294c72d627bff0ff17657522d829de2b4e0ab8..0d524f9a5626dd13511f028159f361eddd5d5f67 100644 (file)
@@ -130,6 +130,7 @@ static void test_unit_name_from_path(void) {
         test_unit_name_from_path_one("///", ".mount", "-.mount", 0);
         test_unit_name_from_path_one("/foo/../bar", ".mount", NULL, -EINVAL);
         test_unit_name_from_path_one("/foo/./bar", ".mount", NULL, -EINVAL);
+        test_unit_name_from_path_one("/waldoaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", ".mount", NULL, -EINVAL);
 }
 
 static void test_unit_name_from_path_instance_one(const char *pattern, const char *path, const char *suffix, const char *expected, int ret) {
@@ -159,6 +160,7 @@ static void test_unit_name_from_path_instance(void) {
         test_unit_name_from_path_instance_one("waldo", "..", ".mount", NULL, -EINVAL);
         test_unit_name_from_path_instance_one("waldo", "/foo", ".waldi", NULL, -EINVAL);
         test_unit_name_from_path_instance_one("wa--ldo", "/--", ".mount", "wa--ldo@\\x2d\\x2d.mount", 0);
+        test_unit_name_from_path_instance_one("waldoaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "/waldo", ".mount", NULL, -EINVAL);
 }
 
 static void test_unit_name_to_path_one(const char *unit, const char *path, int ret) {
index 76dd72a59807a81abb2ec309a234b3991cadbcf5..d4a6c8f5c339029c3349c3dce31213307668dd9c 100644 (file)
@@ -410,6 +410,85 @@ static void test_system_tasks_max_scale(void) {
         assert_se(system_tasks_max_scale(UINT64_MAX/4, UINT64_MAX) == UINT64_MAX);
 }
 
+static void test_foreach_pointer(void) {
+        int a, b, c, *i;
+        size_t k = 0;
+
+        FOREACH_POINTER(i, &a, &b, &c) {
+                switch (k) {
+
+                case 0:
+                        assert_se(i == &a);
+                        break;
+
+                case 1:
+                        assert_se(i == &b);
+                        break;
+
+                case 2:
+                        assert_se(i == &c);
+                        break;
+
+                default:
+                        assert_not_reached("unexpected index");
+                        break;
+                }
+
+                k++;
+        }
+
+        assert(k == 3);
+
+        FOREACH_POINTER(i, &b) {
+                assert(k == 3);
+                assert(i == &b);
+                k = 4;
+        }
+
+        assert(k == 4);
+
+        FOREACH_POINTER(i, NULL, &c, NULL, &b, NULL, &a, NULL) {
+                switch (k) {
+
+                case 4:
+                        assert_se(i == NULL);
+                        break;
+
+                case 5:
+                        assert_se(i == &c);
+                        break;
+
+                case 6:
+                        assert_se(i == NULL);
+                        break;
+
+                case 7:
+                        assert_se(i == &b);
+                        break;
+
+                case 8:
+                        assert_se(i == NULL);
+                        break;
+
+                case 9:
+                        assert_se(i == &a);
+                        break;
+
+                case 10:
+                        assert_se(i == NULL);
+                        break;
+
+                default:
+                        assert_not_reached("unexpected index");
+                        break;
+                }
+
+                k++;
+        }
+
+        assert(k == 11);
+}
+
 int main(int argc, char *argv[]) {
         test_setup_logging(LOG_INFO);
 
@@ -428,6 +507,7 @@ int main(int argc, char *argv[]) {
         test_physical_memory_scale();
         test_system_tasks_max();
         test_system_tasks_max_scale();
+        test_foreach_pointer();
 
         return 0;
 }
index bad289767d212b250ecc274ed2d2194e31e2ae4d..28ecffb0c0bb187adf7b063fe8c8d2d18617e7fe 100644 (file)
@@ -20,7 +20,10 @@ int main(int argc, char *argv[]) {
         if (r == -ENOMEDIUM)
                 return log_tests_skipped("cgroupfs not available");
 
-        assert_se(set_unit_path(get_testdata_dir()) >= 0);
+        _cleanup_free_ char *unit_dir = NULL;
+        assert_se(get_testdata_dir("units/", &unit_dir) >= 0);
+        assert_se(set_unit_path(unit_dir) >= 0);
+
         assert_se(runtime_dir = setup_fake_runtime_dir());
 
         assert_se(manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m) >= 0);
diff --git a/src/test/test-xdg-autostart.c b/src/test/test-xdg-autostart.c
new file mode 100644 (file)
index 0000000..70287b3
--- /dev/null
@@ -0,0 +1,93 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "fs-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "tests.h"
+#include "tmpfile-util.h"
+#include "xdg-autostart-service.h"
+
+static void test_translate_name(void) {
+        _cleanup_free_ char *t;
+
+        assert_se(t = xdg_autostart_service_translate_name("a-b.blub.desktop"));
+        assert_se(streq(t, "app-a\\x2db.blub-autostart.service"));
+}
+
+static void test_xdg_format_exec_start_one(const char *exec, const char *expected) {
+        _cleanup_free_ char* out = NULL;
+
+        xdg_autostart_format_exec_start(exec, &out);
+        log_info("In: '%s', out: '%s', expected: '%s'", exec, out, expected);
+        assert_se(streq(out, expected));
+}
+
+static void test_xdg_format_exec_start(void) {
+        test_xdg_format_exec_start_one("/bin/sleep 100", "/bin/sleep \"100\"");
+
+        /* All standardised % identifiers are stripped. */
+        test_xdg_format_exec_start_one("/bin/sleep %f \"%F\" %u %U %d %D\t%n %N %i %c %k %v %m", "/bin/sleep");
+
+        /* Unknown % identifier currently remain, but are escaped. */
+        test_xdg_format_exec_start_one("/bin/sleep %X \"%Y\"", "/bin/sleep \"%%X\" \"%%Y\"");
+
+        test_xdg_format_exec_start_one("/bin/sleep \";\\\"\"", "/bin/sleep \";\\\"\"");
+}
+
+static const char* const xdg_desktop_file[] = {
+        "[Desktop Entry]\n"
+        "Exec\t =\t /bin/sleep 100\n" /* Whitespace Before/After = must be ignored */
+        "OnlyShowIn = A;B;\n"
+        "NotShowIn=C;;D\\\\\\;;E\n", /* "C", "", "D\;", "E" */
+
+        "[Desktop Entry]\n"
+        "Exec=a\n"
+        "Exec=b\n",
+
+        "[Desktop Entry]\n"
+        "Hidden=\t true\n",
+};
+
+static void test_xdg_desktop_parse(unsigned i, const char *s) {
+        _cleanup_(unlink_tempfilep) char name[] = "/tmp/test-xdg-autostart-parser.XXXXXX";
+        _cleanup_fclose_ FILE *f = NULL;
+        _cleanup_(xdg_autostart_service_freep) XdgAutostartService *service = NULL;
+
+        log_info("== %s[%i] ==", __func__, i);
+
+        assert_se(fmkostemp_safe(name, "r+", &f) == 0);
+        assert_se(fwrite(s, strlen(s), 1, f) == 1);
+        rewind(f);
+
+        assert_se(service = xdg_autostart_service_parse_desktop(name));
+
+        switch (i) {
+        case 0:
+                assert_se(streq(service->exec_string, "/bin/sleep 100"));
+                assert_se(strv_equal(service->only_show_in, STRV_MAKE("A", "B")));
+                assert_se(strv_equal(service->not_show_in, STRV_MAKE("C", "D\\;", "E")));
+                assert_se(!service->hidden);
+                break;
+        case 1:
+                /* The second entry is not permissible and will be ignored (and error logged). */
+                assert_se(streq(service->exec_string, "a"));
+                break;
+        case 2:
+                assert_se(service->hidden);
+                break;
+        }
+}
+
+int main(int argc, char *argv[]) {
+        test_setup_logging(LOG_DEBUG);
+
+        test_translate_name();
+        test_xdg_format_exec_start();
+
+        for (size_t i = 0; i < ELEMENTSOF(xdg_desktop_file); i++)
+                test_xdg_desktop_parse(i, xdg_desktop_file[i]);
+
+        return 0;
+}
index 84962248481e9960d7b1a57982dae711203be5c3..7afc37d9e338c81a87e5c817f095f7374ba7542f 100644 (file)
@@ -9,15 +9,17 @@
 #include "sd-bus.h"
 
 #include "bus-error.h"
-#include "bus-util.h"
+#include "bus-locator.h"
+#include "bus-map-properties.h"
+#include "bus-print-properties.h"
 #include "format-table.h"
 #include "in-addr-util.h"
 #include "main-func.h"
 #include "pager.h"
 #include "parse-util.h"
 #include "pretty-print.h"
-#include "spawn-polkit-agent.h"
 #include "sparse-endian.h"
+#include "spawn-polkit-agent.h"
 #include "string-table.h"
 #include "strv.h"
 #include "terminal-util.h"
@@ -158,7 +160,7 @@ static int print_status_info(const StatusInfo *i) {
 
         r = table_print(table, NULL);
         if (r < 0)
-                return log_error_errno(r, "Failed to show table: %m");
+                return table_log_print_error(r);
 
         if (i->rtc_local)
                 printf("\n%s"
@@ -239,14 +241,13 @@ static int set_time(int argc, char **argv, void *userdata) {
         if (r < 0)
                 return log_error_errno(r, "Failed to parse time specification '%s': %m", argv[1]);
 
-        r = sd_bus_call_method(bus,
-                               "org.freedesktop.timedate1",
-                               "/org/freedesktop/timedate1",
-                               "org.freedesktop.timedate1",
-                               "SetTime",
-                               &error,
-                               NULL,
-                               "xbb", (int64_t) t, relative, interactive);
+        r = bus_call_method(
+                        bus,
+                        bus_timedate,
+                        "SetTime",
+                        &error,
+                        NULL,
+                        "xbb", (int64_t) t, relative, interactive);
         if (r < 0)
                 return log_error_errno(r, "Failed to set time: %s", bus_error_message(&error, r));
 
@@ -260,14 +261,7 @@ static int set_timezone(int argc, char **argv, void *userdata) {
 
         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
 
-        r = sd_bus_call_method(bus,
-                               "org.freedesktop.timedate1",
-                               "/org/freedesktop/timedate1",
-                               "org.freedesktop.timedate1",
-                               "SetTimezone",
-                               &error,
-                               NULL,
-                               "sb", argv[1], arg_ask_password);
+        r = bus_call_method(bus, bus_timedate, "SetTimezone", &error, NULL, "sb", argv[1], arg_ask_password);
         if (r < 0)
                 return log_error_errno(r, "Failed to set time zone: %s", bus_error_message(&error, r));
 
@@ -285,14 +279,13 @@ static int set_local_rtc(int argc, char **argv, void *userdata) {
         if (b < 0)
                 return log_error_errno(b, "Failed to parse local RTC setting '%s': %m", argv[1]);
 
-        r = sd_bus_call_method(bus,
-                               "org.freedesktop.timedate1",
-                               "/org/freedesktop/timedate1",
-                               "org.freedesktop.timedate1",
-                               "SetLocalRTC",
-                               &error,
-                               NULL,
-                               "bbb", b, arg_adjust_system_clock, arg_ask_password);
+        r = bus_call_method(
+                        bus,
+                        bus_timedate,
+                        "SetLocalRTC",
+                        &error,
+                        NULL,
+                        "bbb", b, arg_adjust_system_clock, arg_ask_password);
         if (r < 0)
                 return log_error_errno(r, "Failed to set local RTC: %s", bus_error_message(&error, r));
 
@@ -310,14 +303,7 @@ static int set_ntp(int argc, char **argv, void *userdata) {
         if (b < 0)
                 return log_error_errno(b, "Failed to parse NTP setting '%s': %m", argv[1]);
 
-        r = sd_bus_call_method(bus,
-                               "org.freedesktop.timedate1",
-                               "/org/freedesktop/timedate1",
-                               "org.freedesktop.timedate1",
-                               "SetNTP",
-                               &error,
-                               NULL,
-                               "bb", b, arg_ask_password);
+        r = bus_call_method(bus, bus_timedate, "SetNTP", &error, NULL, "bb", b, arg_ask_password);
         if (r < 0)
                 return log_error_errno(r, "Failed to set ntp: %s", bus_error_message(&error, r));
 
@@ -331,14 +317,7 @@ static int list_timezones(int argc, char **argv, void *userdata) {
         int r;
         char** zones;
 
-        r = sd_bus_call_method(bus,
-                               "org.freedesktop.timedate1",
-                               "/org/freedesktop/timedate1",
-                               "org.freedesktop.timedate1",
-                               "ListTimezones",
-                               &error,
-                               &reply,
-                               NULL);
+        r = bus_call_method(bus, bus_timedate, "ListTimezones", &error, &reply, NULL);
         if (r < 0)
                 return log_error_errno(r, "Failed to request list of time zones: %s",
                                        bus_error_message(&error, r));
@@ -386,10 +365,9 @@ static const char * const ntp_leap_table[4] = {
         [3] = "not synchronized",
 };
 
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wtype-limits"
+DISABLE_WARNING_TYPE_LIMITS;
 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(ntp_leap, uint32_t);
-#pragma GCC diagnostic pop
+REENABLE_WARNING;
 
 static int print_ntp_status_info(NTPStatusInfo *i) {
         char ts[FORMAT_TIMESPAN_MAX], jitter[FORMAT_TIMESPAN_MAX],
@@ -431,7 +409,7 @@ static int print_ntp_status_info(NTPStatusInfo *i) {
         if (r < 0)
                 return table_log_add_error(r);
 
-        r = table_add_cell_stringf(table, NULL, "%s (%s)", i->server_address, i->server_name);
+        r = table_add_cell_stringf(table, NULL, "%s (%s)", strna(i->server_address), strna(i->server_name));
         if (r < 0)
                 return table_log_add_error(r);
 
@@ -455,7 +433,7 @@ static int print_ntp_status_info(NTPStatusInfo *i) {
 
                 r = table_print(table, NULL);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to show table: %m");
+                        return table_log_print_error(r);
 
                 return 0;
         }
@@ -464,7 +442,7 @@ static int print_ntp_status_info(NTPStatusInfo *i) {
                 log_error("Invalid NTP response");
                 r = table_print(table, NULL);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to show table: %m");
+                        return table_log_print_error(r);
 
                 return 0;
         }
@@ -548,7 +526,7 @@ static int print_ntp_status_info(NTPStatusInfo *i) {
 
         r = table_print(table, NULL);
         if (r < 0)
-                log_error_errno(r, "Failed to show table: %m");
+                return table_log_print_error(r);
 
         return 0;
 }
@@ -686,7 +664,7 @@ static int on_properties_changed(sd_bus_message *m, void *userdata, sd_bus_error
 
         r = sd_bus_message_read(m, "s", &name);
         if (r < 0)
-                return log_error_errno(r, "Failed to read interface name: %m");
+                return bus_log_parse_error(r);
 
         if (!streq_ptr(name, "org.freedesktop.timesync1.Manager"))
                 return 0;
@@ -843,15 +821,7 @@ static int parse_ifindex_bus(sd_bus *bus, const char *str) {
                 return r;
         assert(r < 0);
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.network1",
-                        "/org/freedesktop/network1",
-                        "org.freedesktop.network1.Manager",
-                        "GetLinkByName",
-                        &error,
-                        &reply,
-                        "s", str);
+        r = bus_call_method(bus, bus_network_mgr, "GetLinkByName", &error, &reply, "s", str);
         if (r < 0)
                 return log_error_errno(r, "Failed to get ifindex of interfaces %s: %s", str, bus_error_message(&error, r));
 
@@ -876,13 +846,7 @@ static int verb_ntp_servers(int argc, char **argv, void *userdata) {
 
         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
 
-        r = sd_bus_message_new_method_call(
-                        bus,
-                        &req,
-                        "org.freedesktop.network1",
-                        "/org/freedesktop/network1",
-                        "org.freedesktop.network1.Manager",
-                        "SetLinkNTP");
+        r = bus_message_new_method_call(bus, &req, bus_network_mgr, "SetLinkNTP");
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -914,15 +878,7 @@ static int verb_revert(int argc, char **argv, void *userdata) {
 
         polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.network1",
-                        "/org/freedesktop/network1",
-                        "org.freedesktop.network1.Manager",
-                        "RevertLinkNTP",
-                        &error,
-                        NULL,
-                        "i", ifindex);
+        r = bus_call_method(bus, bus_network_mgr, "RevertLinkNTP", &error, NULL, "i", ifindex);
         if (r < 0)
                 return log_error_errno(r, "Failed to revert interface configuration: %s", bus_error_message(&error, r));
 
@@ -1098,9 +1054,7 @@ static int run(int argc, char *argv[]) {
         int r;
 
         setlocale(LC_ALL, "");
-        log_show_color(true);
-        log_parse_environment();
-        log_open();
+        log_setup_cli();
 
         r = parse_argv(argc, argv);
         if (r <= 0)
@@ -1108,7 +1062,7 @@ static int run(int argc, char *argv[]) {
 
         r = bus_connect_transport(arg_transport, arg_host, false, &bus);
         if (r < 0)
-                return log_error_errno(r, "Failed to create bus connection: %m");
+                return bus_log_connect_error(r);
 
         return timedatectl_main(bus, argc, argv);
 }
index 5e2fb50d8371b5138f0a2cafc1b1f74d147c1b75..c467b854775aa9be022c00aeb0ef13ffc456aac5 100644 (file)
 #include "alloc-util.h"
 #include "bus-common-errors.h"
 #include "bus-error.h"
+#include "bus-get-properties.h"
+#include "bus-locator.h"
+#include "bus-log-control-api.h"
+#include "bus-map-properties.h"
 #include "bus-polkit.h"
 #include "clock-util.h"
 #include "conf-files.h"
@@ -27,6 +31,7 @@
 #include "missing_capability.h"
 #include "path-util.h"
 #include "selinux-util.h"
+#include "service-util.h"
 #include "signal-util.h"
 #include "string-util.h"
 #include "strv.h"
@@ -374,7 +379,10 @@ static int context_write_data_local_rtc(Context *c) {
                 }
         }
 
-        mac_selinux_init();
+        r = mac_selinux_init();
+        if (r < 0)
+                return r;
+
         return write_string_file_atomic_label("/etc/adjtime", w);
 }
 
@@ -468,11 +476,9 @@ static int unit_start_or_stop(UnitStatusInfo *u, sd_bus *bus, sd_bus_error *erro
         assert(bus);
         assert(error);
 
-        r = sd_bus_call_method(
+        r = bus_call_method(
                 bus,
-                "org.freedesktop.systemd1",
-                "/org/freedesktop/systemd1",
-                "org.freedesktop.systemd1.Manager",
+                bus_systemd_mgr,
                 start ? "StartUnit" : "StopUnit",
                 error,
                 &reply,
@@ -512,11 +518,9 @@ static int unit_enable_or_disable(UnitStatusInfo *u, sd_bus *bus, sd_bus_error *
         log_unit_info(u, "%s unit.", enable ? "Enabling" : "Disabling");
 
         if (enable)
-                r = sd_bus_call_method(
+                r = bus_call_method(
                                 bus,
-                                "org.freedesktop.systemd1",
-                                "/org/freedesktop/systemd1",
-                                "org.freedesktop.systemd1.Manager",
+                                bus_systemd_mgr,
                                 "EnableUnitFiles",
                                 error,
                                 NULL,
@@ -524,11 +528,9 @@ static int unit_enable_or_disable(UnitStatusInfo *u, sd_bus *bus, sd_bus_error *
                                 u->name,
                                 false, true);
         else
-                r = sd_bus_call_method(
+                r = bus_call_method(
                                 bus,
-                                "org.freedesktop.systemd1",
-                                "/org/freedesktop/systemd1",
-                                "org.freedesktop.systemd1.Manager",
+                                bus_systemd_mgr,
                                 "DisableUnitFiles",
                                 error,
                                 NULL,
@@ -538,15 +540,7 @@ static int unit_enable_or_disable(UnitStatusInfo *u, sd_bus *bus, sd_bus_error *
         if (r < 0)
                 return r;
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "Reload",
-                        error,
-                        NULL,
-                        NULL);
+        r = bus_call_method(bus, bus_systemd_mgr, "Reload", error, NULL, NULL);
         if (r < 0)
                 return r;
 
@@ -949,12 +943,10 @@ static int method_set_ntp(sd_bus_message *m, void *userdata, sd_bus_error *error
                 u->path = mfree(u->path);
 
         if (!c->slot_job_removed) {
-                r = sd_bus_match_signal_async(
+                r = bus_match_signal_async(
                                 bus,
                                 &slot,
-                                "org.freedesktop.systemd1",
-                                "/org/freedesktop/systemd1",
-                                "org.freedesktop.systemd1.Manager",
+                                bus_systemd_mgr,
                                 "JobRemoved",
                                 match_job_removed, NULL, c);
                 if (r < 0)
@@ -1035,6 +1027,7 @@ static int method_list_timezones(sd_bus_message *m, void *userdata, sd_bus_error
 
 static const sd_bus_vtable timedate_vtable[] = {
         SD_BUS_VTABLE_START(0),
+
         SD_BUS_PROPERTY("Timezone", "s", NULL, offsetof(Context, zone), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("LocalRTC", "b", bus_property_get_bool, offsetof(Context, local_rtc), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
         SD_BUS_PROPERTY("CanNTP", "b", property_get_can_ntp, 0, 0),
@@ -1042,14 +1035,53 @@ static const sd_bus_vtable timedate_vtable[] = {
         SD_BUS_PROPERTY("NTPSynchronized", "b", property_get_ntp_sync, 0, 0),
         SD_BUS_PROPERTY("TimeUSec", "t", property_get_time, 0, 0),
         SD_BUS_PROPERTY("RTCTimeUSec", "t", property_get_rtc_time, 0, 0),
-        SD_BUS_METHOD("SetTime", "xbb", NULL, method_set_time, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetTimezone", "sb", NULL, method_set_timezone, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetLocalRTC", "bbb", NULL, method_set_local_rtc, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("SetNTP", "bb", NULL, method_set_ntp, SD_BUS_VTABLE_UNPRIVILEGED),
-        SD_BUS_METHOD("ListTimezones", NULL, "as", method_list_timezones, SD_BUS_VTABLE_UNPRIVILEGED),
+
+        SD_BUS_METHOD_WITH_NAMES("SetTime",
+                                 "xbb",
+                                 SD_BUS_PARAM(usec_utc)
+                                 SD_BUS_PARAM(relative)
+                                 SD_BUS_PARAM(interactive),
+                                 NULL,,
+                                 method_set_time,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("SetTimezone",
+                                 "sb",
+                                 SD_BUS_PARAM(timezone)
+                                 SD_BUS_PARAM(interactive),
+                                 NULL,,
+                                 method_set_timezone,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("SetLocalRTC",
+                                 "bbb",
+                                 SD_BUS_PARAM(local_rtc)
+                                 SD_BUS_PARAM(fix_system)
+                                 SD_BUS_PARAM(interactive),
+                                 NULL,,
+                                 method_set_local_rtc,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("SetNTP",
+                                 "bb",
+                                 SD_BUS_PARAM(use_ntp)
+                                 SD_BUS_PARAM(interactive),
+                                 NULL,,
+                                 method_set_ntp,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD_WITH_NAMES("ListTimezones",
+                                 NULL,,
+                                 "as",
+                                 SD_BUS_PARAM(timezones),
+                                 method_list_timezones,
+                                 SD_BUS_VTABLE_UNPRIVILEGED),
+
         SD_BUS_VTABLE_END,
 };
 
+const BusObjectImplementation manager_object = {
+        "/org/freedesktop/timedate1",
+        "org.freedesktop.timedate1",
+        .vtables = BUS_VTABLES(timedate_vtable),
+};
+
 static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
         int r;
@@ -1062,9 +1094,13 @@ static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) {
         if (r < 0)
                 return log_error_errno(r, "Failed to get system bus connection: %m");
 
-        r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", timedate_vtable, c);
+        r = bus_add_implementation(bus, &manager_object, c);
         if (r < 0)
-                return log_error_errno(r, "Failed to register object: %m");
+                return r;
+
+        r = bus_log_control_api_register(bus);
+        if (r < 0)
+                return r;
 
         r = sd_bus_request_name_async(bus, NULL, "org.freedesktop.timedate1", 0, NULL, NULL);
         if (r < 0)
@@ -1087,10 +1123,15 @@ static int run(int argc, char *argv[]) {
 
         log_setup_service();
 
-        umask(0022);
+        r = service_parse_argv("systemd-timedated.service",
+                               "Manage the system clock and timezone and NTP enablement.",
+                               BUS_IMPLEMENTATIONS(&manager_object,
+                                                   &log_control_object),
+                               argc, argv);
+        if (r <= 0)
+                return r;
 
-        if (argc != 1)
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments.");
+        umask(0022);
 
         assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
 
index 5a5896f0b778b3784a442997d3a91090ae3297b1..6effdfc3250fb76aaeae50b525b370643970e236 100644 (file)
@@ -3,7 +3,9 @@
 #include "sd-bus.h"
 
 #include "alloc-util.h"
+#include "bus-get-properties.h"
 #include "bus-internal.h"
+#include "bus-log-control-api.h"
 #include "bus-protocol.h"
 #include "bus-util.h"
 #include "in-addr-util.h"
@@ -189,6 +191,10 @@ int manager_connect_bus(Manager *m) {
         if (r < 0)
                 return log_error_errno(r, "Failed to add manager object vtable: %m");
 
+        r = bus_log_control_api_register(m->bus);
+        if (r < 0)
+                return r;
+
         r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.timesync1", 0, NULL, NULL);
         if (r < 0)
                 return log_error_errno(r, "Failed to request name: %m");
index a26c2dad7fbd494de6628aa50b7b9682ae03d71c..532d6ea7ec00f594a525ce5e49c1266a8388a36d 100644 (file)
@@ -102,11 +102,14 @@ int manager_parse_config_file(Manager *m) {
 
         assert(m);
 
-        r = config_parse_many_nulstr(PKGSYSCONFDIR "/timesyncd.conf",
-                                     CONF_PATHS_NULSTR("systemd/timesyncd.conf.d"),
-                                     "Time\0",
-                                     config_item_perf_lookup, timesyncd_gperf_lookup,
-                                     CONFIG_PARSE_WARN, m);
+        r = config_parse_many_nulstr(
+                        PKGSYSCONFDIR "/timesyncd.conf",
+                        CONF_PATHS_NULSTR("systemd/timesyncd.conf.d"),
+                        "Time\0",
+                        config_item_perf_lookup, timesyncd_gperf_lookup,
+                        CONFIG_PARSE_WARN,
+                        m,
+                        NULL);
         if (r < 0)
                 return r;
 
index 2980f79b16f8388b91c5ad22ffb59a01f076c161..5570408fa14f5127a37974f7c1604d84af2df16f 100644 (file)
@@ -137,11 +137,10 @@ static int manager_send_request(Manager *m) {
         }
 
         /* re-arm timer with increasing timeout, in case the packets never arrive back */
-        if (m->retry_interval > 0) {
-                if (m->retry_interval < m->poll_interval_max_usec)
-                        m->retry_interval *= 2;
-        } else
-                m->retry_interval = m->poll_interval_min_usec;
+        if (m->retry_interval == 0)
+                m->retry_interval = NTP_RETRY_INTERVAL_MIN_USEC;
+        else
+                m->retry_interval = MIN(m->retry_interval * 4/3, NTP_RETRY_INTERVAL_MAX_USEC);
 
         r = manager_arm_timer(m, m->retry_interval);
         if (r < 0)
@@ -407,10 +406,7 @@ static int manager_receive_response(sd_event_source *source, int fd, uint32_t re
                 .iov_base = &ntpmsg,
                 .iov_len = sizeof(ntpmsg),
         };
-        union {
-                struct cmsghdr cmsghdr;
-                uint8_t buf[CMSG_SPACE(sizeof(struct timeval))];
-        } control;
+        CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct timeval))) control;
         union sockaddr_union server_addr;
         struct msghdr msghdr = {
                 .msg_iov = &iov,
index 97c4e2ff34d90f65b0b2e1392311151bdd491cea..d74521c9cf59a64abeca0031bb12c20979eed71b 100644 (file)
@@ -24,6 +24,9 @@ typedef struct Manager Manager;
 #define NTP_POLL_INTERVAL_MIN_USEC      (32 * USEC_PER_SEC)
 #define NTP_POLL_INTERVAL_MAX_USEC      (2048 * USEC_PER_SEC)
 
+#define NTP_RETRY_INTERVAL_MIN_USEC     (15 * USEC_PER_SEC)
+#define NTP_RETRY_INTERVAL_MAX_USEC     (6 * 60 * USEC_PER_SEC) /* 6 minutes */
+
 struct Manager {
         sd_bus *bus;
         sd_event *event;
index e56e09ca8cec5255a83a9531530b53a8654ab4e6..4f514d536d0b73a00e6eecbd359711f394d46ff5 100644 (file)
@@ -89,8 +89,8 @@ settime:
 }
 
 static int run(int argc, char *argv[]) {
-        _cleanup_(notify_on_cleanup) const char *notify_message = NULL;
         _cleanup_(manager_freep) Manager *m = NULL;
+        _cleanup_(notify_on_cleanup) const char *notify_message = NULL;
         const char *user = "systemd-timesync";
         uid_t uid, uid_current;
         gid_t gid;
diff --git a/src/tmpfiles/meson.build b/src/tmpfiles/meson.build
new file mode 100644 (file)
index 0000000..434dcf8
--- /dev/null
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1+
+
+systemd_tmpfiles_sources = [
+        'src/tmpfiles/tmpfiles.c',
+        'src/shared/offline-passwd.c',
+        'src/shared/offline-passwd.h',
+]
index 3325087b74b72a110ad2c9da0b76ac8607b5b18f..2404e36bf2984873eeb709721115d7ad91a560bc 100644 (file)
@@ -39,6 +39,7 @@
 #include "main-func.h"
 #include "mkdir.h"
 #include "mountpoint-util.h"
+#include "offline-passwd.h"
 #include "pager.h"
 #include "parse-util.h"
 #include "path-lookup.h"
@@ -184,7 +185,13 @@ static const Specifier specifier_table[] = {
         { 'm', specifier_machine_id_safe, NULL },
         { 'b', specifier_boot_id,         NULL },
         { 'H', specifier_host_name,       NULL },
+        { 'l', specifier_short_host_name, NULL },
         { 'v', specifier_kernel_release,  NULL },
+        { 'a', specifier_architecture,    NULL },
+        { 'o', specifier_os_id,           NULL },
+        { 'w', specifier_os_version_id,   NULL },
+        { 'B', specifier_os_build_id,     NULL },
+        { 'W', specifier_os_variant_id,   NULL },
 
         { 'g', specifier_group_name,      NULL },
         { 'G', specifier_group_id,        NULL },
@@ -244,7 +251,7 @@ static int specifier_directory(char specifier, const void *data, const void *use
         i = PTR_TO_UINT(data);
         assert(i < ELEMENTSOF(paths_system));
 
-        return sd_path_home(paths[i].type, paths[i].suffix, ret);
+        return sd_path_lookup(paths[i].type, paths[i].suffix, ret);
 }
 
 static int log_unresolvable_specifier(const char *filename, unsigned line) {
@@ -256,10 +263,11 @@ static int log_unresolvable_specifier(const char *filename, unsigned line) {
          * not considered as an error so log at LOG_NOTICE only for the first time
          * and then downgrade this to LOG_DEBUG for the rest. */
 
-        log_full(notified ? LOG_DEBUG : LOG_NOTICE,
-                 "[%s:%u] Failed to resolve specifier: %s, skipping",
-                 filename, line,
-                 arg_user ? "Required $XDG_... variable not defined" : "uninitialized /etc detected");
+        log_syntax(NULL,
+                   notified ? LOG_DEBUG : LOG_NOTICE,
+                   filename, line, 0,
+                   "Failed to resolve specifier: %s, skipping",
+                   arg_user ? "Required $XDG_... variable not defined" : "uninitialized /etc detected");
 
         if (!notified)
                 log_notice("All rules containing unresolvable specifiers will be skipped.");
@@ -1078,6 +1086,11 @@ static int fd_set_acls(Item *item, int fd, const char *path, const struct stat *
 
         if (r > 0)
                 return -r; /* already warned */
+
+        /* The above procfs paths don't work if /proc is not mounted. */
+        if (r == -ENOENT && proc_mounted() == 0)
+                r = -ENOSYS;
+
         if (r == -EOPNOTSUPP) {
                 log_debug_errno(r, "ACLs not supported by file system at %s", path);
                 return 0;
@@ -1252,7 +1265,7 @@ static int path_set_attribute(Item *item, const char *path) {
 static int write_one_file(Item *i, const char *path) {
         _cleanup_close_ int fd = -1, dir_fd = -1;
         char *bn;
-        int flags, r;
+        int r;
 
         assert(i);
         assert(path);
@@ -1267,15 +1280,19 @@ static int write_one_file(Item *i, const char *path) {
 
         bn = basename(path);
 
-        flags = O_NONBLOCK|O_CLOEXEC|O_WRONLY|O_NOCTTY;
-
         /* Follows symlinks */
-        fd = openat(dir_fd, bn, i->append_or_force ? flags|O_APPEND : flags, i->mode);
+        fd = openat(dir_fd, bn,
+                    O_NONBLOCK|O_CLOEXEC|O_WRONLY|O_NOCTTY|(i->append_or_force ? O_APPEND : 0),
+                    i->mode);
         if (fd < 0) {
                 if (errno == ENOENT) {
                         log_debug_errno(errno, "Not writing missing file \"%s\": %m", path);
                         return 0;
                 }
+
+                if (i->allow_failure)
+                        return log_debug_errno(errno, "Failed to open file \"%s\", ignoring: %m", path);
+
                 return log_error_errno(errno, "Failed to open file \"%s\": %m", path);
         }
 
@@ -1612,7 +1629,7 @@ static int create_subvolume(Item *i, const char *path) {
                         log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (unsupported fs or dir not a subvolume): %m", i->path);
                 else if (r == -EROFS)
                         log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (fs is read-only).", i->path);
-                else if (r == -ENOPROTOOPT)
+                else if (r == -ENOTCONN)
                         log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (quota support is disabled).", i->path);
                 else if (r < 0)
                         q = log_error_errno(r, "Failed to adjust quota for subvolume \"%s\": %m", i->path);
@@ -2388,8 +2405,7 @@ static bool should_include_path(const char *path) {
                         return true;
                 }
 
-        /* no matches, so we should include this path only if we
-         * have no whitelist at all */
+        /* no matches, so we should include this path only if we have no allow list at all */
         if (strv_isempty(arg_include_prefixes))
                 return true;
 
@@ -2426,9 +2442,7 @@ static int specifier_expansion_from_arg(Item *i) {
 
         case SET_XATTR:
         case RECURSIVE_SET_XATTR:
-                assert(i->xattrs);
-
-                STRV_FOREACH (xattr, i->xattrs) {
+                STRV_FOREACH(xattr, i->xattrs) {
                         r = specifier_printf(*xattr, specifier_table, NULL, &resolved);
                         if (r < 0)
                                 return r;
@@ -2478,7 +2492,63 @@ static int patch_var_run(const char *fname, unsigned line, char **path) {
         return 0;
 }
 
-static int parse_line(const char *fname, unsigned line, const char *buffer, bool *invalid_config) {
+static int find_uid(const char *user, uid_t *ret_uid, Hashmap **cache) {
+        int r;
+
+        assert(user);
+        assert(ret_uid);
+
+        /* First: parse as numeric UID string */
+        r = parse_uid(user, ret_uid);
+        if (r >= 0)
+                return r;
+
+        /* Second: pass to NSS if we are running "online" */
+        if (!arg_root)
+                return get_user_creds(&user, ret_uid, NULL, NULL, NULL, 0);
+
+        /* Third, synthesize "root" unconditionally */
+        if (streq(user, "root")) {
+                *ret_uid = 0;
+                return 0;
+        }
+
+        /* Fourth: use fgetpwent() to read /etc/passwd directly, if we are "offline" */
+        return name_to_uid_offline(arg_root, user, ret_uid, cache);
+}
+
+static int find_gid(const char *group, gid_t *ret_gid, Hashmap **cache) {
+        int r;
+
+        assert(group);
+        assert(ret_gid);
+
+        /* First: parse as numeric GID string */
+        r = parse_gid(group, ret_gid);
+        if (r >= 0)
+                return r;
+
+        /* Second: pass to NSS if we are running "online" */
+        if (!arg_root)
+                return get_group_creds(&group, ret_gid, 0);
+
+        /* Third, synthesize "root" unconditionally */
+        if (streq(group, "root")) {
+                *ret_gid = 0;
+                return 0;
+        }
+
+        /* Fourth: use fgetgrent() to read /etc/group directly, if we are "offline" */
+        return name_to_gid_offline(arg_root, group, ret_gid, cache);
+}
+
+static int parse_line(
+                const char *fname,
+                unsigned line,
+                const char *buffer,
+                bool *invalid_config,
+                Hashmap **uid_cache,
+                Hashmap **gid_cache) {
 
         _cleanup_free_ char *action = NULL, *mode = NULL, *user = NULL, *group = NULL, *age = NULL, *path = NULL;
         _cleanup_(item_free_contents) Item i = {};
@@ -2506,11 +2576,10 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool
                 if (IN_SET(r, -EINVAL, -EBADSLT))
                         /* invalid quoting and such or an unknown specifier */
                         *invalid_config = true;
-                return log_error_errno(r, "[%s:%u] Failed to parse line: %m", fname, line);
+                return log_syntax(NULL, LOG_ERR, fname, line, r, "Failed to parse line: %m");
         } else if (r < 2) {
                 *invalid_config = true;
-                log_error("[%s:%u] Syntax error.", fname, line);
-                return -EIO;
+                return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "Syntax error.");
         }
 
         if (!empty_or_dash(buffer)) {
@@ -2521,8 +2590,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool
 
         if (isempty(action)) {
                 *invalid_config = true;
-                log_error("[%s:%u] Command too short '%s'.", fname, line, action);
-                return -EINVAL;
+                return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "Command too short '%s'.", action);
         }
 
         for (pos = 1; action[pos]; pos++) {
@@ -2534,15 +2602,12 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool
                         allow_failure = true;
                 else {
                         *invalid_config = true;
-                        log_error("[%s:%u] Unknown modifiers in command '%s'",
-                                  fname, line, action);
-                        return -EINVAL;
+                        return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "Unknown modifiers in command '%s'", action);
                 }
         }
 
         if (boot && !arg_boot) {
-                log_debug("Ignoring entry %s \"%s\" because --boot is not specified.",
-                          action, path);
+                log_syntax(NULL, LOG_DEBUG, fname, line, 0, "Ignoring entry %s \"%s\" because --boot is not specified.", action, path);
                 return 0;
         }
 
@@ -2556,7 +2621,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool
         if (r < 0) {
                 if (IN_SET(r, -EINVAL, -EBADSLT))
                         *invalid_config = true;
-                return log_error_errno(r, "[%s:%u] Failed to replace specifiers in '%s': %m", fname, line, path);
+                return log_syntax(NULL, LOG_ERR, fname, line, r, "Failed to replace specifiers in '%s': %m", path);
         }
 
         r = patch_var_run(fname, line, &i.path);
@@ -2580,7 +2645,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool
         case RELABEL_PATH:
         case RECURSIVE_RELABEL_PATH:
                 if (i.argument)
-                        log_warning("[%s:%u] %c lines don't take argument fields, ignoring.", fname, line, i.type);
+                        log_syntax(NULL, LOG_WARNING, fname, line, 0, "%c lines don't take argument fields, ignoring.", i.type);
 
                 break;
 
@@ -2599,23 +2664,23 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool
         case WRITE_FILE:
                 if (!i.argument) {
                         *invalid_config = true;
-                        log_error("[%s:%u] Write file requires argument.", fname, line);
-                        return -EBADMSG;
+                        return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "Write file requires argument.");
                 }
                 break;
 
         case COPY_FILES:
                 if (!i.argument) {
-                        i.argument = path_join(arg_root, "/usr/share/factory", i.path);
+                        i.argument = path_join("/usr/share/factory", i.path);
                         if (!i.argument)
                                 return log_oom();
 
                 } else if (!path_is_absolute(i.argument)) {
                         *invalid_config = true;
-                        log_error("[%s:%u] Source path is not absolute.", fname, line);
-                        return -EBADMSG;
+                        return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "Source path '%s' is not absolute.", i.argument);
+
+                }
 
-                } else if (arg_root) {
+                if (!empty_or_root(arg_root)) {
                         char *p;
 
                         p = path_join(arg_root, i.argument);
@@ -2631,15 +2696,13 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool
         case CREATE_BLOCK_DEVICE:
                 if (!i.argument) {
                         *invalid_config = true;
-                        log_error("[%s:%u] Device file requires argument.", fname, line);
-                        return -EBADMSG;
+                        return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG), "Device file requires argument.");
                 }
 
                 r = parse_dev(i.argument, &i.major_minor);
                 if (r < 0) {
                         *invalid_config = true;
-                        log_error_errno(r, "[%s:%u] Can't parse device file major/minor '%s'.", fname, line, i.argument);
-                        return -EBADMSG;
+                        return log_syntax(NULL, LOG_ERR, fname, line, r, "Can't parse device file major/minor '%s'.", i.argument);
                 }
 
                 break;
@@ -2648,8 +2711,8 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool
         case RECURSIVE_SET_XATTR:
                 if (!i.argument) {
                         *invalid_config = true;
-                        log_error("[%s:%u] Set extended attribute requires argument.", fname, line);
-                        return -EBADMSG;
+                        return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG),
+                                          "Set extended attribute requires argument.");
                 }
                 r = parse_xattrs_from_arg(&i);
                 if (r < 0)
@@ -2660,8 +2723,8 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool
         case RECURSIVE_SET_ACL:
                 if (!i.argument) {
                         *invalid_config = true;
-                        log_error("[%s:%u] Set ACLs requires argument.", fname, line);
-                        return -EBADMSG;
+                        return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG),
+                                          "Set ACLs requires argument.");
                 }
                 r = parse_acls_from_arg(&i);
                 if (r < 0)
@@ -2672,8 +2735,8 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool
         case RECURSIVE_SET_ATTRIBUTE:
                 if (!i.argument) {
                         *invalid_config = true;
-                        log_error("[%s:%u] Set file attribute requires argument.", fname, line);
-                        return -EBADMSG;
+                        return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG),
+                                          "Set file attribute requires argument.");
                 }
                 r = parse_attribute_from_arg(&i);
                 if (IN_SET(r, -EINVAL, -EBADSLT))
@@ -2683,15 +2746,15 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool
                 break;
 
         default:
-                log_error("[%s:%u] Unknown command type '%c'.", fname, line, (char) i.type);
                 *invalid_config = true;
-                return -EBADMSG;
+                return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG),
+                                  "Unknown command type '%c'.", (char) i.type);
         }
 
         if (!path_is_absolute(i.path)) {
-                log_error("[%s:%u] Path '%s' not absolute.", fname, line, i.path);
                 *invalid_config = true;
-                return -EBADMSG;
+                return log_syntax(NULL, LOG_ERR, fname, line, SYNTHETIC_ERRNO(EBADMSG),
+                                  "Path '%s' not absolute.", i.path);
         }
 
         path_simplify(i.path, false);
@@ -2705,11 +2768,10 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool
         if (r < 0) {
                 if (IN_SET(r, -EINVAL, -EBADSLT))
                         *invalid_config = true;
-                return log_error_errno(r, "[%s:%u] Failed to substitute specifiers in argument: %m",
-                                       fname, line);
+                return log_syntax(NULL, LOG_ERR, fname, line, r, "Failed to substitute specifiers in argument: %m");
         }
 
-        if (arg_root) {
+        if (!empty_or_root(arg_root)) {
                 char *p;
 
                 p = path_join(arg_root, i.path);
@@ -2719,25 +2781,20 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool
         }
 
         if (!empty_or_dash(user)) {
-                const char *u = user;
-
-                r = get_user_creds(&u, &i.uid, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING);
+                r = find_uid(user, &i.uid, uid_cache);
                 if (r < 0) {
                         *invalid_config = true;
-                        return log_error_errno(r, "[%s:%u] Unknown user '%s'.", fname, line, user);
+                        return log_syntax(NULL, LOG_ERR, fname, line, r, "Failed to resolve user '%s': %m", user);
                 }
 
                 i.uid_set = true;
         }
 
         if (!empty_or_dash(group)) {
-                const char *g = group;
-
-                r = get_group_creds(&g, &i.gid, USER_CREDS_ALLOW_MISSING);
+                r = find_gid(group, &i.gid, gid_cache);
                 if (r < 0) {
                         *invalid_config = true;
-                        log_error("[%s:%u] Unknown group '%s'.", fname, line, group);
-                        return r;
+                        return log_syntax(NULL, LOG_ERR, fname, line, r, "Failed to resolve group '%s'.", group);
                 }
 
                 i.gid_set = true;
@@ -2752,10 +2809,10 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool
                         mm++;
                 }
 
-                if (parse_mode(mm, &m) < 0) {
+                r = parse_mode(mm, &m);
+                if (r < 0) {
                         *invalid_config = true;
-                        log_error("[%s:%u] Invalid mode '%s'.", fname, line, mode);
-                        return -EBADMSG;
+                        return log_syntax(NULL, LOG_ERR, fname, line, r, "Invalid mode '%s'.", mode);
                 }
 
                 i.mode = m;
@@ -2771,10 +2828,10 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool
                         a++;
                 }
 
-                if (parse_sec(a, &i.age) < 0) {
+                r = parse_sec(a, &i.age);
+                if (r < 0) {
                         *invalid_config = true;
-                        log_error("[%s:%u] Invalid age '%s'.", fname, line, age);
-                        return -EBADMSG;
+                        return log_syntax(NULL, LOG_ERR, fname, line, r, "Invalid age '%s'.", age);
                 }
 
                 i.age_set = true;
@@ -2788,8 +2845,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool
 
                 for (n = 0; n < existing->n_items; n++) {
                         if (!item_compatible(existing->items + n, &i) && !i.append_or_force) {
-                                log_notice("[%s:%u] Duplicate line for path \"%s\", ignoring.",
-                                           fname, line, i.path);
+                                log_syntax(NULL, LOG_NOTICE, fname, line, 0, "Duplicate line for path \"%s\", ignoring.", i.path);
                                 return 0;
                         }
                 }
@@ -2943,7 +2999,7 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_ROOT:
-                        r = parse_path_argument_and_warn(optarg, true, &arg_root);
+                        r = parse_path_argument_and_warn(optarg, /* suppress_root= */ false, &arg_root);
                         if (r < 0)
                                 return r;
                         break;
@@ -2984,6 +3040,7 @@ static int parse_argv(int argc, char *argv[]) {
 }
 
 static int read_config_file(char **config_dirs, const char *fn, bool ignore_enoent, bool *invalid_config) {
+        _cleanup_(hashmap_freep) Hashmap *uid_cache = NULL, *gid_cache = NULL;
         _cleanup_fclose_ FILE *_f = NULL;
         Iterator iterator;
         unsigned v = 0;
@@ -3029,7 +3086,7 @@ static int read_config_file(char **config_dirs, const char *fn, bool ignore_enoe
                 if (IN_SET(*l, 0, '#'))
                         continue;
 
-                k = parse_line(fn, v, l, &invalid_line);
+                k = parse_line(fn, v, l, &invalid_line, &uid_cache, &gid_cache);
                 if (k < 0) {
                         if (invalid_line)
                                 /* Allow reporting with a special code if the caller requested this */
@@ -3138,11 +3195,7 @@ static int link_parent(ItemArray *a) {
                 if (!j)
                         j = ordered_hashmap_get(globs, prefix);
                 if (j) {
-                        r = set_ensure_allocated(&j->children, NULL);
-                        if (r < 0)
-                                return log_oom();
-
-                        r = set_put(j->children, a);
+                        r = set_ensure_put(&j->children, NULL, a);
                         if (r < 0)
                                 return log_oom();
 
@@ -3204,7 +3257,9 @@ static int run(int argc, char *argv[]) {
 
         umask(0022);
 
-        mac_selinux_init();
+        r = mac_selinux_init();
+        if (r < 0)
+                return r;
 
         items = ordered_hashmap_new(&item_array_hash_ops);
         globs = ordered_hashmap_new(&item_array_hash_ops);
index 0e33c0b48f01564792080235f43a98d692920a87..4371da4785018969eb03b21d1c6f0e7f5c66457d 100644 (file)
@@ -192,7 +192,9 @@ static int process_one_password_file(const char *filename) {
         r = config_parse(NULL, filename, NULL,
                          NULL,
                          config_item_table_lookup, items,
-                         CONFIG_PARSE_RELAXED|CONFIG_PARSE_WARN, NULL);
+                         CONFIG_PARSE_RELAXED|CONFIG_PARSE_WARN,
+                         NULL,
+                         NULL);
         if (r < 0)
                 return r;
 
@@ -393,6 +395,10 @@ static int process_and_watch_password_files(bool watch) {
                         return -errno;
                 }
 
+                if (pollfd[FD_SIGNAL].revents & POLLNVAL ||
+                    pollfd[FD_INOTIFY].revents & POLLNVAL)
+                        return -EBADF;
+
                 if (pollfd[FD_INOTIFY].revents != 0)
                         (void) flush_fd(notify);
 
index 57389372ce271f9e0c39fe986769f4e239a118ea..b5e14922a25dd8b799d0627ae96bcc71bf9336d1 100644 (file)
@@ -514,9 +514,8 @@ int main(int argc, char *argv[]) {
                                 printf("ID_TYPE=generic\n");
                                 break;
                         }
-                } else {
+                } else
                         printf("ID_TYPE=disk\n");
-                }
                 printf("ID_BUS=ata\n");
                 printf("ID_MODEL=%s\n", model);
                 printf("ID_MODEL_ENC=%s\n", model_enc);
@@ -524,9 +523,8 @@ int main(int argc, char *argv[]) {
                 if (serial[0] != '\0') {
                         printf("ID_SERIAL=%s_%s\n", model, serial);
                         printf("ID_SERIAL_SHORT=%s\n", serial);
-                } else {
+                } else
                         printf("ID_SERIAL=%s\n", model);
-                }
 
                 if (id.command_set_1 & (1<<5)) {
                         printf("ID_ATA_WRITE_CACHE=1\n");
index 173b10be5050b0a97af9d7c78421ded9a1c46afb..aa23b07090458837902e340682e3948774d18452 100644 (file)
@@ -13,10 +13,9 @@ udevadm_sources = files('''
         udevadm-trigger.c
         udevadm-util.c
         udevadm-util.h
+        udevd.c
 '''.split())
 
-systemd_udevd_sources = files('udevd.c')
-
 libudev_core_sources = '''
         udev-ctrl.c
         udev-ctrl.h
index 43d1c59b9401c80f4be2c49ae94556e6c692b6ed..60a9d21c1d11ec1b58badaecf3296030a918a62b 100644 (file)
@@ -59,4 +59,9 @@ Link.OtherChannels,              config_parse_channel,                  0,
 Link.CombinedChannels,           config_parse_channel,                  0,                             offsetof(link_config, channels)
 Link.Advertise,                  config_parse_advertise,                0,                             offsetof(link_config, advertise)
 Link.RxBufferSize,               config_parse_nic_buffer_size,          0,                             offsetof(link_config, ring)
+Link.RxMiniBufferSize,           config_parse_nic_buffer_size,          0,                             offsetof(link_config, ring)
+Link.RxJumboBufferSize,          config_parse_nic_buffer_size,          0,                             offsetof(link_config, ring)
 Link.TxBufferSize,               config_parse_nic_buffer_size,          0,                             offsetof(link_config, ring)
+Link.RxFlowControl,              config_parse_tristate,                 0,                             offsetof(link_config, rx_flow_control)
+Link.TxFlowControl,              config_parse_tristate,                 0,                             offsetof(link_config, tx_flow_control)
+Link.AutoNegotiationFlowControl, config_parse_tristate,                 0,                             offsetof(link_config, autoneg_flow_control)
index 79f963b044c28c658160281b41e6efcf4bfede15..72ef0c56be02ea569942371093ac87878ef9730d 100644 (file)
@@ -2,6 +2,7 @@
 
 #include <linux/netdevice.h>
 #include <netinet/ether.h>
+#include <unistd.h>
 
 #include "sd-device.h"
 #include "sd-netlink.h"
@@ -20,6 +21,7 @@
 #include "netlink-util.h"
 #include "network-internal.h"
 #include "parse-util.h"
+#include "path-lookup.h"
 #include "path-util.h"
 #include "proc-cmdline.h"
 #include "random-util.h"
@@ -148,6 +150,9 @@ int link_load_one(link_config_ctx *ctx, const char *filename) {
                 .duplex = _DUP_INVALID,
                 .port = _NET_DEV_PORT_INVALID,
                 .autonegotiation = -1,
+                .rx_flow_control = -1,
+                .tx_flow_control = -1,
+                .autoneg_flow_control = -1,
         };
 
         for (i = 0; i < ELEMENTSOF(link->features); i++)
@@ -156,7 +161,8 @@ int link_load_one(link_config_ctx *ctx, const char *filename) {
         r = config_parse(NULL, filename, file,
                          "Match\0Link\0",
                          config_item_perf_lookup, link_config_gperf_lookup,
-                         CONFIG_PARSE_WARN, link);
+                         CONFIG_PARSE_WARN, link,
+                         NULL);
         if (r < 0)
                 return r;
 
@@ -169,7 +175,7 @@ int link_load_one(link_config_ctx *ctx, const char *filename) {
                 return 0;
         }
 
-        if (!condition_test_list(link->conditions, NULL, NULL, NULL)) {
+        if (!condition_test_list(link->conditions, environ, NULL, NULL, NULL)) {
                 log_debug("%s: Conditions do not match the system environment, skipping.", filename);
                 return 0;
         }
@@ -323,7 +329,13 @@ static int get_mac(sd_device *device, MACAddressPolicy policy, struct ether_addr
 
         if (want_random) {
                 log_device_debug(device, "Using random bytes to generate MAC");
-                random_bytes(mac->ether_addr_octet, ETH_ALEN);
+
+                /* We require genuine randomness here, since we want to make sure we won't collide with other
+                 * systems booting up at the very same time. We do allow RDRAND however, since this is not
+                 * cryptographic key material. */
+                r = genuine_random_bytes(mac->ether_addr_octet, ETH_ALEN, RANDOM_ALLOW_RDRAND);
+                if (r < 0)
+                        return log_device_error_errno(device, r, "Failed to acquire random data to generate MAC: %m");
         } else {
                 uint64_t result;
 
@@ -346,7 +358,7 @@ static int get_mac(sd_device *device, MACAddressPolicy policy, struct ether_addr
 
 int link_config_apply(link_config_ctx *ctx, link_config *config,
                       sd_device *device, const char **name) {
-        _cleanup_strv_free_ char **altnames = NULL;
+        _cleanup_strv_free_ char **altnames = NULL, **current_altnames = NULL;
         struct ether_addr generated_mac;
         struct ether_addr *mac = NULL;
         const char *new_name = NULL;
@@ -403,12 +415,18 @@ int link_config_apply(link_config_ctx *ctx, link_config *config,
                         log_warning_errno(r, "Could not set channels of %s: %m", old_name);
         }
 
-        if (config->ring.rx_pending_set || config->ring.tx_pending_set) {
+        if (config->ring.rx_pending_set || config->ring.rx_mini_pending_set || config->ring.rx_jumbo_pending_set || config->ring.tx_pending_set) {
                 r = ethtool_set_nic_buffer_size(&ctx->ethtool_fd, old_name, &config->ring);
                 if (r < 0)
                         log_warning_errno(r, "Could not set ring buffer of %s: %m", old_name);
         }
 
+        if (config->rx_flow_control >= 0 || config->tx_flow_control >= 0 || config->autoneg_flow_control >= 0) {
+                r = ethtool_set_flow_control(&ctx->ethtool_fd, old_name, config->rx_flow_control, config->tx_flow_control, config->autoneg_flow_control);
+                if (r < 0)
+                        log_warning_errno(r, "Could not set flow control of %s: %m", old_name);
+        }
+
         r = sd_device_get_ifindex(device, &ifindex);
         if (r < 0)
                 return log_device_warning_errno(device, r, "Could not find ifindex: %m");
@@ -521,9 +539,17 @@ int link_config_apply(link_config_ctx *ctx, link_config *config,
         if (new_name)
                 strv_remove(altnames, new_name);
         strv_remove(altnames, old_name);
+
+        r = rtnl_get_link_alternative_names(&ctx->rtnl, ifindex, &current_altnames);
+        if (r < 0)
+                log_debug_errno(r, "Failed to get alternative names on %s, ignoring: %m", old_name);
+
+        char **p;
+        STRV_FOREACH(p, current_altnames)
+                strv_remove(altnames, *p);
+
         strv_uniq(altnames);
         strv_sort(altnames);
-
         r = rtnl_set_link_alternative_names(&ctx->rtnl, ifindex, altnames);
         if (r == -EOPNOTSUPP)
                 log_debug_errno(r, "Could not set AlternativeName= or apply AlternativeNamesPolicy= on %s, ignoring: %m", old_name);
index a85bd4b46b021bca937c713e2e1497fefbd5dbfc..827ebf436c715bb12bcdc774807879ca7139a9a1 100644 (file)
@@ -62,6 +62,9 @@ struct link_config {
         int features[_NET_DEV_FEAT_MAX];
         netdev_channels channels;
         netdev_ring_param ring;
+        int rx_flow_control;
+        int tx_flow_control;
+        int autoneg_flow_control;
 
         LIST_FIELDS(link_config, links);
 };
index 94c3b232e5e408e3dae2eef76d16c370400dda35..bb08da28b52273fbe1ddcc80a51c3f5e8a4bd3cd 100644 (file)
@@ -111,9 +111,8 @@ static char *get_value(char **buffer) {
                  */
                 (*buffer)++;
                 end = quote_string;
-        } else {
+        } else
                 end = comma_string;
-        }
         val = strsep(buffer, end);
         if (val && end == quote_string)
                 /*
index 8e86d2f0d1c01fbd89e3a21b97cebb4c0834de54..59ae6c7ad44ee3449235f6cecc08eaa9385e9d9d 100644 (file)
@@ -47,7 +47,7 @@ int udev_builtin_hwdb_lookup(sd_device *dev,
 }
 
 static const char *modalias_usb(sd_device *dev, char *s, size_t size) {
-        const char *v, *p;
+        const char *v, *p, *n = NULL;
         uint16_t vn, pn;
 
         if (sd_device_get_sysattr_value(dev, "idVendor", &v) < 0)
@@ -58,15 +58,16 @@ static const char *modalias_usb(sd_device *dev, char *s, size_t size) {
                 return NULL;
         if (safe_atoux16(p, &pn) < 0)
                 return NULL;
-        snprintf(s, size, "usb:v%04Xp%04X*", vn, pn);
+        (void) sd_device_get_sysattr_value(dev, "product", &n);
+
+        snprintf(s, size, "usb:v%04Xp%04X:%s", vn, pn, strempty(n));
         return s;
 }
 
 static int udev_builtin_hwdb_search(sd_device *dev, sd_device *srcdev,
                                     const char *subsystem, const char *prefix,
                                     const char *filter, bool test) {
-        sd_device *d;
-        char s[16];
+        char s[LINE_MAX];
         bool last = false;
         int r = 0;
 
@@ -75,7 +76,7 @@ static int udev_builtin_hwdb_search(sd_device *dev, sd_device *srcdev,
         if (!srcdev)
                 srcdev = dev;
 
-        for (d = srcdev; d; ) {
+        for (sd_device *d = srcdev; d; ) {
                 const char *dsubsys, *devtype, *modalias = NULL;
 
                 if (sd_device_get_subsystem(d, &dsubsys) < 0)
@@ -101,6 +102,8 @@ static int udev_builtin_hwdb_search(sd_device *dev, sd_device *srcdev,
                 if (!modalias)
                         goto next;
 
+                log_device_debug(dev, "hwdb modalias key: \"%s\"", modalias);
+
                 r = udev_builtin_hwdb_lookup(dev, prefix, modalias, filter, test);
                 if (r > 0)
                         break;
index 9ff2b02289892d408f514265c1d66a0f35ec19c4..215287c114cb0050f884246324edba6bd85389ce 100644 (file)
@@ -228,9 +228,8 @@ static bool test_pointers(sd_device *dev,
                         is_touchscreen = true;
                 else if (has_joystick_axes_or_buttons)
                         is_joystick = true;
-        } else if (has_joystick_axes_or_buttons) {
+        } else if (has_joystick_axes_or_buttons)
                 is_joystick = true;
-        }
 
         if (has_mt_coordinates) {
                 if (stylus_or_pen)
index 169d6ce8f7cb2e164baa6f988882cf8e74d6d098..b3c0ec827b655334572e1ac43780ba0d699e9fe1 100644 (file)
@@ -449,11 +449,10 @@ static int names_platform(sd_device *dev, struct netnames *names, bool test) {
          * The Vendor (3 or 4 char), followed by hexdecimal model number : instance id.
          */
 
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+        DISABLE_WARNING_FORMAT_NONLITERAL;
         if (sscanf(syspath, pattern, vendor, &model, &instance, &ethid) != 4)
                 return -EINVAL;
-#pragma GCC diagnostic pop
+        REENABLE_WARNING;
 
         if (!in_charset(vendor, validchars))
                 return -ENOENT;
index ca38f56087911f283165d6207ca69e67ec616c1c..6c020ac0ed0037db2ebdb928f0994f7833a6947b 100644 (file)
@@ -253,14 +253,20 @@ static sd_device *handle_scsi_iscsi(sd_device *parent, char **path) {
         return parent;
 }
 
-static sd_device *handle_scsi_ata(sd_device *parent, char **path) {
+static sd_device *handle_scsi_ata(sd_device *parent, char **path, char **compat_path) {
         sd_device *targetdev, *target_parent;
         _cleanup_(sd_device_unrefp) sd_device *atadev = NULL;
-        const char *port_no, *sysname;
+        const char *port_no, *sysname, *name;
+        unsigned host, bus, target, lun;
 
         assert(parent);
         assert(path);
 
+        if (sd_device_get_sysname(parent, &name) < 0)
+                return NULL;
+        if (sscanf(name, "%u:%u:%u:%u", &host, &bus, &target, &lun) != 4)
+                return NULL;
+
         if (sd_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_host", &targetdev) < 0)
                 return NULL;
 
@@ -275,7 +281,17 @@ static sd_device *handle_scsi_ata(sd_device *parent, char **path) {
         if (sd_device_get_sysattr_value(atadev, "port_no", &port_no) < 0)
                 return NULL;
 
-        path_prepend(path, "ata-%s", port_no);
+        if (bus != 0)
+                /* Devices behind port multiplier have a bus != 0*/
+                path_prepend(path, "ata-%s.%u.0", port_no, bus);
+        else
+                /* Master/slave are distinguished by target id */
+                path_prepend(path, "ata-%s.%u", port_no, target);
+
+        /* old compatible persistent link for ATA devices */
+        if (compat_path)
+                path_prepend(compat_path, "ata-%s", port_no);
+
         return parent;
 }
 
@@ -392,7 +408,7 @@ static sd_device *handle_scsi_hyperv(sd_device *parent, char **path, size_t guid
         return parent;
 }
 
-static sd_device *handle_scsi(sd_device *parent, char **path, bool *supported_parent) {
+static sd_device *handle_scsi(sd_device *parent, char **path, char **compat_path, bool *supported_parent) {
         const char *devtype, *id, *name;
 
         if (sd_device_get_devtype(parent, &devtype) < 0 ||
@@ -426,7 +442,7 @@ static sd_device *handle_scsi(sd_device *parent, char **path, bool *supported_pa
         }
 
         if (strstr(name, "/ata"))
-                return handle_scsi_ata(parent, path);
+                return handle_scsi_ata(parent, path, compat_path);
 
         if (strstr(name, "/vmbus_"))
                 return handle_scsi_hyperv(parent, path, 37);
@@ -520,6 +536,7 @@ static sd_device *handle_ap(sd_device *parent, char **path) {
 static int builtin_path_id(sd_device *dev, int argc, char *argv[], bool test) {
         sd_device *parent;
         _cleanup_free_ char *path = NULL;
+        _cleanup_free_ char *compat_path = NULL;
         bool supported_transport = false;
         bool supported_parent = false;
         const char *subsystem;
@@ -537,7 +554,7 @@ static int builtin_path_id(sd_device *dev, int argc, char *argv[], bool test) {
                 } else if (streq(subsys, "scsi_tape")) {
                         handle_scsi_tape(parent, &path);
                 } else if (streq(subsys, "scsi")) {
-                        parent = handle_scsi(parent, &path, &supported_parent);
+                        parent = handle_scsi(parent, &path, &compat_path, &supported_parent);
                         supported_transport = true;
                 } else if (streq(subsys, "cciss")) {
                         parent = handle_cciss(parent, &path);
@@ -557,19 +574,27 @@ static int builtin_path_id(sd_device *dev, int argc, char *argv[], bool test) {
                         }
                 } else if (streq(subsys, "pci")) {
                         path_prepend(&path, "pci-%s", sysname);
+                        if (compat_path)
+                                path_prepend(&compat_path, "pci-%s", sysname);
                         parent = skip_subsystem(parent, "pci");
                         supported_parent = true;
                 } else if (streq(subsys, "platform")) {
                         path_prepend(&path, "platform-%s", sysname);
+                        if (compat_path)
+                                path_prepend(&compat_path, "platform-%s", sysname);
                         parent = skip_subsystem(parent, "platform");
                         supported_transport = true;
                         supported_parent = true;
                 } else if (streq(subsys, "acpi")) {
                         path_prepend(&path, "acpi-%s", sysname);
+                        if (compat_path)
+                                path_prepend(&compat_path, "acpi-%s", sysname);
                         parent = skip_subsystem(parent, "acpi");
                         supported_parent = true;
                 } else if (streq(subsys, "xen")) {
                         path_prepend(&path, "xen-%s", sysname);
+                        if (compat_path)
+                                path_prepend(&compat_path, "xen-%s", sysname);
                         parent = skip_subsystem(parent, "xen");
                         supported_parent = true;
                 } else if (streq(subsys, "virtio")) {
@@ -577,16 +602,22 @@ static int builtin_path_id(sd_device *dev, int argc, char *argv[], bool test) {
                         supported_transport = true;
                 } else if (streq(subsys, "scm")) {
                         path_prepend(&path, "scm-%s", sysname);
+                        if (compat_path)
+                                path_prepend(&compat_path, "scm-%s", sysname);
                         parent = skip_subsystem(parent, "scm");
                         supported_transport = true;
                         supported_parent = true;
                 } else if (streq(subsys, "ccw")) {
                         path_prepend(&path, "ccw-%s", sysname);
+                        if (compat_path)
+                                path_prepend(&compat_path, "ccw-%s", sysname);
                         parent = skip_subsystem(parent, "ccw");
                         supported_transport = true;
                         supported_parent = true;
                 } else if (streq(subsys, "ccwgroup")) {
                         path_prepend(&path, "ccwgroup-%s", sysname);
+                        if (compat_path)
+                                path_prepend(&compat_path, "ccwgroup-%s", sysname);
                         parent = skip_subsystem(parent, "ccwgroup");
                         supported_transport = true;
                         supported_parent = true;
@@ -596,6 +627,8 @@ static int builtin_path_id(sd_device *dev, int argc, char *argv[], bool test) {
                         supported_parent = true;
                 } else if (streq(subsys, "iucv")) {
                         path_prepend(&path, "iucv-%s", sysname);
+                        if (compat_path)
+                                path_prepend(&compat_path, "iucv-%s", sysname);
                         parent = skip_subsystem(parent, "iucv");
                         supported_transport = true;
                         supported_parent = true;
@@ -604,10 +637,19 @@ static int builtin_path_id(sd_device *dev, int argc, char *argv[], bool test) {
 
                         if (sd_device_get_sysattr_value(dev, "nsid", &nsid) >= 0) {
                                 path_prepend(&path, "nvme-%s", nsid);
+                                if (compat_path)
+                                        path_prepend(&compat_path, "nvme-%s", nsid);
                                 parent = skip_subsystem(parent, "nvme");
                                 supported_parent = true;
                                 supported_transport = true;
                         }
+                } else if (streq(subsys, "spi")) {
+                        const char *sysnum;
+
+                        if (sd_device_get_sysnum(parent, &sysnum) >= 0 && sysnum) {
+                                path_prepend(&path, "cs-%s", sysnum);
+                                parent = skip_subsystem(parent, "spi");
+                        }
                 }
 
                 if (!parent)
@@ -671,6 +713,14 @@ static int builtin_path_id(sd_device *dev, int argc, char *argv[], bool test) {
                 udev_builtin_add_property(dev, test, "ID_PATH_TAG", tag);
         }
 
+        /*
+         * Compatible link generation for ATA devices
+         * we assign compat_link to the env variable
+         * ID_PATH_ATA_COMPAT
+         */
+        if (compat_path)
+                udev_builtin_add_property(dev, test, "ID_PATH_ATA_COMPAT", compat_path);
+
         return 0;
 }
 
index d067279f3e1aea02c555a5a41a05af9607d18fcf..1e51f22e26035c41b0873e9a314c1488d430f004 100644 (file)
@@ -189,12 +189,12 @@ static int udev_ctrl_connection_event_handler(sd_event_source *s, int fd, uint32
         _cleanup_(udev_ctrl_disconnect_and_listen_againp) struct udev_ctrl *uctrl = NULL;
         struct udev_ctrl_msg_wire msg_wire;
         struct iovec iov = IOVEC_MAKE(&msg_wire, sizeof(struct udev_ctrl_msg_wire));
-        char cred_msg[CMSG_SPACE(sizeof(struct ucred))];
+        CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control;
         struct msghdr smsg = {
                 .msg_iov = &iov,
                 .msg_iovlen = 1,
-                .msg_control = cred_msg,
-                .msg_controllen = sizeof(cred_msg),
+                .msg_control = &control,
+                .msg_controllen = sizeof(control),
         };
         struct cmsghdr *cmsg;
         struct ucred *cred;
index b10d488281db12215454155cf93684adde2b584e..e1c2baf7f21247bbc44ef47dcdaa09731fdb5c57 100644 (file)
@@ -41,6 +41,7 @@ typedef struct Spawn {
         pid_t pid;
         usec_t timeout_warn_usec;
         usec_t timeout_usec;
+        int timeout_signal;
         usec_t event_birth_usec;
         bool accept_failure;
         int fd_stdout;
@@ -599,7 +600,7 @@ static int on_spawn_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
 
         assert(spawn);
 
-        kill_and_sigcont(spawn->pid, SIGKILL);
+        kill_and_sigcont(spawn->pid, spawn->timeout_signal);
 
         log_device_error(spawn->device, "Spawned process '%s' ["PID_FMT"] timed out after %s, killing",
                          spawn->cmd, spawn->pid,
@@ -720,6 +721,7 @@ static int spawn_wait(Spawn *spawn) {
 
 int udev_event_spawn(UdevEvent *event,
                      usec_t timeout_usec,
+                     int timeout_signal,
                      bool accept_failure,
                      const char *cmd,
                      char *result, size_t ressize) {
@@ -796,6 +798,7 @@ int udev_event_spawn(UdevEvent *event,
                 .accept_failure = accept_failure,
                 .timeout_warn_usec = udev_warn_timeout(timeout_usec),
                 .timeout_usec = timeout_usec,
+                .timeout_signal = timeout_signal,
                 .event_birth_usec = event->birth_usec,
                 .fd_stdout = outpipe[READ_END],
                 .fd_stderr = errpipe[READ_END],
@@ -837,11 +840,6 @@ static int rename_netif(UdevEvent *event) {
         if (r < 0)
                 return log_device_error_errno(dev, r, "Failed to get ifindex: %m");
 
-        r = rtnl_set_link_name(&event->rtnl, ifindex, event->name);
-        if (r < 0)
-                return log_device_error_errno(dev, r, "Failed to rename network interface %i from '%s' to '%s': %m",
-                                              ifindex, oldname, event->name);
-
         /* Set ID_RENAMING boolean property here, and drop it in the corresponding move uevent later. */
         r = device_add_property(dev, "ID_RENAMING", "1");
         if (r < 0)
@@ -851,6 +849,22 @@ static int rename_netif(UdevEvent *event) {
         if (r < 0)
                 return log_device_warning_errno(dev, r, "Failed to update properties with new name '%s': %m", event->name);
 
+        /* Also set ID_RENAMING boolean property to cloned sd_device object and save it to database
+         * before calling rtnl_set_link_name(). Otherwise, clients (e.g., systemd-networkd) may receive
+         * RTM_NEWLINK netlink message before the database is updated. */
+        r = device_add_property(event->dev_db_clone, "ID_RENAMING", "1");
+        if (r < 0)
+                return log_device_warning_errno(event->dev_db_clone, r, "Failed to add 'ID_RENAMING' property: %m");
+
+        r = device_update_db(event->dev_db_clone);
+        if (r < 0)
+                return log_device_debug_errno(event->dev_db_clone, r, "Failed to update database under /run/udev/data/: %m");
+
+        r = rtnl_set_link_name(&event->rtnl, ifindex, event->name);
+        if (r < 0)
+                return log_device_error_errno(dev, r, "Failed to rename network interface %i from '%s' to '%s': %m",
+                                              ifindex, oldname, event->name);
+
         log_device_debug(dev, "Network interface %i is renamed from '%s' to '%s'", ifindex, oldname, event->name);
 
         return 1;
@@ -867,8 +881,7 @@ static int update_devnode(UdevEvent *event) {
                 return log_device_error_errno(dev, r, "Failed to get devnum: %m");
 
         /* remove/update possible left-over symlinks from old database entry */
-        if (event->dev_db_clone)
-                (void) udev_node_update_old_links(dev, event->dev_db_clone);
+        (void) udev_node_update_old_links(dev, event->dev_db_clone);
 
         if (!uid_is_valid(event->uid)) {
                 r = device_get_devnode_uid(dev, &event->uid);
@@ -899,6 +912,7 @@ static int update_devnode(UdevEvent *event) {
 static void event_execute_rules_on_remove(
                 UdevEvent *event,
                 usec_t timeout_usec,
+                int timeout_signal,
                 Hashmap *properties_list,
                 UdevRules *rules) {
 
@@ -920,7 +934,7 @@ static void event_execute_rules_on_remove(
         if (sd_device_get_devnum(dev, NULL) >= 0)
                 (void) udev_watch_end(dev);
 
-        (void) udev_rules_apply_to_event(rules, event, timeout_usec, properties_list);
+        (void) udev_rules_apply_to_event(rules, event, timeout_usec, timeout_signal, properties_list);
 
         if (sd_device_get_devnum(dev, NULL) >= 0)
                 (void) udev_node_remove(dev);
@@ -930,8 +944,7 @@ static int udev_event_on_move(UdevEvent *event) {
         sd_device *dev = event->dev;
         int r;
 
-        if (event->dev_db_clone &&
-            sd_device_get_devnum(dev, NULL) < 0) {
+        if (sd_device_get_devnum(dev, NULL) < 0) {
                 r = device_copy_properties(dev, event->dev_db_clone);
                 if (r < 0)
                         log_device_debug_errno(dev, r, "Failed to copy properties from cloned sd_device object, ignoring: %m");
@@ -947,6 +960,7 @@ static int udev_event_on_move(UdevEvent *event) {
 
 int udev_event_execute_rules(UdevEvent *event,
                              usec_t timeout_usec,
+                             int timeout_signal,
                              Hashmap *properties_list,
                              UdevRules *rules) {
         const char *subsystem;
@@ -968,7 +982,7 @@ int udev_event_execute_rules(UdevEvent *event,
                 return log_device_error_errno(dev, r, "Failed to get ACTION: %m");
 
         if (action == DEVICE_ACTION_REMOVE) {
-                event_execute_rules_on_remove(event, timeout_usec, properties_list, rules);
+                event_execute_rules_on_remove(event, timeout_usec, timeout_signal, properties_list, rules);
                 return 0;
         }
 
@@ -976,7 +990,7 @@ int udev_event_execute_rules(UdevEvent *event,
         if (r < 0)
                 return log_device_debug_errno(dev, r, "Failed to clone sd_device object: %m");
 
-        if (event->dev_db_clone && sd_device_get_devnum(dev, NULL) >= 0)
+        if (sd_device_get_devnum(dev, NULL) >= 0)
                 /* Disable watch during event processing. */
                 (void) udev_watch_end(event->dev_db_clone);
 
@@ -986,7 +1000,7 @@ int udev_event_execute_rules(UdevEvent *event,
                         return r;
         }
 
-        r = udev_rules_apply_to_event(rules, event, timeout_usec, properties_list);
+        r = udev_rules_apply_to_event(rules, event, timeout_usec, timeout_signal, properties_list);
         if (r < 0)
                 return log_device_debug_errno(dev, r, "Failed to apply udev rules: %m");
 
@@ -1014,12 +1028,10 @@ int udev_event_execute_rules(UdevEvent *event,
 
         device_set_is_initialized(dev);
 
-        event->dev_db_clone = sd_device_unref(event->dev_db_clone);
-
         return 0;
 }
 
-void udev_event_execute_run(UdevEvent *event, usec_t timeout_usec) {
+void udev_event_execute_run(UdevEvent *event, usec_t timeout_usec, int timeout_signal) {
         const char *command;
         void *val;
         Iterator i;
@@ -1043,7 +1055,8 @@ void udev_event_execute_run(UdevEvent *event, usec_t timeout_usec) {
                         }
 
                         log_device_debug(event->dev, "Running command \"%s\"", command);
-                        r = udev_event_spawn(event, timeout_usec, false, command, NULL, 0);
+
+                        r = udev_event_spawn(event, timeout_usec, timeout_signal, false, command, NULL, 0);
                         if (r < 0)
                                 log_device_warning_errno(event->dev, r, "Failed to execute '%s', ignoring: %m", command);
                         else if (r > 0) /* returned value is positive when program fails */
index 5cb35514c8914b6bf72900bfa44eb5d1001f8cd8..a0193ffef350c54972ef8b2f11fed615452fd3ca 100644 (file)
@@ -54,13 +54,15 @@ size_t udev_event_apply_format(UdevEvent *event,
 int udev_check_format(const char *value, size_t *offset, const char **hint);
 int udev_event_spawn(UdevEvent *event,
                      usec_t timeout_usec,
+                     int timeout_signal,
                      bool accept_failure,
                      const char *cmd, char *result, size_t ressize);
 int udev_event_execute_rules(UdevEvent *event,
                              usec_t timeout_usec,
+                             int timeout_signal,
                              Hashmap *properties_list,
                              UdevRules *rules);
-void udev_event_execute_run(UdevEvent *event, usec_t timeout_usec);
+void udev_event_execute_run(UdevEvent *event, usec_t timeout_usec, int timeout_signal);
 
 static inline usec_t udev_warn_timeout(usec_t timeout_usec) {
         return DIV_ROUND_UP(timeout_usec, 3);
index a34b8d694521ba1ef4a500d444a5a1397089806d..31a34030935829a4f6b97382fdfd9dc556dd84c4 100644 (file)
@@ -47,10 +47,10 @@ static int node_symlink(sd_device *dev, const char *node, const char *slink) {
 
         /* preserve link with correct target, do not replace node of other device */
         if (lstat(slink, &stats) == 0) {
-                if (S_ISBLK(stats.st_mode) || S_ISCHR(stats.st_mode)) {
-                        log_device_error(dev, "Conflicting device node '%s' found, link to '%s' will not be created.", slink, node);
-                        return -EOPNOTSUPP;
-                else if (S_ISLNK(stats.st_mode)) {
+                if (S_ISBLK(stats.st_mode) || S_ISCHR(stats.st_mode))
+                        return log_device_error_errno(dev, SYNTHETIC_ERRNO(EOPNOTSUPP),
+                                                      "Conflicting device node '%s' found, link to '%s' will not be created.", slink, node);
+                else if (S_ISLNK(stats.st_mode)) {
                         _cleanup_free_ char *buf = NULL;
 
                         if (readlink_malloc(slink, &buf) >= 0 &&
index 5eb358383f2b6ce558ec38639fd61ff66bd16cf5..c36f032f6622ba25625a86b4d18855384621cca1 100644 (file)
@@ -78,7 +78,7 @@ typedef enum {
         TK_M_ATTR,                          /* string, takes filename through attribute, sd_device_get_sysattr_value(), util_resolve_subsys_kernel(), etc. */
         TK_M_SYSCTL,                        /* string, takes kernel parameter through attribute */
 
-        /* matches parent paramters */
+        /* matches parent parameters */
         TK_M_PARENTS_KERNEL,                /* string */
         TK_M_PARENTS_SUBSYSTEM,             /* string */
         TK_M_PARENTS_DRIVER,                /* string */
@@ -595,7 +595,7 @@ static int parse_token(UdevRules *rules, const char *key, char *attr, UdevRuleOp
                 if (!is_match) {
                         if (streq(value, "%k"))
                                 return log_token_error_errno(rules, SYNTHETIC_ERRNO(EINVAL),
-                                                             "Ignoring NAME=\"%%k\" is ignored, as it breaks kernel supplied names.");
+                                                             "NAME=\"%%k\" is ignored, as it breaks kernel supplied names.");
                         if (isempty(value))
                                 return log_token_error_errno(rules, SYNTHETIC_ERRNO(EINVAL),
                                                              "Ignoring NAME=\"\", as udev will not delete any device nodes.");
@@ -860,7 +860,7 @@ static int parse_token(UdevRules *rules, const char *key, char *attr, UdevRuleOp
                         check_value_format_and_warn(rules, key, value, true);
                         r = rule_line_add_token(rule_line, TK_A_OWNER, op, value, NULL);
                 } else {
-                        log_token_debug(rules, "Resolving user name is disabled, ignoring %s=%s", key, value);
+                        log_token_debug(rules, "User name resolution is disabled, ignoring %s=%s", key, value);
                         return 0;
                 }
         } else if (streq(key, "GROUP")) {
@@ -943,7 +943,7 @@ static int parse_token(UdevRules *rules, const char *key, char *attr, UdevRuleOp
                 if (op != OP_ASSIGN)
                         return log_token_invalid_op(rules, key);
                 if (FLAGS_SET(rule_line->type, LINE_HAS_GOTO)) {
-                        log_token_warning(rules, "Contains multiple GOTO key, ignoring GOTO=\"%s\".", value);
+                        log_token_warning(rules, "Contains multiple GOTO keys, ignoring GOTO=\"%s\".", value);
                         return 0;
                 }
 
@@ -1174,7 +1174,7 @@ static void rule_resolve_goto(UdevRuleFile *rule_file) {
         }
 }
 
-static int parse_file(UdevRules *rules, const char *filename) {
+int udev_rules_parse_file(UdevRules *rules, const char *filename) {
         _cleanup_free_ char *continuation = NULL, *name = NULL;
         _cleanup_fclose_ FILE *f = NULL;
         UdevRuleFile *rule_file;
@@ -1277,30 +1277,41 @@ static int parse_file(UdevRules *rules, const char *filename) {
         return 0;
 }
 
-int udev_rules_new(UdevRules **ret_rules, ResolveNameTiming resolve_name_timing) {
-        _cleanup_(udev_rules_freep) UdevRules *rules = NULL;
-        _cleanup_strv_free_ char **files = NULL;
-        char **f;
-        int r;
-
+UdevRules* udev_rules_new(ResolveNameTiming resolve_name_timing) {
         assert(resolve_name_timing >= 0 && resolve_name_timing < _RESOLVE_NAME_TIMING_MAX);
 
-        rules = new(UdevRules, 1);
+        UdevRules *rules = new(UdevRules, 1);
         if (!rules)
-                return -ENOMEM;
+                return NULL;
 
         *rules = (UdevRules) {
                 .resolve_name_timing = resolve_name_timing,
         };
 
+        return rules;
+}
+
+int udev_rules_load(UdevRules **ret_rules, ResolveNameTiming resolve_name_timing) {
+        _cleanup_(udev_rules_freep) UdevRules *rules = NULL;
+        _cleanup_strv_free_ char **files = NULL;
+        char **f;
+        int r;
+
+        rules = udev_rules_new(resolve_name_timing);
+        if (!rules)
+                return -ENOMEM;
+
         (void) udev_rules_check_timestamp(rules);
 
         r = conf_files_list_strv(&files, ".rules", NULL, 0, RULES_DIRS);
         if (r < 0)
-                return log_error_errno(r, "Failed to enumerate rules files: %m");
+                return log_debug_errno(r, "Failed to enumerate rules files: %m");
 
-        STRV_FOREACH(f, files)
-                (void) parse_file(rules, *f);
+        STRV_FOREACH(f, files) {
+                r = udev_rules_parse_file(rules, *f);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to read rules file %s, ignoring: %m", *f);
+        }
 
         *ret_rules = TAKE_PTR(rules);
         return 0;
@@ -1329,11 +1340,7 @@ static bool token_match_string(UdevRuleToken *token, const char *str) {
                 match = isempty(str);
                 break;
         case MATCH_TYPE_SUBSYSTEM:
-                NULSTR_FOREACH(i, "subsystem\0class\0bus\0")
-                        if (streq(i, str)) {
-                                match = true;
-                                break;
-                        }
+                match = STR_IN_SET(str, "subsystem", "class", "bus");
                 break;
         case MATCH_TYPE_PLAIN_WITH_EMPTY:
                 if (isempty(str)) {
@@ -1523,6 +1530,7 @@ static int udev_rule_apply_token_to_event(
                 sd_device *dev,
                 UdevEvent *event,
                 usec_t timeout_usec,
+                int timeout_signal,
                 Hashmap *properties_list) {
 
         UdevRuleToken *token;
@@ -1647,7 +1655,7 @@ static int udev_rule_apply_token_to_event(
                 if (r == -ENOENT)
                         return token->op == OP_NOMATCH;
                 if (r < 0)
-                        return log_rule_error_errno(dev, rules, r, "Failed to test the existence of '%s': %m", buf);
+                        return log_rule_error_errno(dev, rules, r, "Failed to test for the existence of '%s': %m", buf);
 
                 if (stat(buf, &statbuf) < 0)
                         return token->op == OP_NOMATCH;
@@ -1665,19 +1673,19 @@ static int udev_rule_apply_token_to_event(
                 (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false);
                 log_rule_debug(dev, rules, "Running PROGRAM '%s'", buf);
 
-                r = udev_event_spawn(event, timeout_usec, true, buf, result, sizeof(result));
+                r = udev_event_spawn(event, timeout_usec, timeout_signal, true, buf, result, sizeof(result));
                 if (r != 0) {
                         if (r < 0)
-                                log_rule_warning_errno(dev, rules, r, "Failed to execute '%s', ignoring: %m", buf);
+                                log_rule_warning_errno(dev, rules, r, "Failed to execute \"%s\": %m", buf);
                         else /* returned value is positive when program fails */
-                                log_rule_debug(dev, rules, "Command \"%s\" returned %d (error), ignoring", buf, r);
+                                log_rule_debug(dev, rules, "Command \"%s\" returned %d (error)", buf, r);
                         return token->op == OP_NOMATCH;
                 }
 
                 delete_trailing_chars(result, "\n");
                 count = util_replace_chars(result, UDEV_ALLOWED_CHARS_INPUT);
                 if (count > 0)
-                        log_rule_debug(dev, rules, "Replaced %zu character(s) from result of '%s'",
+                        log_rule_debug(dev, rules, "Replaced %zu character(s) in result of \"%s\"",
                                        count, buf);
 
                 event->program_result = strdup(result);
@@ -1735,7 +1743,7 @@ static int udev_rule_apply_token_to_event(
                 (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false);
                 log_rule_debug(dev, rules, "Importing properties from results of '%s'", buf);
 
-                r = udev_event_spawn(event, timeout_usec, true, buf, result, sizeof result);
+                r = udev_event_spawn(event, timeout_usec, timeout_signal, true, buf, result, sizeof result);
                 if (r != 0) {
                         if (r < 0)
                                 log_rule_warning_errno(dev, rules, r, "Failed to execute '%s', ignoring: %m", buf);
@@ -1817,7 +1825,7 @@ static int udev_rule_apply_token_to_event(
         case TK_M_IMPORT_CMDLINE: {
                 _cleanup_free_ char *value = NULL;
 
-                r = proc_cmdline_get_key(token->value, PROC_CMDLINE_VALUE_OPTIONAL, &value);
+                r = proc_cmdline_get_key(token->value, PROC_CMDLINE_VALUE_OPTIONAL|PROC_CMDLINE_IGNORE_EFI_OPTIONS, &value);
                 if (r < 0)
                         return log_rule_error_errno(dev, rules, r,
                                                     "Failed to read '%s' option from /proc/cmdline: %m",
@@ -2165,7 +2173,8 @@ static bool token_is_for_parents(UdevRuleToken *token) {
 
 static int udev_rule_apply_parent_token_to_event(
                 UdevRules *rules,
-                UdevEvent *event) {
+                UdevEvent *event,
+                int timeout_signal) {
 
         UdevRuleLine *line;
         UdevRuleToken *head;
@@ -2178,7 +2187,7 @@ static int udev_rule_apply_parent_token_to_event(
                 LIST_FOREACH(tokens, line->current_token, head) {
                         if (!token_is_for_parents(line->current_token))
                                 return true; /* All parent tokens match. */
-                        r = udev_rule_apply_token_to_event(rules, event->dev_parent, event, 0, NULL);
+                        r = udev_rule_apply_token_to_event(rules, event->dev_parent, event, 0, timeout_signal, NULL);
                         if (r < 0)
                                 return r;
                         if (r == 0)
@@ -2199,6 +2208,7 @@ static int udev_rule_apply_line_to_event(
                 UdevRules *rules,
                 UdevEvent *event,
                 usec_t timeout_usec,
+                int timeout_signal,
                 Hashmap *properties_list,
                 UdevRuleLine **next_line) {
 
@@ -2232,7 +2242,7 @@ static int udev_rule_apply_line_to_event(
                         if (parents_done)
                                 continue;
 
-                        r = udev_rule_apply_parent_token_to_event(rules, event);
+                        r = udev_rule_apply_parent_token_to_event(rules, event, timeout_signal);
                         if (r <= 0)
                                 return r;
 
@@ -2240,7 +2250,7 @@ static int udev_rule_apply_line_to_event(
                         continue;
                 }
 
-                r = udev_rule_apply_token_to_event(rules, event->dev, event, timeout_usec, properties_list);
+                r = udev_rule_apply_token_to_event(rules, event->dev, event, timeout_usec, timeout_signal, properties_list);
                 if (r <= 0)
                         return r;
         }
@@ -2255,6 +2265,7 @@ int udev_rules_apply_to_event(
                 UdevRules *rules,
                 UdevEvent *event,
                 usec_t timeout_usec,
+                int timeout_signal,
                 Hashmap *properties_list) {
 
         UdevRuleFile *file;
@@ -2267,7 +2278,7 @@ int udev_rules_apply_to_event(
         LIST_FOREACH(rule_files, file, rules->rule_files) {
                 rules->current_file = file;
                 LIST_FOREACH_SAFE(rule_lines, file->current_line, next_line, file->rule_lines) {
-                        r = udev_rule_apply_line_to_event(rules, event, timeout_usec, properties_list, &next_line);
+                        r = udev_rule_apply_line_to_event(rules, event, timeout_usec, timeout_signal, properties_list, &next_line);
                         if (r < 0)
                                 return r;
                 }
index 9fff5da7b5d48e30969d9f5a122440a9c5b9a735..cdb98e8ceccf4bc3be58f9791b0dafde73e73401 100644 (file)
@@ -16,12 +16,15 @@ typedef enum {
         _ESCAPE_TYPE_INVALID = -1
 } UdevRuleEscapeType;
 
-int udev_rules_new(UdevRules **ret_rules, ResolveNameTiming resolve_name_timing);
+int udev_rules_parse_file(UdevRules *rules, const char *filename);
+UdevRules* udev_rules_new(ResolveNameTiming resolve_name_timing);
+int udev_rules_load(UdevRules **ret_rules, ResolveNameTiming resolve_name_timing);
 UdevRules *udev_rules_free(UdevRules *rules);
 DEFINE_TRIVIAL_CLEANUP_FUNC(UdevRules*, udev_rules_free);
 
 bool udev_rules_check_timestamp(UdevRules *rules);
 int udev_rules_apply_to_event(UdevRules *rules, UdevEvent *event,
                               usec_t timeout_usec,
+                              int timeout_signal,
                               Hashmap *properties_list);
 int udev_rules_apply_static_dev_perms(UdevRules *rules);
index 7deb7715bed4329c6b02018568de8869713fe666..07d7f0cd1a5da95f4610b7130a011c7ce302eb8e 100644 (file)
@@ -7,4 +7,5 @@
 #children_max=
 #exec_delay=
 #event_timeout=180
+#timeout_signal=SIGKILL
 #resolve_names=early
index 5acbb2d01a95791740837eb20e0893fb6699a913..7b4f4006b56d405a42d39d786eaf37cef645310d 100644 (file)
@@ -2,4 +2,5 @@ Name: udev
 Description: udev
 Version: @PROJECT_VERSION@
 
-udevdir=@udevlibexecdir@
+udev_dir=@udevlibexecdir@
+udevdir=${udev_dir}
index 1debdf2b314cd4ecd213002191defc7db1368088..ae6d8caf54e02c39c4b5f4245b5523cc9713eb97 100644 (file)
@@ -17,6 +17,7 @@
 #include "device-util.h"
 #include "dirent-util.h"
 #include "fd-util.h"
+#include "sort-util.h"
 #include "string-table.h"
 #include "string-util.h"
 #include "udev-util.h"
@@ -43,22 +44,47 @@ static const char *arg_export_prefix = NULL;
 static usec_t arg_wait_for_initialization_timeout = 0;
 
 static bool skip_attribute(const char *name) {
-        static const char* const skip[] = {
-                "uevent",
-                "dev",
-                "modalias",
-                "resource",
-                "driver",
-                "subsystem",
-                "module",
-        };
+        /* Those are either displayed separately or should not be shown at all. */
+        return STR_IN_SET(name,
+                          "uevent",
+                          "dev",
+                          "modalias",
+                          "resource",
+                          "driver",
+                          "subsystem",
+                          "module");
+}
+
+typedef struct SysAttr {
+        const char *name;
+        const char *value;
+} SysAttr;
 
-        return string_table_lookup(skip, ELEMENTSOF(skip), name) >= 0;
+static int sysattr_compare(const SysAttr *a, const SysAttr *b) {
+        return strcmp(a->name, b->name);
 }
 
-static void print_all_attributes(sd_device *device, const char *key) {
+static int print_all_attributes(sd_device *device, bool is_parent) {
+        _cleanup_free_ SysAttr *sysattrs = NULL;
+        size_t n_items = 0, n_allocated = 0;
         const char *name, *value;
 
+        value = NULL;
+        (void) sd_device_get_devpath(device, &value);
+        printf("  looking at %sdevice '%s':\n", is_parent ? "parent " : "", strempty(value));
+
+        value = NULL;
+        (void) sd_device_get_sysname(device, &value);
+        printf("    %s==\"%s\"\n", is_parent ? "KERNELS" : "KERNEL", strempty(value));
+
+        value = NULL;
+        (void) sd_device_get_subsystem(device, &value);
+        printf("    %s==\"%s\"\n", is_parent ? "SUBSYSTEMS" : "SUBSYSTEM", strempty(value));
+
+        value = NULL;
+        (void) sd_device_get_driver(device, &value);
+        printf("    %s==\"%s\"\n", is_parent ? "DRIVERS" : "DRIVER", strempty(value));
+
         FOREACH_DEVICE_SYSATTR(device, name) {
                 size_t len;
 
@@ -74,19 +100,34 @@ static void print_all_attributes(sd_device *device, const char *key) {
 
                 /* skip nonprintable attributes */
                 len = strlen(value);
-                while (len > 0 && isprint(value[len-1]))
+                while (len > 0 && isprint((unsigned char) value[len-1]))
                         len--;
                 if (len > 0)
                         continue;
 
-                printf("    %s{%s}==\"%s\"\n", key, name, value);
+                if (!GREEDY_REALLOC(sysattrs, n_allocated, n_items + 1))
+                        return log_oom();
+
+                sysattrs[n_items] = (SysAttr) {
+                        .name = name,
+                        .value = value,
+                };
+                n_items++;
         }
+
+        typesafe_qsort(sysattrs, n_items, sysattr_compare);
+
+        for (size_t i = 0; i < n_items; i++)
+                printf("    %s{%s}==\"%s\"\n", is_parent ? "ATTRS" : "ATTR", sysattrs[i].name, sysattrs[i].value);
+
         puts("");
+
+        return 0;
 }
 
 static int print_device_chain(sd_device *device) {
         sd_device *child, *parent;
-        const char *str;
+        int r;
 
         printf("\n"
                "Udevadm info starts with the device specified by the devpath and then\n"
@@ -96,30 +137,14 @@ static int print_device_chain(sd_device *device) {
                "and the attributes from one single parent device.\n"
                "\n");
 
-        (void) sd_device_get_devpath(device, &str);
-        printf("  looking at device '%s':\n", str);
-        (void) sd_device_get_sysname(device, &str);
-        printf("    KERNEL==\"%s\"\n", str);
-        if (sd_device_get_subsystem(device, &str) < 0)
-                str = "";
-        printf("    SUBSYSTEM==\"%s\"\n", str);
-        if (sd_device_get_driver(device, &str) < 0)
-                str = "";
-        printf("    DRIVER==\"%s\"\n", str);
-        print_all_attributes(device, "ATTR");
+        r = print_all_attributes(device, false);
+        if (r < 0)
+                return r;
 
         for (child = device; sd_device_get_parent(child, &parent) >= 0; child = parent) {
-                (void) sd_device_get_devpath(parent, &str);
-                printf("  looking at parent device '%s':\n", str);
-                (void) sd_device_get_sysname(parent, &str);
-                printf("    KERNELS==\"%s\"\n", str);
-                if (sd_device_get_subsystem(parent, &str) < 0)
-                        str = "";
-                printf("    SUBSYSTEMS==\"%s\"\n", str);
-                if (sd_device_get_driver(parent, &str) < 0)
-                        str = "";
-                printf("    DRIVERS==\"%s\"\n", str);
-                print_all_attributes(parent, "ATTRS");
+                r = print_all_attributes(parent, true);
+                if (r < 0)
+                        return r;
         }
 
         return 0;
index 2ca98a729f01541486e43cc3304cce3e897734d4..8cd8ed1a1ee6c2e052eb29530eda25446552d7aa 100644 (file)
@@ -169,24 +169,13 @@ static int parse_argv(int argc, char *argv[]) {
                         subsystem = devtype = NULL;
                         break;
                 }
-                case 't': {
-                        _cleanup_free_ char *tag = NULL;
-
-                        r = set_ensure_allocated(&arg_tag_filter, &string_hash_ops);
+                case 't':
+                        /* optarg is stored in argv[], so we don't need to copy it */
+                        r = set_ensure_put(&arg_tag_filter, &string_hash_ops, optarg);
                         if (r < 0)
                                 return r;
-
-                        tag = strdup(optarg);
-                        if (!tag)
-                                return -ENOMEM;
-
-                        r = set_put(arg_tag_filter, tag);
-                        if (r < 0)
-                                return r;
-
-                        tag = NULL;
                         break;
-                }
+
                 case 'V':
                         return print_version();
                 case 'h':
@@ -260,7 +249,7 @@ int monitor_main(int argc, char *argv[], void *userdata) {
 
 finalize:
         hashmap_free_free_free(arg_subsystem_filter);
-        set_free_free(arg_tag_filter);
+        set_free(arg_tag_filter);
 
         return r;
 }
index 7d7044eac3c019dc5cb58394782321d560ab3d79..66c7103d783ac729756319fa251518d3b06e8ec9 100644 (file)
 
 #include "sd-bus.h"
 #include "sd-login.h"
+#include "sd-messages.h"
 
+#include "bus-util.h"
+#include "io-util.h"
 #include "libudev-util.h"
 #include "string-util.h"
 #include "strv.h"
@@ -86,54 +89,69 @@ static int parse_argv(int argc, char *argv[]) {
 
 static int emit_deprecation_warning(void) {
         _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
-        _cleanup_free_ char *unit = NULL, *unit_path = NULL;
-        _cleanup_strv_free_ char **a = NULL, **b = NULL;
+        _cleanup_strv_free_ char **a = NULL;
+        _cleanup_free_ char *unit = NULL;
         int r;
 
         r = sd_pid_get_unit(0, &unit);
-        if (r < 0 || !streq(unit, "systemd-udev-settle.service"))
+        if (r < 0) {
+                log_debug_errno(r, "Failed to determine unit we run in, ignoring: %m");
                 return 0;
+        }
 
-        log_notice("systemd-udev-settle.service is deprecated.");
+        if (!streq(unit, "systemd-udev-settle.service"))
+                return 0;
 
-        r = sd_bus_open_system(&bus);
-        if (r < 0)
-                return log_debug_errno(r, "Failed to open system bus, skipping dependency queries: %m");
-
-        unit_path = unit_dbus_path_from_name("systemd-udev-settle.service");
-        if (!unit_path)
-                return -ENOMEM;
-
-        (void) sd_bus_get_property_strv(
-                        bus,
-                        "org.freedesktop.systemd1",
-                        unit_path,
-                        "org.freedesktop.systemd1.Unit",
-                        "WantedBy",
-                        NULL,
-                        &a);
-
-        (void) sd_bus_get_property_strv(
-                        bus,
-                        "org.freedesktop.systemd1",
-                        unit_path,
-                        "org.freedesktop.systemd1.Unit",
-                        "RequiredBy",
-                        NULL,
-                        &b);
-
-        r = strv_extend_strv(&a, b, true);
+        r = bus_connect_system_systemd(&bus);
         if (r < 0)
-                return r;
+                log_debug_errno(r, "Failed to open connection to systemd, skipping dependency queries: %m");
+        else {
+                _cleanup_strv_free_ char **b = NULL;
+                _cleanup_free_ char *unit_path = NULL;
+
+                unit_path = unit_dbus_path_from_name("systemd-udev-settle.service");
+                if (!unit_path)
+                        return -ENOMEM;
 
-        if (!strv_isempty(a)) {
+                (void) sd_bus_get_property_strv(
+                                bus,
+                                "org.freedesktop.systemd1",
+                                unit_path,
+                                "org.freedesktop.systemd1.Unit",
+                                "WantedBy",
+                                NULL,
+                                &a);
+
+                (void) sd_bus_get_property_strv(
+                                bus,
+                                "org.freedesktop.systemd1",
+                                unit_path,
+                                "org.freedesktop.systemd1.Unit",
+                                "RequiredBy",
+                                NULL,
+                                &b);
+
+                r = strv_extend_strv(&a, b, true);
+                if (r < 0)
+                        return r;
+        }
+
+        if (strv_isempty(a))
+                /* Print a simple message if we cannot determine the dependencies */
+                log_notice("systemd-udev-settle.service is deprecated.");
+        else {
+                /* Print a longer, structured message if we can acquire the dependencies (this should be the
+                 * common case). This is hooked up with a catalog entry and everything. */
                 _cleanup_free_ char *t = NULL;
 
                 t = strv_join(a, ", ");
                 if (!t)
                         return -ENOMEM;
 
-                log_notice("Hint: please fix %s not to pull it in.", t);
+                log_struct(LOG_NOTICE,
+                           "MESSAGE=systemd-udev-settle.service is deprecated. Please fix %s not to pull it in.", t,
+                           "OFFENDING_UNITS=%s", t,
+                           "MESSAGE_ID=" SD_MESSAGE_SYSTEMD_UDEV_SETTLE_DEPRECATED_STR);
         }
 
         return 0;
@@ -141,9 +159,8 @@ static int emit_deprecation_warning(void) {
 
 int settle_main(int argc, char *argv[], void *userdata) {
         _cleanup_(udev_queue_unrefp) struct udev_queue *queue = NULL;
-        struct pollfd pfd;
         usec_t deadline;
-        int r;
+        int r, fd;
 
         r = parse_argv(argc, argv);
         if (r <= 0)
@@ -177,17 +194,12 @@ int settle_main(int argc, char *argv[], void *userdata) {
         if (!queue)
                 return log_error_errno(errno, "Failed to get udev queue: %m");
 
-        r = udev_queue_get_fd(queue);
-        if (r < 0) {
-                log_debug_errno(r, "Queue is empty, nothing to watch.");
+        fd = udev_queue_get_fd(queue);
+        if (fd < 0) {
+                log_debug_errno(fd, "Queue is empty, nothing to watch: %m");
                 return 0;
         }
 
-        pfd = (struct pollfd) {
-                .events = POLLIN,
-                .fd = r,
-        };
-
         (void) emit_deprecation_warning();
 
         for (;;) {
@@ -202,7 +214,10 @@ int settle_main(int argc, char *argv[], void *userdata) {
                         return -ETIMEDOUT;
 
                 /* wake up when queue becomes empty */
-                if (poll(&pfd, 1, MSEC_PER_SEC) > 0 && pfd.revents & POLLIN) {
+                r = fd_wait_for_event(fd, POLLIN, MSEC_PER_SEC);
+                if (r < 0)
+                        return r;
+                if (r & POLLIN) {
                         r = udev_queue_flush(queue);
                         if (r < 0)
                                 return log_error_errno(r, "Failed to flush queue: %m");
index 404e3b5a7b1149fbc4bf94d5d1a5c658023a0faf..5b9b65439ad30f5c4ce8fed9259f1f243b7e557f 100644 (file)
@@ -123,7 +123,7 @@ int test_main(int argc, char *argv[], void *userdata) {
 
         udev_builtin_init();
 
-        r = udev_rules_new(&rules, arg_resolve_name_timing);
+        r = udev_rules_load(&rules, arg_resolve_name_timing);
         if (r < 0) {
                 log_error_errno(r, "Failed to read udev rules: %m");
                 goto out;
@@ -143,7 +143,7 @@ int test_main(int argc, char *argv[], void *userdata) {
         assert_se(sigfillset(&mask) >= 0);
         assert_se(sigprocmask(SIG_SETMASK, &mask, &sigmask_orig) >= 0);
 
-        udev_event_execute_rules(event, 60 * USEC_PER_SEC, NULL, rules);
+        udev_event_execute_rules(event, 60 * USEC_PER_SEC, SIGKILL, NULL, rules);
 
         FOREACH_DEVICE_PROPERTY(dev, key, value)
                 printf("%s=%s\n", key, value);
index 60c68b5029cf986981902c287d50d9309be291d8..39113d2fa23294b5ad3dc963681b42389c52f7b3 100644 (file)
@@ -23,7 +23,7 @@
 static bool arg_verbose = false;
 static bool arg_dry_run = false;
 
-static int exec_list(sd_device_enumerator *e, const char *action, Set *settle_set) {
+static int exec_list(sd_device_enumerator *e, const char *action, Set **settle_set) {
         sd_device *d;
         int r, ret = 0;
 
@@ -172,7 +172,7 @@ int trigger_main(int argc, char *argv[], void *userdata) {
         _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
         _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *m = NULL;
         _cleanup_(sd_event_unrefp) sd_event *event = NULL;
-        _cleanup_set_free_free_ Set *settle_set = NULL;
+        _cleanup_set_free_ Set *settle_set = NULL;
         usec_t ping_timeout_usec = 5 * USEC_PER_SEC;
         bool settle = false, ping = false;
         int c, r;
@@ -342,7 +342,7 @@ int trigger_main(int argc, char *argv[], void *userdata) {
         }
 
         if (settle) {
-                settle_set = set_new(&string_hash_ops);
+                settle_set = set_new(&string_hash_ops_free);
                 if (!settle_set)
                         return log_oom();
 
@@ -377,7 +377,8 @@ int trigger_main(int argc, char *argv[], void *userdata) {
         default:
                 assert_not_reached("Unknown device type");
         }
-        r = exec_list(e, action, settle_set);
+
+        r = exec_list(e, action, settle ? &settle_set : NULL);
         if (r < 0)
                 return r;
 
index e6dbb111a9a05b349286ca2d2d1ef91f7e2c0a13..e476f88f00812db5a3abc907493291683c737a0a 100644 (file)
@@ -11,6 +11,7 @@
 #include "selinux-util.h"
 #include "string-util.h"
 #include "udevadm.h"
+#include "udevd.h"
 #include "udev-util.h"
 #include "verbs.h"
 #include "util.h"
@@ -110,6 +111,9 @@ static int udevadm_main(int argc, char *argv[]) {
 static int run(int argc, char *argv[]) {
         int r;
 
+        if (strstr(program_invocation_short_name, "udevd"))
+                return run_udevd(argc, argv);
+
         udev_parse_config();
         log_parse_environment();
         log_open();
@@ -120,7 +124,10 @@ static int run(int argc, char *argv[]) {
 
         log_set_max_level_realm(LOG_REALM_SYSTEMD, log_get_max_level());
 
-        mac_selinux_init();
+        r = mac_selinux_init();
+        if (r < 0)
+                return r;
+
         return udevadm_main(argc, argv);
 }
 
index 2ac1cac970fb93b5840354ecf95215511e5115cb..f3236dedfab75c3b865d121fc923130967e11150 100644 (file)
@@ -59,6 +59,7 @@
 #include "strv.h"
 #include "strxcpyx.h"
 #include "syslog-util.h"
+#include "udevd.h"
 #include "udev-builtin.h"
 #include "udev-ctrl.h"
 #include "udev-event.h"
@@ -74,6 +75,8 @@ static ResolveNameTiming arg_resolve_name_timing = RESOLVE_NAME_EARLY;
 static unsigned arg_children_max = 0;
 static usec_t arg_exec_delay_usec = 0;
 static usec_t arg_event_timeout_usec = 180 * USEC_PER_SEC;
+static int arg_timeout_signal = SIGKILL;
+static bool arg_blockdev_read_only = false;
 
 typedef struct Manager {
         sd_event *event;
@@ -227,7 +230,7 @@ static int on_event_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
         assert(event);
         assert(event->worker);
 
-        kill_and_sigcont(event->worker->pid, SIGKILL);
+        kill_and_sigcont(event->worker->pid, arg_timeout_signal);
         event->worker->state = WORKER_KILLED;
 
         log_device_error(event->dev, "Worker ["PID_FMT"] processing SEQNUM=%"PRIu64" killed", event->worker->pid, event->seqnum);
@@ -381,6 +384,56 @@ static int worker_lock_block_device(sd_device *dev, int *ret_fd) {
         return 1;
 }
 
+static int worker_mark_block_device_read_only(sd_device *dev) {
+        _cleanup_close_ int fd = -1;
+        const char *val;
+        int state = 1, r;
+
+        assert(dev);
+
+        if (!arg_blockdev_read_only)
+                return 0;
+
+        /* Do this only once, when the block device is new. If the device is later retriggered let's not
+         * toggle the bit again, so that people can boot up with full read-only mode and then unset the bit
+         * for specific devices only. */
+        if (!device_for_action(dev, DEVICE_ACTION_ADD))
+                return 0;
+
+        r = sd_device_get_subsystem(dev, &val);
+        if (r < 0)
+                return log_device_debug_errno(dev, r, "Failed to get subsystem: %m");
+
+        if (!streq(val, "block"))
+                return 0;
+
+        r = sd_device_get_sysname(dev, &val);
+        if (r < 0)
+                return log_device_debug_errno(dev, r, "Failed to get sysname: %m");
+
+        /* Exclude synthetic devices for now, this is supposed to be a safety feature to avoid modification
+         * of physical devices, and what sits on top of those doesn't really matter if we don't allow the
+         * underlying block devices to receive changes. */
+        if (STARTSWITH_SET(val, "dm-", "md", "drbd", "loop", "nbd", "zram"))
+                return 0;
+
+        r = sd_device_get_devname(dev, &val);
+        if (r == -ENOENT)
+                return 0;
+        if (r < 0)
+                return log_device_debug_errno(dev, r, "Failed to get devname: %m");
+
+        fd = open(val, O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK);
+        if (fd < 0)
+                return log_device_debug_errno(dev, errno, "Failed to open '%s', ignoring: %m", val);
+
+        if (ioctl(fd, BLKROSET, &state) < 0)
+                return log_device_warning_errno(dev, errno, "Failed to mark block device '%s' read-only: %m", val);
+
+        log_device_info(dev, "Successfully marked block device '%s' read-only.", val);
+        return 0;
+}
+
 static int worker_process_device(Manager *manager, sd_device *dev) {
         _cleanup_(udev_event_freep) UdevEvent *udev_event = NULL;
         _cleanup_close_ int fd_lock = -1;
@@ -410,12 +463,14 @@ static int worker_process_device(Manager *manager, sd_device *dev) {
         if (r < 0)
                 return r;
 
+        (void) worker_mark_block_device_read_only(dev);
+
         /* apply rules, create node, symlinks */
-        r = udev_event_execute_rules(udev_event, arg_event_timeout_usec, manager->properties, manager->rules);
+        r = udev_event_execute_rules(udev_event, arg_event_timeout_usec, arg_timeout_signal, manager->properties, manager->rules);
         if (r < 0)
                 return r;
 
-        udev_event_execute_run(udev_event, arg_event_timeout_usec);
+        udev_event_execute_run(udev_event, arg_event_timeout_usec, arg_timeout_signal);
 
         if (!manager->rtnl)
                 /* in case rtnl was initialized */
@@ -564,6 +619,14 @@ static void event_run(Manager *manager, struct event *event) {
         assert(manager);
         assert(event);
 
+        if (DEBUG_LOGGING) {
+                DeviceAction action;
+
+                r = device_get_action(event->dev, &action);
+                log_device_debug(event->dev, "Device (SEQNUM=%"PRIu64", ACTION=%s) ready for processing",
+                                 event->seqnum, r >= 0 ? device_action_to_string(action) : "<unknown>");
+        }
+
         HASHMAP_FOREACH(worker, manager->workers, i) {
                 if (worker->state != WORKER_IDLE)
                         continue;
@@ -775,6 +838,9 @@ static int is_device_busy(Manager *manager, struct event *event) {
         return false;
 
 set_delaying_seqnum:
+        log_device_debug(event->dev, "SEQNUM=%" PRIu64 " blocked by SEQNUM=%" PRIu64,
+                         event->seqnum, loop_event->seqnum);
+
         event->delaying_seqnum = loop_event->seqnum;
         return true;
 }
@@ -859,7 +925,7 @@ static void event_queue_start(Manager *manager) {
         udev_builtin_init();
 
         if (!manager->rules) {
-                r = udev_rules_new(&manager->rules, arg_resolve_name_timing);
+                r = udev_rules_load(&manager->rules, arg_resolve_name_timing);
                 if (r < 0) {
                         log_warning_errno(r, "Failed to read udev rules: %m");
                         return;
@@ -900,19 +966,15 @@ static int on_worker(sd_event_source *s, int fd, uint32_t revents, void *userdat
                         .iov_base = &msg,
                         .iov_len = sizeof(msg),
                 };
-                union {
-                        struct cmsghdr cmsghdr;
-                        uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
-                } control = {};
+                CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control;
                 struct msghdr msghdr = {
                         .msg_iov = &iovec,
                         .msg_iovlen = 1,
                         .msg_control = &control,
                         .msg_controllen = sizeof(control),
                 };
-                struct cmsghdr *cmsg;
                 ssize_t size;
-                struct ucred *ucred = NULL;
+                struct ucred *ucred;
                 struct worker *worker;
 
                 size = recvmsg_safe(fd, &msghdr, MSG_DONTWAIT);
@@ -931,12 +993,7 @@ static int on_worker(sd_event_source *s, int fd, uint32_t revents, void *userdat
                         continue;
                 }
 
-                CMSG_FOREACH(cmsg, &msghdr)
-                        if (cmsg->cmsg_level == SOL_SOCKET &&
-                            cmsg->cmsg_type == SCM_CREDENTIALS &&
-                            cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred)))
-                                ucred = (struct ucred*) CMSG_DATA(cmsg);
-
+                ucred = CMSG_FIND_DATA(&msghdr, SOL_SOCKET, SCM_CREDENTIALS, struct ucred);
                 if (!ucred || ucred->pid <= 0) {
                         log_warning("Ignoring worker message without valid PID");
                         continue;
@@ -1413,15 +1470,13 @@ static int listen_fds(int *ret_ctrl, int *ret_netlink) {
  *   udev.children_max=<number of workers>     events are fully serialized if set to 1
  *   udev.exec_delay=<number of seconds>       delay execution of every executed program
  *   udev.event_timeout=<number of seconds>    seconds to wait before terminating an event
+ *   udev.blockdev_read_only<=bool>            mark all block devices read-only when they appear
  */
 static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
-        int r = 0;
+        int r;
 
         assert(key);
 
-        if (!value)
-                return 0;
-
         if (proc_cmdline_key_streq(key, "udev.log_priority")) {
 
                 if (proc_cmdline_value_missing(key, value))
@@ -1452,8 +1507,38 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
 
                 r = parse_sec(value, &arg_exec_delay_usec);
 
-        } else if (startswith(key, "udev."))
-                log_warning("Unknown udev kernel command line option \"%s\", ignoring", key);
+        } else if (proc_cmdline_key_streq(key, "udev.timeout_signal")) {
+
+                if (proc_cmdline_value_missing(key, value))
+                        return 0;
+
+                r = signal_from_string(value);
+                if (r > 0)
+                        arg_timeout_signal = r;
+
+        } else if (proc_cmdline_key_streq(key, "udev.blockdev_read_only")) {
+
+                if (!value)
+                        arg_blockdev_read_only = true;
+                else {
+                        r = parse_boolean(value);
+                        if (r < 0)
+                                log_warning_errno(r, "Failed to parse udev.blockdev-read-only argument, ignoring: %s", value);
+                        else
+                                arg_blockdev_read_only = r;
+                }
+
+                if (arg_blockdev_read_only)
+                        log_notice("All physical block devices will be marked read-only.");
+
+                return 0;
+
+        } else {
+                if (startswith(key, "udev."))
+                        log_warning("Unknown udev kernel command line option \"%s\", ignoring.", key);
+
+                return 0;
+        }
 
         if (r < 0)
                 log_warning_errno(r, "Failed to parse \"%s=%s\", ignoring: %m", key, value);
@@ -1470,7 +1555,7 @@ static int help(void) {
                 return log_oom();
 
         printf("%s [OPTIONS...]\n\n"
-               "Manages devices.\n\n"
+               "Rule-based manager for device events and files.\n\n"
                "  -h --help                   Print this message\n"
                "  -V --version                Print version of the program\n"
                "  -d --daemon                 Detach and run in the background\n"
@@ -1489,15 +1574,20 @@ static int help(void) {
 }
 
 static int parse_argv(int argc, char *argv[]) {
+        enum {
+                ARG_TIMEOUT_SIGNAL,
+        };
+
         static const struct option options[] = {
-                { "daemon",             no_argument,            NULL, 'd' },
-                { "debug",              no_argument,            NULL, 'D' },
-                { "children-max",       required_argument,      NULL, 'c' },
-                { "exec-delay",         required_argument,      NULL, 'e' },
-                { "event-timeout",      required_argument,      NULL, 't' },
-                { "resolve-names",      required_argument,      NULL, 'N' },
-                { "help",               no_argument,            NULL, 'h' },
-                { "version",            no_argument,            NULL, 'V' },
+                { "daemon",             no_argument,            NULL, 'd'                 },
+                { "debug",              no_argument,            NULL, 'D'                 },
+                { "children-max",       required_argument,      NULL, 'c'                 },
+                { "exec-delay",         required_argument,      NULL, 'e'                 },
+                { "event-timeout",      required_argument,      NULL, 't'                 },
+                { "resolve-names",      required_argument,      NULL, 'N'                 },
+                { "help",               no_argument,            NULL, 'h'                 },
+                { "version",            no_argument,            NULL, 'V'                 },
+                { "timeout-signal",     required_argument,      NULL,  ARG_TIMEOUT_SIGNAL },
                 {}
         };
 
@@ -1522,6 +1612,14 @@ static int parse_argv(int argc, char *argv[]) {
                         if (r < 0)
                                 log_warning_errno(r, "Failed to parse --exec-delay= value '%s', ignoring: %m", optarg);
                         break;
+                case ARG_TIMEOUT_SIGNAL:
+                        r = signal_from_string(optarg);
+                        if (r <= 0)
+                                log_warning_errno(r, "Failed to parse --timeout-signal= value '%s', ignoring: %m", optarg);
+                        else
+                                arg_timeout_signal = r;
+
+                        break;
                 case 't':
                         r = parse_sec(optarg, &arg_event_timeout_usec);
                         if (r < 0)
@@ -1689,7 +1787,7 @@ static int main_loop(Manager *manager) {
 
         udev_builtin_init();
 
-        r = udev_rules_new(&manager->rules, arg_resolve_name_timing);
+        r = udev_rules_load(&manager->rules, arg_resolve_name_timing);
         if (!manager->rules)
                 return log_error_errno(r, "Failed to read udev rules: %m");
 
@@ -1711,7 +1809,7 @@ static int main_loop(Manager *manager) {
         return r;
 }
 
-static int run(int argc, char *argv[]) {
+int run_udevd(int argc, char *argv[]) {
         _cleanup_free_ char *cgroup = NULL;
         _cleanup_(manager_freep) Manager *manager = NULL;
         int fd_ctrl = -1, fd_uevent = -1;
@@ -1719,7 +1817,7 @@ static int run(int argc, char *argv[]) {
 
         log_set_target(LOG_TARGET_AUTO);
         log_open();
-        udev_parse_config_full(&arg_children_max, &arg_exec_delay_usec, &arg_event_timeout_usec, &arg_resolve_name_timing);
+        udev_parse_config_full(&arg_children_max, &arg_exec_delay_usec, &arg_event_timeout_usec, &arg_resolve_name_timing, &arg_timeout_signal);
         log_parse_environment();
         log_open(); /* Done again to update after reading configuration. */
 
@@ -1743,12 +1841,13 @@ static int run(int argc, char *argv[]) {
                 return r;
 
         if (arg_children_max == 0) {
-                unsigned long cpu_limit, mem_limit;
-                unsigned long cpu_count = 1;
-                cpu_set_t cpu_set;
+                unsigned long cpu_limit, mem_limit, cpu_count = 1;
 
-                if (sched_getaffinity(0, sizeof(cpu_set), &cpu_set) == 0)
-                        cpu_count = CPU_COUNT(&cpu_set);
+                r = cpus_in_affinity_mask();
+                if (r < 0)
+                        log_warning_errno(r, "Failed to determine number of local CPUs, ignoring: %m");
+                else
+                        cpu_count = r;
 
                 cpu_limit = cpu_count * 2 + 16;
                 mem_limit = MAX(physical_memory() / (128UL*1024*1024), 10U);
@@ -1760,22 +1859,16 @@ static int run(int argc, char *argv[]) {
         }
 
         /* set umask before creating any file/directory */
-        r = chdir("/");
-        if (r < 0)
-                return log_error_errno(errno, "Failed to change dir to '/': %m");
-
         umask(022);
 
         r = mac_selinux_init();
         if (r < 0)
-                return log_error_errno(r, "Could not initialize labelling: %m");
+                return r;
 
         r = mkdir_errno_wrapper("/run/udev", 0755);
         if (r < 0 && r != -EEXIST)
                 return log_error_errno(r, "Failed to create /run/udev: %m");
 
-        dev_setup(NULL, UID_INVALID, GID_INVALID);
-
         if (getppid() == 1 && sd_booted() > 0) {
                 /* Get our own cgroup, we regularly kill everything udev has left behind.
                  * We only do this on systemd systems, and only if we are directly spawned
@@ -1818,13 +1911,7 @@ static int run(int argc, char *argv[]) {
 
                 /* child */
                 (void) setsid();
-
-                r = set_oom_score_adjust(-1000);
-                if (r < 0)
-                        log_debug_errno(r, "Failed to adjust OOM score, ignoring: %m");
         }
 
         return main_loop(manager);
 }
-
-DEFINE_MAIN_FUNCTION(run);
diff --git a/src/udev/udevd.h b/src/udev/udevd.h
new file mode 100644 (file)
index 0000000..848ffc2
--- /dev/null
@@ -0,0 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+#pragma once
+
+int run_udevd(int argc, char *argv[]);
index bbd14165d5ad4b4199c5a554911bffdb81b58e9f..c001802dc910b93eb347ef51f39b7917ecf6559d 100644 (file)
@@ -49,10 +49,8 @@ int main(int argc, char *argv[]) {
         }
 
         r = mac_selinux_init();
-        if (r < 0) {
-                log_error_errno(r, "SELinux setup failed: %m");
+        if (r < 0)
                 return EXIT_FAILURE;
-        }
 
         r = apply_timestamp("/etc/.updated", &st.st_mtim);
         q = apply_timestamp("/var/.updated", &st.st_mtim);
index 55fd8ba46e4c1a530ace772e6c4f44047b0b7c92..47354d50129090875b799341f0a361dd25df1806 100644 (file)
@@ -84,11 +84,10 @@ static int get_current_runlevel(Context *c) {
 
         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
         int r;
-        unsigned i;
 
         assert(c);
 
-        for (i = 0; i < ELEMENTSOF(table); i++) {
+        for (size_t i = 0; i < ELEMENTSOF(table); i++) {
                 _cleanup_free_ char *state = NULL, *path = NULL;
 
                 path = unit_dbus_path_from_name(table[i].special);
@@ -125,9 +124,8 @@ static int on_reboot(Context *c) {
 #if HAVE_AUDIT
         if (c->audit_fd >= 0)
                 if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_BOOT, "", "systemd-update-utmp", NULL, NULL, NULL, 1) < 0 &&
-                    errno != EPERM) {
+                    errno != EPERM)
                         r = log_error_errno(errno, "Failed to send audit message: %m");
-                }
 #endif
 
         /* If this call fails it will return 0, which
@@ -154,9 +152,8 @@ static int on_shutdown(Context *c) {
 #if HAVE_AUDIT
         if (c->audit_fd >= 0)
                 if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_SHUTDOWN, "", "systemd-update-utmp", NULL, NULL, NULL, 1) < 0 &&
-                    errno != EPERM) {
+                    errno != EPERM)
                         r = log_error_errno(errno, "Failed to send audit message: %m");
-                }
 #endif
 
         q = utmp_put_shutdown();
@@ -225,9 +222,6 @@ static int run(int argc, char *argv[]) {
         };
         int r;
 
-        if (getppid() != 1)
-                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
-                                       "This program should be invoked by init only.");
         if (argc != 2)
                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                        "This program requires one argument.");
index c24142951709eadd0e59606c03655f1f663a8f02..cd92b696c0c20733599deea4c37ecce3fa587f52 100644 (file)
@@ -25,7 +25,9 @@ static int run(int argc, char *argv[]) {
 
         umask(0022);
 
-        mac_selinux_init();
+        r = mac_selinux_init();
+        if (r < 0)
+                return r;
 
         if (streq(argv[1], "start")) {
                 r = unlink_or_warn("/run/nologin");
index 39dff013a5720d34505459c9acb7a09ae90fd519..c973ee9c0114e27a0390f5a2904ac51a45d3ae72 100644 (file)
@@ -85,7 +85,7 @@ static int show_user(UserRecord *ur, Table *table) {
                                 TABLE_STRING, user_record_shell(ur),
                                 TABLE_INT, (int) user_record_disposition(ur));
                 if (r < 0)
-                        return log_error_errno(r, "Failed to add row to table: %m");
+                        return table_log_add_error(r);
 
                 break;
 
@@ -180,7 +180,7 @@ static int display_user(int argc, char *argv[], void *userdata) {
         if (table) {
                 r = table_print(table, NULL);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to show table: %m");
+                        return table_log_print_error(r);
         }
 
         return ret;
@@ -234,12 +234,12 @@ static int show_group(GroupRecord *gr, Table *table) {
                                 TABLE_GID, gr->gid,
                                 TABLE_INT, (int) group_record_disposition(gr));
                 if (r < 0)
-                        return log_error_errno(r, "Failed to add row to table: %m");
+                        return table_log_add_error(r);
 
                 break;
 
         default:
-                assert_not_reached("Unexpected disply mode");
+                assert_not_reached("Unexpected display mode");
         }
 
         return 0;
@@ -330,7 +330,7 @@ static int display_group(int argc, char *argv[], void *userdata) {
         if (table) {
                 r = table_print(table, NULL);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to show table: %m");
+                        return table_log_print_error(r);
         }
 
         return ret;
@@ -377,7 +377,7 @@ static int show_membership(const char *user, const char *group, Table *table) {
                                 TABLE_STRING, user,
                                 TABLE_STRING, group);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to add row to table: %m");
+                        return table_log_add_error(r);
 
                 break;
 
@@ -463,7 +463,7 @@ static int display_memberships(int argc, char *argv[], void *userdata) {
         if (table) {
                 r = table_print(table, NULL);
                 if (r < 0)
-                        return log_error_errno(r, "Failed to show table: %m");
+                        return table_log_print_error(r);
         }
 
         return ret;
@@ -521,7 +521,7 @@ static int display_services(int argc, char *argv[], void *userdata) {
                                    TABLE_STRING, no ?: "yes",
                                    TABLE_SET_COLOR, no ? ansi_highlight_red() : ansi_highlight_green());
                 if (r < 0)
-                        return log_error_errno(r, "Failed to add table row: %m");
+                        return table_log_add_error(r);
         }
 
         if (table_get_rows(t) <= 0) {
@@ -761,9 +761,7 @@ static int run(int argc, char *argv[]) {
 
         int r;
 
-        log_show_color(true);
-        log_parse_environment();
-        log_open();
+        log_setup_cli();
 
         r = parse_argv(argc, argv);
         if (r <= 0)
index 978fd1d7886223763e1aba953aae128633b08b53..dbc285e61aac1b0224968c208836e39e2d769069 100644 (file)
@@ -20,8 +20,8 @@
  */
 
 static int run(int argc, char *argv[]) {
-        _cleanup_(notify_on_cleanup) const char *notify_stop = NULL;
         _cleanup_(manager_freep) Manager *m = NULL;
+        _cleanup_(notify_on_cleanup) const char *notify_stop = NULL;
         int r;
 
         log_setup_service();
index 3bc5ecc1d0393d47ceffac5e34c8393c181bd617..d7202099be29071fca360e3a9ff5dc04475350a1 100644 (file)
@@ -9,6 +9,7 @@
 #include "fd-util.h"
 #include "group-record-nss.h"
 #include "group-record.h"
+#include "io-util.h"
 #include "main-func.h"
 #include "process-util.h"
 #include "strv.h"
@@ -137,9 +138,9 @@ static int vl_method_get_user_record(Varlink *link, JsonVariant *parameters, Var
 
         if (streq_ptr(p.service, "io.systemd.NameServiceSwitch")) {
                 if (uid_is_valid(p.uid))
-                        r = nss_user_record_by_uid(p.uid, &hr);
+                        r = nss_user_record_by_uid(p.uid, true, &hr);
                 else if (p.user_name)
-                        r = nss_user_record_by_name(p.user_name, &hr);
+                        r = nss_user_record_by_name(p.user_name, true, &hr);
                 else {
                         _cleanup_(json_variant_unrefp) JsonVariant *last = NULL;
 
@@ -324,9 +325,9 @@ static int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, Va
         if (streq_ptr(p.service, "io.systemd.NameServiceSwitch")) {
 
                 if (gid_is_valid(p.gid))
-                        r = nss_group_record_by_gid(p.gid, &g);
+                        r = nss_group_record_by_gid(p.gid, true, &g);
                 else if (p.group_name)
-                        r = nss_group_record_by_name(p.group_name, &g);
+                        r = nss_group_record_by_name(p.group_name, true, &g);
                 else {
                         _cleanup_(json_variant_unrefp) JsonVariant *last = NULL;
 
@@ -467,7 +468,7 @@ static int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, Var
                         const char *last = NULL;
                         char **i;
 
-                        r = nss_group_record_by_name(p.group_name, &g);
+                        r = nss_group_record_by_name(p.group_name, true, &g);
                         if (r == -ESRCH)
                                 return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
                         if (r < 0)
@@ -659,7 +660,6 @@ static int process_connection(VarlinkServer *server, int fd) {
 static int run(int argc, char *argv[]) {
         usec_t start_time, listen_idle_usec, last_busy_usec = USEC_INFINITY;
         _cleanup_(varlink_server_unrefp) VarlinkServer *server = NULL;
-        _cleanup_close_ int lock = -1;
         unsigned n_iterations = 0;
         int m, listen_fd, r;
 
@@ -696,8 +696,8 @@ static int run(int argc, char *argv[]) {
                 return log_error_errno(r, "Failed to parse USERDB_FIXED_WORKER: %m");
         listen_idle_usec = r ? USEC_INFINITY : LISTEN_IDLE_USEC;
 
-        lock = userdb_nss_compat_disable();
-        if (lock < 0)
+        r = userdb_block_nss_systemd(true);
+        if (r < 0)
                 return log_error_errno(r, "Failed to disable userdb NSS compatibility: %m");
 
         start_time = now(CLOCK_MONOTONIC);
@@ -715,7 +715,8 @@ static int run(int argc, char *argv[]) {
                 n = now(CLOCK_MONOTONIC);
                 if (n >= usec_add(start_time, RUNTIME_MAX_USEC)) {
                         char buf[FORMAT_TIMESPAN_MAX];
-                        log_debug("Exiting worker, ran for %s, that's enough.", format_timespan(buf, sizeof(buf), usec_sub_unsigned(n, start_time), 0));
+                        log_debug("Exiting worker, ran for %s, that's enough.",
+                                  format_timespan(buf, sizeof(buf), usec_sub_unsigned(n, start_time), 0));
                         break;
                 }
 
@@ -723,7 +724,8 @@ static int run(int argc, char *argv[]) {
                         last_busy_usec = n;
                 else if (listen_idle_usec != USEC_INFINITY && n >= usec_add(last_busy_usec, listen_idle_usec)) {
                         char buf[FORMAT_TIMESPAN_MAX];
-                        log_debug("Exiting worker, been idle for %s, .", format_timespan(buf, sizeof(buf), usec_sub_unsigned(n, last_busy_usec), 0));
+                        log_debug("Exiting worker, been idle for %s.",
+                                  format_timespan(buf, sizeof(buf), usec_sub_unsigned(n, last_busy_usec), 0));
                         break;
                 }
 
@@ -736,7 +738,7 @@ static int run(int argc, char *argv[]) {
                 (void) rename_process("systemd-userwork: processing...");
 
                 if (fd == -EAGAIN)
-                        continue; /* The listening socket as SO_RECVTIMEO set, hence a time-out is expected
+                        continue; /* The listening socket has SO_RECVTIMEO set, hence a timeout is expected
                                    * after a while, let's check if it's time to exit though. */
                 if (fd == -EINTR)
                         continue; /* Might be that somebody attached via strace, let's just continue in that
@@ -745,18 +747,14 @@ static int run(int argc, char *argv[]) {
                         return log_error_errno(fd, "Failed to accept() from listening socket: %m");
 
                 if (now(CLOCK_MONOTONIC) <= usec_add(n, PRESSURE_SLEEP_TIME_USEC)) {
-                        struct pollfd pfd = {
-                                .fd = listen_fd,
-                                .events = POLLIN,
-                        };
-
                         /* We only slept a very short time? If so, let's see if there are more sockets
                          * pending, and if so, let's ask our parent for more workers */
 
-                        if (poll(&pfd, 1, 0) < 0)
-                                return log_error_errno(errno, "Failed to test for POLLIN on listening socket: %m");
+                        r = fd_wait_for_event(listen_fd, POLLIN, 0);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to test for POLLIN on listening socket: %m");
 
-                        if (FLAGS_SET(pfd.revents, POLLIN)) {
+                        if (FLAGS_SET(r, POLLIN)) {
                                 pid_t parent;
 
                                 parent = getppid();
index 9c2fe9a1b409dce707f96adb7068e1333e754029..e475402d9d46eaef9b2b75930525c1367373b2f0 100644 (file)
@@ -6,9 +6,11 @@
 
 #include "alloc-util.h"
 #include "crypt-util.h"
+#include "fileio.h"
 #include "hexdecoct.h"
 #include "log.h"
 #include "main-func.h"
+#include "path-util.h"
 #include "pretty-print.h"
 #include "string-util.h"
 #include "terminal-util.h"
@@ -29,7 +31,7 @@ static int help(void) {
         if (r < 0)
                 return log_oom();
 
-        printf("%s attach VOLUME DATADEVICE HASHDEVICE ROOTHASH\n"
+        printf("%s attach VOLUME DATADEVICE HASHDEVICE ROOTHASH [ROOTHASHSIG]\n"
                "%s detach VOLUME\n\n"
                "Attaches or detaches an integrity protected block device.\n"
                "\nSee the %s for details.\n"
@@ -87,7 +89,28 @@ static int run(int argc, char *argv[]) {
                 if (r < 0)
                         return log_error_errno(r, "Failed to configure data device: %m");
 
-                r = crypt_activate_by_volume_key(cd, argv[2], m, l, CRYPT_ACTIVATE_READONLY);
+                if (argc > 6) {
+#if HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY
+                        _cleanup_free_ char *hash_sig = NULL;
+                        size_t hash_sig_size;
+                        char *value;
+
+                        if ((value = startswith(argv[6], "base64:"))) {
+                                r = unbase64mem(value, strlen(value), (void *)&hash_sig, &hash_sig_size);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to parse root hash signature '%s': %m", argv[6]);
+                        } else {
+                                r = read_full_file_full(AT_FDCWD, argv[6], READ_FULL_FILE_CONNECT_SOCKET, &hash_sig, &hash_sig_size);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to read root hash signature: %m");
+                        }
+
+                        r = crypt_activate_by_signed_key(cd, argv[2], m, l, hash_sig, hash_sig_size, CRYPT_ACTIVATE_READONLY);
+#else
+                        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "activation of verity device with signature %s requested, but not supported by cryptsetup due to missing crypt_activate_by_signed_key()", argv[6]);
+#endif
+                } else
+                        r = crypt_activate_by_volume_key(cd, argv[2], m, l, CRYPT_ACTIVATE_READONLY);
                 if (r < 0)
                         return log_error_errno(r, "Failed to set up verity device: %m");
 
index 4219dce83a971ce45d90edf09bcf1707cdeafbf7..7b0bf8e264a75a92e4ceb3ab5963fd171152162f 100644 (file)
@@ -1,6 +1,6 @@
 /* Detailed project version that includes git commit when not built from a release.
  * Use this in preference to PROJECT_VERSION, with the following exceptions:
- * - where a simplified form is expected for compatiblity, for example
+ * - where a simplified form is expected for compatibility, for example
  *   'udevadm version',
  * - where a simplified machine-parsable form is more useful, for example
  *   pkgconfig files and version information written to binary files.
index af78a87d6feff1a8b50f07f672c39a80c06c4433..e55864d6cc14665b615d734dc0a17a20986d7260 100644 (file)
@@ -29,7 +29,7 @@ static int make_volatile(const char *path) {
         if (r < 0)
                 return log_error_errno(r, "Couldn't generate volatile sysroot directory: %m");
 
-        r = mount_verbose(LOG_ERR, "tmpfs", "/run/systemd/volatile-sysroot", "tmpfs", MS_STRICTATIME, "mode=755");
+        r = mount_verbose(LOG_ERR, "tmpfs", "/run/systemd/volatile-sysroot", "tmpfs", MS_STRICTATIME, "mode=755" TMPFS_LIMITS_ROOTFS);
         if (r < 0)
                 goto finish_rmdir;
 
@@ -80,7 +80,7 @@ static int make_overlay(const char *path) {
         if (r < 0)
                 return log_error_errno(r, "Couldn't create overlay sysroot directory: %m");
 
-        r = mount_verbose(LOG_ERR, "tmpfs", "/run/systemd/overlay-sysroot", "tmpfs", MS_STRICTATIME, "mode=755");
+        r = mount_verbose(LOG_ERR, "tmpfs", "/run/systemd/overlay-sysroot", "tmpfs", MS_STRICTATIME, "mode=755" TMPFS_LIMITS_ROOTFS);
         if (r < 0)
                 goto finish;
 
diff --git a/src/xdg-autostart-generator/xdg-autostart-condition.c b/src/xdg-autostart-generator/xdg-autostart-condition.c
new file mode 100644 (file)
index 0000000..84a3561
--- /dev/null
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "main-func.h"
+#include "strv.h"
+
+/*
+ * This binary is intended to be run as an ExecCondition= in units generated
+ * by the xdg-autostart-generator. It does the appropriate checks against
+ * XDG_CURRENT_DESKTOP that are too advanced for simple ConditionEnvironment=
+ * matches.
+ */
+
+static int run(int argc, char *argv[]) {
+        _cleanup_strv_free_ char **only_show_in = NULL, **not_show_in = NULL, **desktops = NULL;
+        const char *xdg_current_desktop;
+        char **d;
+
+        if (argc != 3)
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "Wrong argument count. Expected the OnlyShowIn= and NotShowIn= sets, each colon separated.");
+
+        xdg_current_desktop = getenv("XDG_CURRENT_DESKTOP");
+        if (xdg_current_desktop) {
+                desktops = strv_split(xdg_current_desktop, ":");
+                if (!desktops)
+                        return log_oom();
+        }
+
+        only_show_in = strv_split(argv[1], ":");
+        not_show_in = strv_split(argv[2], ":");
+        if (!only_show_in || !not_show_in)
+                return log_oom();
+
+        /* Each desktop in XDG_CURRENT_DESKTOP needs to be matched in order. */
+        STRV_FOREACH(d, desktops) {
+                if (strv_contains(only_show_in, *d))
+                        return 0;
+                if (strv_contains(not_show_in, *d))
+                        return 1;
+        }
+
+        /* non-zero exit code when only_show_in has a proper value */
+        return !strv_isempty(only_show_in);
+}
+
+DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);
diff --git a/src/xdg-autostart-generator/xdg-autostart-generator.c b/src/xdg-autostart-generator/xdg-autostart-generator.c
new file mode 100644 (file)
index 0000000..53366a3
--- /dev/null
@@ -0,0 +1,116 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "dirent-util.h"
+#include "fd-util.h"
+#include "generator.h"
+#include "hashmap.h"
+#include "log.h"
+#include "main-func.h"
+#include "nulstr-util.h"
+#include "path-lookup.h"
+#include "stat-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "xdg-autostart-service.h"
+
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(xdgautostartservice_hash_ops, char, string_hash_func, string_compare_func, XdgAutostartService, xdg_autostart_service_free);
+
+static int enumerate_xdg_autostart(Hashmap *all_services) {
+        _cleanup_strv_free_ char **autostart_dirs = NULL;
+        _cleanup_strv_free_ char **config_dirs = NULL;
+        _unused_ _cleanup_strv_free_ char **data_dirs = NULL;
+        _cleanup_free_ char *user_config_autostart_dir = NULL;
+        char **path;
+        int r;
+
+        r = xdg_user_config_dir(&user_config_autostart_dir, "/autostart");
+        if (r < 0)
+                return r;
+        r = strv_extend(&autostart_dirs, user_config_autostart_dir);
+        if (r < 0)
+                return r;
+
+        r = xdg_user_dirs(&config_dirs, &data_dirs);
+        if (r < 0)
+                return r;
+        r = strv_extend_strv_concat(&autostart_dirs, config_dirs, "/autostart");
+        if (r < 0)
+                return r;
+
+        STRV_FOREACH(path, autostart_dirs) {
+                _cleanup_closedir_ DIR *d = NULL;
+                struct dirent *de;
+
+                d = opendir(*path);
+                if (!d) {
+                        if (errno != ENOENT)
+                                log_warning_errno(errno, "Opening %s failed, ignoring: %m", *path);
+                        continue;
+                }
+
+                FOREACH_DIRENT(de, d, log_warning_errno(errno, "Failed to enumerate directory %s, ignoring: %m", *path)) {
+                        _cleanup_free_ char *fpath = NULL, *name = NULL;
+                        _cleanup_(xdg_autostart_service_freep) XdgAutostartService *service = NULL;
+                        struct stat st;
+
+                        if (fstatat(dirfd(d), de->d_name, &st, 0) < 0) {
+                                log_warning_errno(errno, "stat() failed on %s/%s, ignoring: %m", *path, de->d_name);
+                                continue;
+                        }
+
+                        if (!S_ISREG(st.st_mode))
+                                continue;
+
+                        name = xdg_autostart_service_translate_name(de->d_name);
+                        if (!name)
+                                return log_oom();
+
+                        if (hashmap_contains(all_services, name))
+                                continue;
+
+                        fpath = path_join(*path, de->d_name);
+                        if (!fpath)
+                                return log_oom();
+
+                        service = xdg_autostart_service_parse_desktop(fpath);
+                        if (!service)
+                                return log_oom();
+                        service->name = TAKE_PTR(name);
+
+                        r = hashmap_put(all_services, service->name, service);
+                        if (r < 0)
+                                return log_oom();
+                        TAKE_PTR(service);
+                }
+        }
+
+        return 0;
+}
+
+static int run(const char *dest, const char *dest_early, const char *dest_late) {
+        _cleanup_(hashmap_freep) Hashmap *all_services = NULL;
+        XdgAutostartService *service;
+        Iterator j;
+        int r;
+
+        assert_se(dest_late);
+
+        all_services = hashmap_new(&xdgautostartservice_hash_ops);
+        if (!all_services)
+                return log_oom();
+
+        r = enumerate_xdg_autostart(all_services);
+        if (r < 0)
+                return r;
+
+        HASHMAP_FOREACH(service, all_services, j)
+                (void) xdg_autostart_service_generate_unit(service, dest_late);
+
+        return 0;
+}
+
+DEFINE_MAIN_GENERATOR_FUNCTION(run);
diff --git a/src/xdg-autostart-generator/xdg-autostart-service.c b/src/xdg-autostart-generator/xdg-autostart-service.c
new file mode 100644 (file)
index 0000000..4a30f9e
--- /dev/null
@@ -0,0 +1,645 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "xdg-autostart-service.h"
+
+#include "conf-parser.h"
+#include "escape.h"
+#include "unit-name.h"
+#include "path-util.h"
+#include "fd-util.h"
+#include "generator.h"
+#include "log.h"
+#include "specifier.h"
+#include "string-util.h"
+#include "nulstr-util.h"
+#include "strv.h"
+
+XdgAutostartService* xdg_autostart_service_free(XdgAutostartService *s) {
+        if (!s)
+                return NULL;
+
+        free(s->name);
+        free(s->path);
+        free(s->description);
+
+        free(s->type);
+        free(s->exec_string);
+
+        strv_free(s->only_show_in);
+        strv_free(s->not_show_in);
+
+        free(s->try_exec);
+        free(s->autostart_condition);
+        free(s->kde_autostart_condition);
+
+        free(s->gnome_autostart_phase);
+
+        return mfree(s);
+}
+
+char *xdg_autostart_service_translate_name(const char *name) {
+        _cleanup_free_ char *c = NULL, *escaped = NULL;
+        char *res;
+
+        c = strdup(name);
+        if (!c)
+                return NULL;
+
+        res = endswith(c, ".desktop");
+        if (res)
+                *res = '\0';
+
+        escaped = unit_name_escape(c);
+        if (!escaped)
+                return NULL;
+
+        return strjoin("app-", escaped, "-autostart.service");
+}
+
+static int xdg_config_parse_bool(
+                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) {
+
+        bool *b = data;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if (streq(rvalue, "true"))
+                *b = true;
+        else if (streq(rvalue, "false"))
+                *b = false;
+        else
+                return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), "Invalid value for boolean: %s", rvalue);
+
+        return 0;
+}
+
+/* Unescapes the string in-place, returns non-zero status on error. */
+static int xdg_unescape_string(
+                const char *unit,
+                const char *filename,
+                int line,
+                char *str) {
+
+        char *in;
+        char *out;
+
+        assert(str);
+
+        in = out = str;
+
+        for (; *in; in++, out++) {
+                if (*in == '\\') {
+                        /* Move forward, and ensure it is a valid escape. */
+                        in++;
+
+                        switch (*in) {
+                                case 's':
+                                        *out = ' ';
+                                        break;
+                                case 'n':
+                                        *out = '\n';
+                                        break;
+                                case 't':
+                                        *out = '\t';
+                                        break;
+                                case 'r':
+                                        *out = '\r';
+                                        break;
+                                case '\\':
+                                        *out = '\\';
+                                        break;
+                                case ';':
+                                        /* Technically only permitted for strv. */
+                                        *out = ';';
+                                        break;
+                                default:
+                                        return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), "Undefined escape sequence \\%c.", *in);
+                        }
+
+                        continue;
+                }
+
+                *out = *in;
+        }
+        *out = '\0';
+
+        return 0;
+}
+
+/* Note: We do not bother with unescaping the strings, hence the _raw postfix. */
+static int xdg_config_parse_string(
+                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_ char *res = NULL;
+        char **out = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        /* XDG does not allow duplicate definitions. */
+        if (*out) {
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Key %s was defined multiple times, ignoring.", lvalue);
+                return 0;
+        }
+
+        res = strdup(rvalue);
+        if (!res)
+                return log_oom();
+
+        r = xdg_unescape_string(unit, filename, line, res);
+        if (r < 0)
+                return r;
+
+        *out = TAKE_PTR(res);
+        return 0;
+}
+
+static int strv_strndup_unescape_and_push(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                char ***sv,
+                size_t *n_allocated,
+                size_t *n,
+                const char *start,
+                const char *end) {
+
+        if (end == start)
+                return 0;
+
+        _cleanup_free_ char *copy = NULL;
+        int r;
+
+        copy = strndup(start, end - start);
+        if (!copy)
+                return log_oom();
+
+        r = xdg_unescape_string(unit, filename, line, copy);
+        if (r < 0)
+                return r;
+
+        if (!greedy_realloc((void**) sv, n_allocated, *n + 2, sizeof(char*))) /* One extra for NULL */
+                return log_oom();
+
+        (*sv)[*n] = TAKE_PTR(copy);
+        (*sv)[*n + 1] = NULL;
+        (*n)++;
+
+        return 0;
+}
+
+static int xdg_config_parse_strv(
+                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) {
+
+        char ***ret_sv = data;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        /* XDG does not allow duplicate definitions. */
+        if (*ret_sv) {
+                log_syntax(unit, LOG_ERR, filename, line, 0, "Key %s was already defined, ignoring.", lvalue);
+                return 0;
+        }
+
+        size_t n = 0, n_allocated = 0;
+        _cleanup_strv_free_ char **sv = NULL;
+
+        if (!GREEDY_REALLOC0(sv, n_allocated, 1))
+                return log_oom();
+
+        /* We cannot use strv_split because it does not handle escaping correctly. */
+        const char *start = rvalue, *end;
+
+        for (end = start; *end; end++) {
+                if (*end == '\\') {
+                        /* Move forward, and ensure it is a valid escape. */
+                        end++;
+                        if (!strchr("sntr\\;", *end)) {
+                                log_syntax(unit, LOG_ERR, filename, line, 0, "Undefined escape sequence \\%c.", *end);
+                                return 0;
+                        }
+                        continue;
+                }
+
+                if (*end == ';') {
+                        r = strv_strndup_unescape_and_push(unit, filename, line,
+                                                           &sv, &n_allocated, &n,
+                                                           start, end);
+                        if (r < 0)
+                                return r;
+
+                        start = end + 1;
+                }
+        }
+
+        /* Handle the trailing entry after the last separator */
+        r = strv_strndup_unescape_and_push(unit, filename, line,
+                                           &sv, &n_allocated, &n,
+                                           start, end);
+        if (r < 0)
+                return r;
+
+        *ret_sv = TAKE_PTR(sv);
+        return 0;
+}
+
+static int xdg_config_item_table_lookup(
+                const void *table,
+                const char *section,
+                const char *lvalue,
+                ConfigParserCallback *func,
+                int *ltype,
+                void **data,
+                void *userdata) {
+
+        assert(lvalue);
+
+        /* Ignore any keys with [] as those are translations. */
+        if (strchr(lvalue, '[')) {
+                *func = NULL;
+                *ltype = 0;
+                *data = NULL;
+                return 1;
+        }
+
+        return config_item_table_lookup(table, section, lvalue, func, ltype, data, userdata);
+}
+
+XdgAutostartService *xdg_autostart_service_parse_desktop(const char *path) {
+        _cleanup_(xdg_autostart_service_freep) XdgAutostartService *service = NULL;
+        int r;
+
+        service = new0(XdgAutostartService, 1);
+        if (!service)
+                return NULL;
+
+        service->path = strdup(path);
+        if (!service->path)
+                return NULL;
+
+        const ConfigTableItem items[] = {
+                { "Desktop Entry", "Name",                      xdg_config_parse_string, 0, &service->description},
+                { "Desktop Entry", "Exec",                      xdg_config_parse_string, 0, &service->exec_string},
+                { "Desktop Entry", "TryExec",                   xdg_config_parse_string, 0, &service->try_exec},
+                { "Desktop Entry", "Type",                      xdg_config_parse_string, 0, &service->type},
+                { "Desktop Entry", "OnlyShowIn",                xdg_config_parse_strv, 0,   &service->only_show_in},
+                { "Desktop Entry", "NotShowIn",                 xdg_config_parse_strv, 0,   &service->not_show_in},
+                { "Desktop Entry", "Hidden",                    xdg_config_parse_bool, 0,   &service->hidden},
+                { "Desktop Entry", "AutostartCondition",        xdg_config_parse_string, 0, &service->autostart_condition},
+                { "Desktop Entry", "X-KDE-autostart-condition", xdg_config_parse_string, 0, &service->kde_autostart_condition},
+                { "Desktop Entry", "X-GNOME-Autostart-Phase",   xdg_config_parse_string, 0, &service->gnome_autostart_phase},
+                { "Desktop Entry", "X-systemd-skip",            xdg_config_parse_bool, 0,   &service->systemd_skip},
+
+                /* Common entries that we do not use currently. */
+                { "Desktop Entry", "Categories", NULL, 0, NULL},
+                { "Desktop Entry", "Comment", NULL, 0, NULL},
+                { "Desktop Entry", "Encoding", NULL, 0, NULL},
+                { "Desktop Entry", "GenericName", NULL, 0, NULL},
+                { "Desktop Entry", "Icon", NULL, 0, NULL},
+                { "Desktop Entry", "Keywords", NULL, 0, NULL},
+                { "Desktop Entry", "NoDisplay", NULL, 0, NULL},
+                { "Desktop Entry", "StartupNotify", NULL, 0, NULL},
+                { "Desktop Entry", "Terminal", NULL, 0, NULL},
+                { "Desktop Entry", "Version", NULL, 0, NULL},
+                {}
+        };
+
+        r = config_parse(NULL, service->path, NULL,
+                         "Desktop Entry\0",
+                         xdg_config_item_table_lookup, items,
+                         CONFIG_PARSE_WARN, service,
+                         NULL);
+        /* If parsing failed, only hide the file so it will still mask others. */
+        if (r < 0) {
+                log_warning_errno(r, "Failed to parse %s, ignoring it", service->path);
+                service->hidden = true;
+        }
+
+        return TAKE_PTR(service);
+}
+
+int xdg_autostart_format_exec_start(
+                const char *exec,
+                char **ret_exec_start) {
+
+        _cleanup_strv_free_ char **exec_split = NULL;
+        char *res;
+        size_t n, i;
+        bool first_arg;
+        int r;
+
+        /*
+         * Unfortunately, there is a mismatch between systemd's idea of $PATH
+         * and XDGs. i.e. we need to ensure that we have an absolute path to
+         * support cases where $PATH has been modified from the default set.
+         *
+         * Note that this is only needed for development environments though;
+         * so while it is important, this should have no effect in production
+         * environments.
+         *
+         * To be compliant with the XDG specification, we also need to strip
+         * certain parameters and such. Doing so properly makes parsing the
+         * command line unavoidable.
+         *
+         * NOTE: Technically, XDG only specifies " as quotes, while this also
+         *       accepts '.
+         */
+        exec_split = strv_split_full(exec, WHITESPACE, SPLIT_QUOTES | SPLIT_RELAX);
+        if (!exec_split)
+                return -ENOMEM;
+
+        if (strv_isempty(exec_split))
+                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Exec line is empty");
+
+        first_arg = true;
+        for (i = n = 0; exec_split[i]; i++) {
+                _cleanup_free_ char *c = NULL, *raw = NULL, *p = NULL, *escaped = NULL, *quoted = NULL;
+
+                r = cunescape(exec_split[i], 0, &c);
+                if (r < 0)
+                        return log_debug_errno(r, "Failed to unescape '%s': %m", exec_split[i]);
+
+                if (first_arg) {
+                        _cleanup_free_ char *executable = NULL;
+
+                        /* This is the executable, find it in $PATH */
+                        first_arg = false;
+                        r = find_binary(c, &executable);
+                        if (r < 0)
+                                return log_info_errno(r, "Exec binary '%s' does not exist: %m", c);
+
+                        escaped = cescape(executable);
+                        if (!escaped)
+                                return log_oom();
+
+                        free(exec_split[n]);
+                        exec_split[n++] = TAKE_PTR(escaped);
+                        continue;
+                }
+
+                /*
+                 * Remove any standardised XDG fields; we assume they never appear as
+                 * part of another argument as that just does not make any sense as
+                 * they can be empty (GLib will e.g. turn "%f" into an empty argument).
+                 * Other implementations may handle this differently.
+                 */
+                if (STR_IN_SET(c,
+                               "%f", "%F",
+                               "%u", "%U",
+                               "%d", "%D",
+                               "%n", "%N",
+                               "%i", /* Location of icon, could be implemented. */
+                               "%c", /* Translated application name, could be implemented. */
+                               "%k", /* Location of desktop file, could be implemented. */
+                               "%v",
+                               "%m"
+                               ))
+                        continue;
+
+                /*
+                 * %% -> % and then % -> %% means that we correctly quote any %
+                 * and also quote any left over (and invalid) % specifier from
+                 * the desktop file.
+                 */
+                raw = strreplace(c, "%%", "%");
+                if (!raw)
+                        return log_oom();
+                p = strreplace(raw, "%", "%%");
+                if (!p)
+                        return log_oom();
+                escaped = cescape(p);
+                if (!escaped)
+                        return log_oom();
+
+                quoted = strjoin("\"", escaped, "\"");
+                if (!quoted)
+                        return log_oom();
+
+                free(exec_split[n]);
+                exec_split[n++] = TAKE_PTR(quoted);
+        }
+        for (; exec_split[n]; n++)
+                exec_split[n] = mfree(exec_split[n]);
+
+        res = strv_join(exec_split, " ");
+        if (!res)
+                return log_oom();
+
+        *ret_exec_start = res;
+        return 0;
+}
+
+static int xdg_autostart_generate_desktop_condition(
+                FILE *f,
+                const char *test_binary,
+                const char *condition) {
+
+        int r;
+
+        /* Generate an ExecCondition for GNOME autostart condition */
+        if (!isempty(condition)) {
+                _cleanup_free_ char *gnome_autostart_condition_path = NULL, *e_autostart_condition = NULL;
+
+                r = find_binary(test_binary, &gnome_autostart_condition_path);
+                if (r < 0) {
+                        log_full_errno(r == -ENOENT ? LOG_INFO : LOG_WARNING, r,
+                                       "%s not found: %m", test_binary);
+                        fprintf(f, "# ExecCondition using %s skipped due to missing binary.\n", test_binary);
+                        return r;
+                }
+
+                e_autostart_condition = cescape(condition);
+                if (!e_autostart_condition)
+                        return log_oom();
+
+                fprintf(f,
+                         "ExecCondition=%s --condition \"%s\"\n",
+                         gnome_autostart_condition_path,
+                         e_autostart_condition);
+        }
+
+        return 0;
+}
+
+int xdg_autostart_service_generate_unit(
+                XdgAutostartService *service,
+                const char *dest) {
+
+        _cleanup_free_ char *path_escaped = NULL, *exec_start = NULL, *unit = NULL;
+        _cleanup_fclose_ FILE *f = NULL;
+        int r;
+
+        assert(service);
+
+        /* Nothing to do for hidden services. */
+        if (service->hidden) {
+                log_info("Not generating service for XDG autostart %s, it is hidden.", service->name);
+                return 0;
+        }
+
+        if (service->systemd_skip) {
+                log_info("Not generating service for XDG autostart %s, should be skipped by generator.", service->name);
+                return 0;
+        }
+
+        /* Nothing to do if type is not Application. */
+        if (!streq_ptr(service->type, "Application")) {
+                log_info("Not generating service for XDG autostart %s, only Type=Application is supported.", service->name);
+                return 0;
+        }
+
+        if (!service->exec_string) {
+                log_warning("Not generating service for XDG autostart %s, it is has no Exec= line.", service->name);
+                return 0;
+        }
+
+        /*
+         * The TryExec key cannot be checked properly from the systemd unit,
+         * it is trivial to check using find_binary though.
+         */
+        if (service->try_exec) {
+                r = find_binary(service->try_exec, NULL);
+                if (r < 0) {
+                        log_full_errno(r == -ENOENT ? LOG_INFO : LOG_WARNING, r,
+                                       "Not generating service for XDG autostart %s, could not find TryExec= binary %s: %m",
+                                       service->name, service->try_exec);
+                        return 0;
+                }
+        }
+
+        r = xdg_autostart_format_exec_start(service->exec_string, &exec_start);
+        if (r < 0) {
+                log_warning_errno(r,
+                                  "Not generating service for XDG autostart %s, error parsing Exec= line: %m",
+                                  service->name);
+                return 0;
+        }
+
+        if (service->gnome_autostart_phase) {
+                /* There is no explicit value for the "Application" phase. */
+                log_info("Not generating service for XDG autostart %s, startup phases are not supported.",
+                         service->name);
+                return 0;
+        }
+
+        path_escaped = specifier_escape(service->path);
+        if (!path_escaped)
+                return log_oom();
+
+        unit = path_join(dest, service->name);
+        if (!unit)
+                return log_oom();
+
+        f = fopen(unit, "wxe");
+        if (!f)
+                return log_error_errno(errno, "Failed to create unit file %s: %m", unit);
+
+        fprintf(f,
+                "# Automatically generated by systemd-xdg-autostart-generator\n\n"
+                "[Unit]\n"
+                "Documentation=man:systemd-xdg-autostart-generator(8)\n"
+                "SourcePath=%s\n"
+                "PartOf=graphical-session.target\n\n",
+                path_escaped);
+
+        if (service->description) {
+                _cleanup_free_ char *t = NULL;
+
+                t = specifier_escape(service->description);
+                if (!t)
+                        return log_oom();
+
+                fprintf(f, "Description=%s\n", t);
+        }
+
+        /* Only start after the session is ready. */
+        fprintf(f,
+                "After=graphical-session.target\n");
+
+        fprintf(f,
+                "\n[Service]\n"
+                "Type=simple\n"
+                "ExecStart=:%s\n"
+                "Restart=no\n"
+                "TimeoutSec=5s\n"
+                "Slice=app.slice\n",
+                exec_start);
+
+        /* Generate an ExecCondition to check $XDG_CURRENT_DESKTOP */
+        if (!strv_isempty(service->only_show_in) || !strv_isempty(service->not_show_in)) {
+                _cleanup_free_ char *only_show_in = NULL, *not_show_in = NULL, *e_only_show_in = NULL, *e_not_show_in = NULL;
+
+                only_show_in = strv_join(service->only_show_in, ":");
+                not_show_in = strv_join(service->not_show_in, ":");
+                if (!only_show_in || !not_show_in)
+                        return log_oom();
+
+                e_only_show_in = cescape(only_show_in);
+                e_not_show_in = cescape(not_show_in);
+                if (!e_only_show_in || !e_not_show_in)
+                        return log_oom();
+
+                /* Just assume the values are reasonably sane */
+                fprintf(f,
+                        "ExecCondition=" ROOTLIBEXECDIR "/systemd-xdg-autostart-condition \"%s\" \"%s\"\n",
+                        e_only_show_in,
+                        e_not_show_in);
+        }
+
+        r = xdg_autostart_generate_desktop_condition(f,
+                                                     "gnome-systemd-autostart-condition",
+                                                     service->autostart_condition);
+        if (r < 0)
+                return r;
+
+        r = xdg_autostart_generate_desktop_condition(f,
+                                                     "kde-systemd-start-condition",
+                                                     service->kde_autostart_condition);
+        if (r < 0)
+                return r;
+
+        (void) generator_add_symlink(dest, "xdg-desktop-autostart.target", "wants", service->name);
+
+        return 0;
+}
diff --git a/src/xdg-autostart-generator/xdg-autostart-service.h b/src/xdg-autostart-generator/xdg-autostart-service.h
new file mode 100644 (file)
index 0000000..685f978
--- /dev/null
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "macro.h"
+
+typedef struct XdgAutostartService {
+        char *name;
+        char *path;
+        char *description; /* Name in XDG desktop file */
+
+        char *type; /* Purely as an assertion check */
+        char *exec_string;
+
+        char **only_show_in;
+        char **not_show_in;
+
+        char *try_exec;
+        char *autostart_condition; /* This is mostly GNOME specific */
+        char *kde_autostart_condition;
+
+        char *gnome_autostart_phase;
+
+        bool hidden;
+        bool systemd_skip;
+
+} XdgAutostartService;
+
+
+XdgAutostartService * xdg_autostart_service_free(XdgAutostartService *s);
+DEFINE_TRIVIAL_CLEANUP_FUNC(XdgAutostartService*, xdg_autostart_service_free);
+
+char *xdg_autostart_service_translate_name(const char *name);
+int xdg_autostart_format_exec_start(const char *exec, char **ret_exec_start);
+
+XdgAutostartService *xdg_autostart_service_parse_desktop(const char *path);
+int xdg_autostart_service_generate_unit(XdgAutostartService *service, const char *dest);
index 47bf84769334fa093a985ea98115cd6b7a75dadf..da76fd71d6a2e3da0602bcc4fafc6e4f8251fc5f 100644 (file)
@@ -5,8 +5,23 @@
 #  the Free Software Foundation; either version 2.1 of the License, or
 #  (at your option) any later version.
 
-# See sysctl.d(5) for the description of the files in this directory,
-# and systemd-coredump(8) and core(5) for the explanation of the
-# setting below.
+# See sysctl.d(5) for the description of the files in this directory.
 
+# Pipe the core file to systemd-coredump. The systemd-coredump process spawned
+# by the kernel will start a second copy of itself as the
+# systemd-coredump@.service, which will do the actual processing and storing of
+# the core dump.
+#
+# See systemd-coredump(8) and core(5).
 kernel.core_pattern=|@rootlibexecdir@/systemd-coredump %P %u %g %s %t %c %h
+
+# Also dump processes executing a set-user-ID/set-group-ID program that is
+# owned by a user/group other than the real user/group ID of the process, or
+# a program that has file capabilities. ("2" is called "suidsafe" in core(5)).
+#
+# systemd-coredump will store the core file owned by the effective uid and gid
+# of the running process (and not the filesystem-user-ID which the kernel uses
+# when saving a core dump).
+#
+# See proc(5), setuid(2), capabilities(7).
+fs.suid_dumpable=2
index 471771acd464b4ff748b175e070bd571e47aa436..7204fdb00b1a4d58018284b609b49a240c0324e4 100644 (file)
@@ -10,10 +10,10 @@ ninja: no work to do.
 --x-- Running TEST-01-BASIC --x--
 + make -C TEST-01-BASIC BUILD_DIR=/home/zbyszek/src/systemd/build clean setup run
 make: Entering directory '/home/zbyszek/src/systemd/test/TEST-01-BASIC'
-TEST CLEANUP: Basic systemd setup
-TEST SETUP: Basic systemd setup
+TEST-01-BASIC CLEANUP: Basic systemd setup
+TEST-01-BASIC SETUP: Basic systemd setup
 ...
-TEST RUN: Basic systemd setup [OK]
+TEST-01-BASIC RUN: Basic systemd setup [OK]
 make: Leaving directory '/home/zbyszek/src/systemd/test/TEST-01-BASIC'
 --x-- Result of TEST-01-BASIC: 0 --x--
 --x-- Running TEST-02-CRYPTSETUP --x--
index 45e9bfc67cd50f3290bb1e9475a59c819f4d4549..79fe9688b816cf7658de39eea5783d17cef78442 100644 (file)
@@ -1,9 +1,6 @@
 BUILD_DIR=$(shell ../../tools/find-build-dir.sh)
 
-all setup run:
+all setup run clean clean-again:
        @basedir=../.. TEST_BASE_DIR=../ BUILD_DIR=$(BUILD_DIR) ./test.sh --$@
 
-clean clean-again:
-       @basedir=../.. TEST_BASE_DIR=../ BUILD_DIR=$(BUILD_DIR) ./test.sh --clean
-
 .PHONY: all setup run clean clean-again
index 0eaa8f991ad8bf18f0703814908ce1cb6cde9807..58f6cd141429a2d2c880c8b13953fd5ef3aa22f3 100755 (executable)
@@ -1,34 +1,25 @@
 #!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="Basic systemd setup"
+IMAGE_NAME="basic"
 RUN_IN_UNPRIVILEGED_CONTAINER=${RUN_IN_UNPRIVILEGED_CONTAINER:-yes}
+TEST_REQUIRE_INSTALL_TESTS=0
 
 . $TEST_BASE_DIR/test-functions
 
-test_setup() {
+test_create_image() {
     create_empty_image_rootdir
 
     # Create what will eventually be our root filesystem onto an overlay
     (
         LOG_LEVEL=5
-        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
-
         setup_basic_environment
 
-        # setup the testsuite service
-        cat >$initdir/etc/systemd/system/testsuite.service <<EOF
-[Unit]
-Description=Testsuite service
-After=multi-user.target
-
-[Service]
-ExecStart=/bin/sh -e -x -c 'systemctl --state=failed --no-legend --no-pager > /failed ; systemctl daemon-reload ; echo OK > /testok'
-Type=oneshot
-EOF
-
-        setup_testsuite
+        # install tests manually so the test is functional even when -Dinstall-tests=false
+        mkdir -p $initdir/usr/lib/systemd/tests/testdata/units/
+        cp -v $(dirname $0)/../units/{testsuite-01,end}.service $initdir/usr/lib/systemd/tests/testdata/units/
     )
     setup_nspawn_root
 }
 
-do_test "$@"
+do_test "$@" 01
index a859b345d08d10f8c5d453362d1c81192c9d5646..ef1811122e1346d0a632978cd14efa2bdeb2e439 100755 (executable)
@@ -1,30 +1,29 @@
 #!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="cryptsetup systemd setup"
+IMAGE_NAME="cryptsetup"
 TEST_NO_NSPAWN=1
 
 . $TEST_BASE_DIR/test-functions
 
 check_result_qemu() {
     ret=1
-    mkdir -p $initdir
-    mount ${LOOPDEV}p1 $initdir
+    mount_initdir
     [[ -e $initdir/testok ]] && ret=0
     [[ -f $initdir/failed ]] && cp -a $initdir/failed $TESTDIR
     cryptsetup luksOpen ${LOOPDEV}p2 varcrypt <$TESTDIR/keyfile
     mount /dev/mapper/varcrypt $initdir/var
-    cp -a $initdir/var/log/journal $TESTDIR
-    umount $initdir/var
-    umount $initdir
+    save_journal $initdir/var/log/journal
+    _umount_dir $initdir/var
+    _umount_dir $initdir
     cryptsetup luksClose /dev/mapper/varcrypt
     [[ -f $TESTDIR/failed ]] && cat $TESTDIR/failed
-    ls -l $TESTDIR/journal/*/*.journal
+    echo $JOURNAL_LIST
     test -s $TESTDIR/failed && ret=$(($ret+1))
     return $ret
 }
 
-
-test_setup() {
+test_create_image() {
     create_empty_image_rootdir
     echo -n test >$TESTDIR/keyfile
     cryptsetup -q luksFormat --pbkdf pbkdf2 --pbkdf-force-iterations 1000 ${LOOPDEV}p2 $TESTDIR/keyfile
@@ -42,30 +41,21 @@ test_setup() {
         setup_basic_environment
         mask_supporting_services
 
-        # setup the testsuite service
-        cat >$initdir/etc/systemd/system/testsuite.service <<EOF
-[Unit]
-Description=Testsuite service
-After=multi-user.target
-
-[Service]
-ExecStart=/bin/sh -x -c 'systemctl --state=failed --no-legend --no-pager > /failed ; echo OK > /testok'
-Type=oneshot
-EOF
-
-        setup_testsuite
-
         install_dmevent
         generate_module_dependencies
         cat >$initdir/etc/crypttab <<EOF
 $DM_NAME UUID=$ID_FS_UUID /etc/varkey
 EOF
-        echo -n test > $initdir/etc/varkey
+        echo -n test >$initdir/etc/varkey
         cat $initdir/etc/crypttab | ddebug
 
         cat >>$initdir/etc/fstab <<EOF
 /dev/mapper/varcrypt    /var    ext4    defaults 0 1
 EOF
+
+        # Forward journal messages to the console, so we have something
+        # to investigate even if we fail to mount the encrypted /var
+        echo ForwardToConsole=yes >> $initdir/etc/systemd/journald.conf
     )
 }
 
@@ -82,8 +72,8 @@ test_cleanup() {
 }
 
 test_setup_cleanup() {
-    cleanup_root_var
-    _test_setup_cleanup
+    cleanup_root_var || :
+    cleanup_initdir
 }
 
-do_test "$@"
+do_test "$@" 02
diff --git a/test/TEST-03-JOBS/test-jobs.sh b/test/TEST-03-JOBS/test-jobs.sh
deleted file mode 100755 (executable)
index 85efeeb..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-#!/usr/bin/env bash
-set -ex
-
-# Test merging of a --job-mode=ignore-dependencies job into a previously
-# installed job.
-
-systemctl start --no-block hello-after-sleep.target
-
-systemctl list-jobs > /root/list-jobs.txt
-while ! grep 'sleep\.service.*running' /root/list-jobs.txt; do
-    systemctl list-jobs > /root/list-jobs.txt
-done
-
-grep 'hello\.service.*waiting' /root/list-jobs.txt
-
-# This is supposed to finish quickly, not wait for sleep to finish.
-START_SEC=$(date -u '+%s')
-systemctl start --job-mode=ignore-dependencies hello
-END_SEC=$(date -u '+%s')
-ELAPSED=$(($END_SEC-$START_SEC))
-
-[ "$ELAPSED" -lt 3 ]
-
-# sleep should still be running, hello not.
-systemctl list-jobs > /root/list-jobs.txt
-grep 'sleep\.service.*running' /root/list-jobs.txt
-grep 'hello\.service' /root/list-jobs.txt && exit 1
-systemctl stop sleep.service hello-after-sleep.target
-
-# Some basic testing that --show-transaction does something useful
-! systemctl is-active systemd-importd
-systemctl -T start systemd-importd
-systemctl is-active systemd-importd
-systemctl --show-transaction stop systemd-importd
-! systemctl is-active systemd-importd
-
-# Test for a crash when enqueuing a JOB_NOP when other job already exists
-systemctl start --no-block hello-after-sleep.target
-# hello.service should still be waiting, so these try-restarts will collapse
-# into NOPs.
-systemctl try-restart --job-mode=fail hello.service
-systemctl try-restart hello.service
-systemctl stop hello.service sleep.service hello-after-sleep.target
-
-# TODO: add more job queueing/merging tests here.
-
-# Test for irreversible jobs
-systemctl start unstoppable.service
-
-# This is expected to fail with 'job cancelled'
-systemctl stop unstoppable.service && exit 1
-# But this should succeed
-systemctl stop --job-mode=replace-irreversibly unstoppable.service
-
-# We're going to shutdown soon. Let's see if it succeeds when
-# there's an active service that tries to be unstoppable.
-# Shutdown of the container/VM will hang if not.
-systemctl start unstoppable.service
-
-# Test waiting for a started unit(s) to terminate again
-cat <<EOF >  /run/systemd/system/wait2.service
-[Unit]
-Description=Wait for 2 seconds
-[Service]
-ExecStart=/bin/sh -ec 'sleep 2'
-EOF
-cat <<EOF >  /run/systemd/system/wait5fail.service
-[Unit]
-Description=Wait for 5 seconds and fail
-[Service]
-ExecStart=/bin/sh -ec 'sleep 5; false'
-EOF
-
-# wait2 succeeds
-START_SEC=$(date -u '+%s')
-systemctl start --wait wait2.service
-END_SEC=$(date -u '+%s')
-ELAPSED=$(($END_SEC-$START_SEC))
-[[ "$ELAPSED" -ge 2 ]] && [[ "$ELAPSED" -le 4 ]] || exit 1
-
-# wait5fail fails, so systemctl should fail
-START_SEC=$(date -u '+%s')
-! systemctl start --wait wait2.service wait5fail.service || exit 1
-END_SEC=$(date -u '+%s')
-ELAPSED=$(($END_SEC-$START_SEC))
-[[ "$ELAPSED" -ge 5 ]] && [[ "$ELAPSED" -le 7 ]] || exit 1
-
-# Test time-limited scopes
-START_SEC=$(date -u '+%s')
-set +e
-systemd-run --scope --property=RuntimeMaxSec=3s sleep 10
-RESULT=$?
-END_SEC=$(date -u '+%s')
-ELAPSED=$(($END_SEC-$START_SEC))
-[[ "$ELAPSED" -ge 3 ]] && [[ "$ELAPSED" -le 5 ]] || exit 1
-[[ "$RESULT" -ne 0 ]] || exit 1
-
-touch /testok
index 5299464b81d46805cde46ef0dd1b5a99f4cbc34f..221a18682a9f90820db8357ee837716c7b13bdbe 100755 (executable)
@@ -2,39 +2,8 @@
 set -e
 TEST_DESCRIPTION="Job-related tests"
 TEST_NO_QEMU=1
+IMAGE_NAME="default"
 
 . $TEST_BASE_DIR/test-functions
 
-test_setup() {
-    create_empty_image_rootdir
-
-    # Create what will eventually be our root filesystem onto an overlay
-    (
-        LOG_LEVEL=5
-        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
-
-        setup_basic_environment
-        mask_supporting_services
-
-        # setup the testsuite service
-        cat >$initdir/etc/systemd/system/testsuite.service <<EOF
-[Unit]
-Description=Testsuite service
-After=multi-user.target
-
-[Service]
-ExecStart=/test-jobs.sh
-Type=oneshot
-EOF
-
-        # copy the units used by this test
-        cp $TEST_BASE_DIR/{hello.service,sleep.service,hello-after-sleep.target,unstoppable.service} \
-            $initdir/etc/systemd/system
-        cp test-jobs.sh $initdir/
-
-        setup_testsuite
-    )
-    setup_nspawn_root
-}
-
-do_test "$@"
+do_test "$@" 03
diff --git a/test/TEST-04-JOURNAL/test-journal.sh b/test/TEST-04-JOURNAL/test-journal.sh
deleted file mode 100755 (executable)
index 1431dad..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-#!/usr/bin/env bash
-set -x
-set -e
-set -o pipefail
-
-# Test stdout stream
-
-# Skip empty lines
-ID=$(journalctl --new-id128 | sed -n 2p)
->/expected
-printf $'\n\n\n' | systemd-cat -t "$ID" --level-prefix false
-journalctl --sync
-journalctl -b -o cat -t "$ID" >/output
-cmp /expected /output
-
-ID=$(journalctl --new-id128 | sed -n 2p)
->/expected
-printf $'<5>\n<6>\n<7>\n' | systemd-cat -t "$ID" --level-prefix true
-journalctl --sync
-journalctl -b -o cat -t "$ID" >/output
-cmp /expected /output
-
-# Remove trailing spaces
-ID=$(journalctl --new-id128 | sed -n 2p)
-printf "Trailing spaces\n">/expected
-printf $'<5>Trailing spaces \t \n' | systemd-cat -t "$ID" --level-prefix true
-journalctl --sync
-journalctl -b -o cat -t "$ID" >/output
-cmp /expected /output
-
-ID=$(journalctl --new-id128 | sed -n 2p)
-printf "Trailing spaces\n">/expected
-printf $'Trailing spaces \t \n' | systemd-cat -t "$ID" --level-prefix false
-journalctl --sync
-journalctl -b -o cat -t "$ID" >/output
-cmp /expected /output
-
-# Don't remove leading spaces
-ID=$(journalctl --new-id128 | sed -n 2p)
-printf $' \t Leading spaces\n'>/expected
-printf $'<5> \t Leading spaces\n' | systemd-cat -t "$ID" --level-prefix true
-journalctl --sync
-journalctl -b -o cat -t "$ID" >/output
-cmp /expected /output
-
-ID=$(journalctl --new-id128 | sed -n 2p)
-printf $' \t Leading spaces\n'>/expected
-printf $' \t Leading spaces\n' | systemd-cat -t "$ID" --level-prefix false
-journalctl --sync
-journalctl -b -o cat -t "$ID" >/output
-cmp /expected /output
-
-# --output-fields restricts output
-ID=$(journalctl --new-id128 | sed -n 2p)
-printf $'foo' | systemd-cat -t "$ID" --level-prefix false
-journalctl --sync
-journalctl -b -o export --output-fields=MESSAGE,FOO --output-fields=PRIORITY,MESSAGE -t "$ID" >/output
-[[ `grep -c . /output` -eq 6 ]]
-grep -q '^__CURSOR=' /output
-grep -q '^MESSAGE=foo$' /output
-grep -q '^PRIORITY=6$' /output
-! grep -q '^FOO=' /output
-! grep -q '^SYSLOG_FACILITY=' /output
-
-# `-b all` negates earlier use of -b (-b and -m are otherwise exclusive)
-journalctl -b -1 -b all -m > /dev/null
-
-# -b always behaves like -b0
-journalctl -q -b-1 -b0 | head -1 > /expected
-journalctl -q -b-1 -b  | head -1 > /output
-cmp /expected /output
-# ... even when another option follows (both of these should fail due to -m)
-{ journalctl -ball -b0 -m 2>&1 || :; } | head -1 > /expected
-{ journalctl -ball -b  -m 2>&1 || :; } | head -1 > /output
-cmp /expected /output
-
-# https://github.com/systemd/systemd/issues/13708
-ID=$(systemd-id128 new)
-systemd-cat -t "$ID" bash -c 'echo parent; (echo child) & wait' &
-PID=$!
-wait %%
-journalctl --sync
-# We can drop this grep when https://github.com/systemd/systemd/issues/13937
-# has a fix.
-journalctl -b -o export -t "$ID" --output-fields=_PID | grep '^_PID=' >/output
-[[ `grep -c . /output` -eq 2 ]]
-grep -q "^_PID=$PID" /output
-grep -vq "^_PID=$PID" /output
-
-# Add new tests before here, the journald restarts below
-# may make tests flappy.
-
-# Don't lose streams on restart
-systemctl start forever-print-hola
-sleep 3
-systemctl restart systemd-journald
-sleep 3
-systemctl stop forever-print-hola
-[[ ! -f "/i-lose-my-logs" ]]
-
-# https://github.com/systemd/systemd/issues/4408
-rm -f /i-lose-my-logs
-systemctl start forever-print-hola
-sleep 3
-systemctl kill --signal=SIGKILL systemd-journald
-sleep 3
-[[ ! -f "/i-lose-my-logs" ]]
-
-touch /testok
index af96dfd7191a7563b319404ec974bdb12b59175d..f16543c2b44b7ebd7076357af9f2d6f8c7d674d9 100755 (executable)
@@ -4,41 +4,4 @@ TEST_DESCRIPTION="Journal-related tests"
 
 . $TEST_BASE_DIR/test-functions
 
-test_setup() {
-    create_empty_image_rootdir
-
-    # Create what will eventually be our root filesystem onto an overlay
-    (
-        LOG_LEVEL=5
-        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
-
-        setup_basic_environment
-        mask_supporting_services
-
-        # setup the testsuite service
-        cat >$initdir/etc/systemd/system/testsuite.service <<EOF
-[Unit]
-Description=Testsuite service
-
-[Service]
-ExecStart=/test-journal.sh
-Type=oneshot
-EOF
-
-        cat >$initdir/etc/systemd/system/forever-print-hola.service <<EOF
-[Unit]
-Description=ForeverPrintHola service
-
-[Service]
-Type=simple
-ExecStart=/bin/sh -x -c 'while :; do printf "Hola\n" || touch /i-lose-my-logs; sleep 1; done'
-EOF
-
-        cp test-journal.sh $initdir/
-
-        setup_testsuite
-    )
-    setup_nspawn_root
-}
-
-do_test "$@"
+do_test "$@" 04
diff --git a/test/TEST-05-RLIMITS/test-rlimits.sh b/test/TEST-05-RLIMITS/test-rlimits.sh
deleted file mode 100755 (executable)
index 86b5760..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/usr/bin/env bash
-set -x
-set -e
-set -o pipefail
-
-[[ "$(systemctl show -p DefaultLimitNOFILESoft)" = "DefaultLimitNOFILESoft=10000" ]]
-[[ "$(systemctl show -p DefaultLimitNOFILE)" = "DefaultLimitNOFILE=16384" ]]
-
-[[ "$(systemctl show -p LimitNOFILESoft testsuite.service)" = "LimitNOFILESoft=10000" ]]
-[[ "$(systemctl show -p LimitNOFILE testsuite.service)" = "LimitNOFILE=16384" ]]
-
-[[ "$(ulimit -n -S)" = "10000" ]]
-[[ "$(ulimit -n -H)" = "16384" ]]
-
-touch /testok
index bda37ef212c16e4da458fd88072754341a532aa8..463fe42a7cf200f78ae5c6d8dc8c540128b682c1 100755 (executable)
@@ -4,37 +4,4 @@ TEST_DESCRIPTION="Resource limits-related tests"
 
 . $TEST_BASE_DIR/test-functions
 
-test_setup() {
-    create_empty_image_rootdir
-
-    # Create what will eventually be our root filesystem onto an overlay
-    (
-        LOG_LEVEL=5
-        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
-
-        setup_basic_environment
-        mask_supporting_services
-
-        cat >$initdir/etc/systemd/system.conf <<EOF
-[Manager]
-DefaultLimitNOFILE=10000:16384
-EOF
-
-        # setup the testsuite service
-        cat >$initdir/etc/systemd/system/testsuite.service <<EOF
-[Unit]
-Description=Testsuite service
-
-[Service]
-ExecStart=/test-rlimits.sh
-Type=oneshot
-EOF
-
-        cp test-rlimits.sh $initdir/
-
-        setup_testsuite
-    )
-    setup_nspawn_root
-}
-
-do_test "$@"
+do_test "$@" 05
diff --git a/test/TEST-06-SELINUX/test-selinux-checks.sh b/test/TEST-06-SELINUX/test-selinux-checks.sh
deleted file mode 100755 (executable)
index 9e722e3..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/usr/bin/env bash
-set -x
-set -e
-set -o pipefail
-
-echo 1 >/sys/fs/selinux/enforce
-runcon -t systemd_test_start_t systemctl start hola
-runcon -t systemd_test_reload_t systemctl reload hola
-runcon -t systemd_test_stop_t systemctl stop hola
-
-touch /testok
index 46dc1cd805e36a430bd25554c33124d7664aad3b..0acd7a10e8004df6e0a27543ea0871fcfd929d20 100755 (executable)
@@ -1,6 +1,7 @@
 #!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="SELinux tests"
+IMAGE_NAME="selinux"
 TEST_NO_NSPAWN=1
 
 # Requirements:
@@ -15,60 +16,16 @@ test -f /usr/share/selinux/devel/include/system/systemd.if || exit 0
 SETUP_SELINUX=yes
 KERNEL_APPEND="$KERNEL_APPEND selinux=1 security=selinux"
 
-test_setup() {
+test_create_image() {
     create_empty_image_rootdir
 
     # Create what will eventually be our root filesystem onto an overlay
     (
         LOG_LEVEL=5
-        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
 
         setup_basic_environment
         mask_supporting_services
 
-        # setup the testsuite service
-        cat <<EOF >$initdir/etc/systemd/system/testsuite.service
-[Unit]
-Description=Testsuite service
-
-[Service]
-ExecStart=/test-selinux-checks.sh
-Type=oneshot
-EOF
-
-        cat <<EOF >$initdir/etc/systemd/system/hola.service
-[Service]
-Type=oneshot
-ExecStart=/bin/echo Start Hola
-ExecReload=/bin/echo Reload Hola
-ExecStop=/bin/echo Stop Hola
-RemainAfterExit=yes
-EOF
-
-        setup_testsuite
-
-        cat <<EOF >$initdir/etc/systemd/system/load-systemd-test-module.service
-[Unit]
-Description=Load systemd-test module
-DefaultDependencies=no
-Requires=local-fs.target
-Conflicts=shutdown.target
-After=local-fs.target
-Before=sysinit.target shutdown.target autorelabel.service
-ConditionSecurity=selinux
-ConditionPathExists=|/.load-systemd-test-module
-
-[Service]
-ExecStart=/bin/sh -x -c 'echo 0 >/sys/fs/selinux/enforce && cd /systemd-test-module && make -f /usr/share/selinux/devel/Makefile load  && rm /.load-systemd-test-module'
-Type=oneshot
-TimeoutSec=0
-RemainAfterExit=yes
-EOF
-
-        touch $initdir/.load-systemd-test-module
-        mkdir -p $initdir/etc/systemd/system/basic.target.wants
-        ln -fs load-systemd-test-module.service $initdir/etc/systemd/system/basic.target.wants/load-systemd-test-module.service
-
         local _modules_dir=/var/lib/selinux
         rm -rf $initdir/$_modules_dir
         if ! cp -ar $_modules_dir $initdir/$_modules_dir; then
@@ -87,11 +44,12 @@ EOF
         mkdir $initdir/systemd-test-module
         cp systemd_test.te $initdir/systemd-test-module
         cp systemd_test.if $initdir/systemd-test-module
-        cp test-selinux-checks.sh $initdir
         dracut_install -o sesearch
         dracut_install runcon
-        dracut_install checkmodule semodule semodule_package m4 make /usr/libexec/selinux/hll/pp load_policy sefcontext_compile
+        dracut_install checkmodule semodule semodule_package m4 make load_policy sefcontext_compile
+        dracut_install -o /usr/libexec/selinux/hll/pp # Fedora/RHEL/...
+        dracut_install -o /usr/lib/selinux/hll/pp     # Debian/Ubuntu/...
     )
 }
 
-do_test "$@"
+do_test "$@" 06
diff --git a/test/TEST-07-ISSUE-1981/test-segfault.sh b/test/TEST-07-ISSUE-1981/test-segfault.sh
deleted file mode 100755 (executable)
index fbb2d1d..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/usr/bin/env bash
-set -x
-set -e
-
->/failed
-
-cat <<'EOL' >/lib/systemd/system/my.service
-[Service]
-Type=oneshot
-ExecStart=/bin/echo Timer runs me
-EOL
-
-cat <<'EOL' >/lib/systemd/system/my.timer
-[Timer]
-OnBootSec=10s
-OnUnitInactiveSec=1h
-EOL
-
-systemctl unmask my.timer
-
-systemctl start my.timer
-
-mkdir -p /etc/systemd/system/my.timer.d/
-cat <<'EOL' >/etc/systemd/system/my.timer.d/override.conf
-[Timer]
-OnBootSec=10s
-OnUnitInactiveSec=1h
-EOL
-
-systemctl daemon-reload
-
-systemctl mask my.timer
-
-touch /testok
-rm /failed
index 7927294a8ab81c3fe2b238df94137a9f2ab67a2a..5da24a987ce5d5933e555dd99164983a4c492ac1 100755 (executable)
@@ -7,32 +7,4 @@ TEST_NO_QEMU=1
 
 NSPAWN_TIMEOUT=30
 
-test_setup() {
-    create_empty_image_rootdir
-
-    # Create what will eventually be our root filesystem onto an overlay
-    (
-        LOG_LEVEL=5
-        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
-
-        setup_basic_environment
-        mask_supporting_services
-
-        # setup the testsuite service
-        cat >$initdir/etc/systemd/system/testsuite.service <<EOF
-[Unit]
-Description=Testsuite service
-
-[Service]
-ExecStart=/test-segfault.sh
-Type=oneshot
-EOF
-
-        cp test-segfault.sh $initdir/
-
-        setup_testsuite
-    )
-    setup_nspawn_root
-}
-
-do_test "$@"
+do_test "$@" 07
index 2fedef7b58a83f646f93c786f0c19b371bff39a5..e5dedf2f0c2765f3289a7c905bec7b28bd4c9297 100755 (executable)
@@ -1,70 +1,22 @@
 #!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/2730"
+IMAGE_NAME="test08"
 TEST_NO_NSPAWN=1
 
 . $TEST_BASE_DIR/test-functions
 QEMU_TIMEOUT=300
 FSTYPE=ext4
 
-test_setup() {
+test_create_image() {
     create_empty_image_rootdir
 
     # Create what will eventually be our root filesystem onto an overlay
     (
         LOG_LEVEL=5
-        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
-
         setup_basic_environment
-
-        # setup the testsuite service
-        cat >$initdir/etc/systemd/system/testsuite.service <<EOF
-[Unit]
-Description=Testsuite service
-
-[Service]
-ExecStart=/bin/sh -x -c 'mount -o remount,rw /dev/sda1 && echo OK > /testok; systemctl poweroff'
-Type=oneshot
-EOF
-
-    rm $initdir/etc/fstab
-    cat >$initdir/etc/systemd/system/-.mount <<EOF
-[Unit]
-Before=local-fs.target
-
-[Mount]
-What=/dev/sda1
-Where=/
-Type=ext4
-Options=errors=remount-ro,noatime
-
-[Install]
-WantedBy=local-fs.target
-Alias=root.mount
-EOF
-
-    cat >$initdir/etc/systemd/system/systemd-remount-fs.service <<EOF
-[Unit]
-DefaultDependencies=no
-Conflicts=shutdown.target
-After=systemd-fsck-root.service
-Before=local-fs-pre.target local-fs.target shutdown.target
-Wants=local-fs-pre.target
-
-[Service]
-Type=oneshot
-RemainAfterExit=yes
-ExecStart=/bin/systemctl reload /
-EOF
-
-        setup_testsuite
     )
-
-    ln -s /etc/systemd/system/-.mount $initdir/etc/systemd/system/root.mount
-    mkdir -p $initdir/etc/systemd/system/local-fs.target.wants
-    ln -s /etc/systemd/system/-.mount $initdir/etc/systemd/system/local-fs.target.wants/-.mount
-
     mask_supporting_services
 }
 
-do_test "$@"
+do_test "$@" 08
index efe75d140b683dc71512ed08a4023ca9a094e01e..a4d155be1db3443b5de823671ba57d017eb9e069 100755 (executable)
@@ -6,32 +6,4 @@ TEST_NO_NSPAWN=1
 . $TEST_BASE_DIR/test-functions
 QEMU_TIMEOUT=300
 
-test_setup() {
-    create_empty_image_rootdir
-
-    # Create what will eventually be our root filesystem onto an overlay
-    (
-        LOG_LEVEL=5
-        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
-
-        setup_basic_environment
-        mask_supporting_services
-
-        # setup the testsuite service
-        cat >$initdir/etc/systemd/system/testsuite.service <<'EOF'
-[Unit]
-Description=Testsuite service
-
-[Service]
-Type=oneshot
-ExecStart=/bin/sh -c '>/testok'
-RemainAfterExit=yes
-ExecStop=/bin/sh -c 'kill -SEGV $$$$'
-TimeoutStopSec=270s
-EOF
-
-        setup_testsuite
-    )
-}
-
-do_test "$@"
+do_test "$@" 09
index 1761ad1e4304228f444903df3a50220fa034a506..14ded56ba15c938a4bfb84bb1d7396062b5dadb3 100755 (executable)
@@ -4,45 +4,4 @@ TEST_DESCRIPTION="https://github.com/systemd/systemd/issues/2467"
 
 . $TEST_BASE_DIR/test-functions
 
-test_setup() {
-    create_empty_image_rootdir
-
-    # Create what will eventually be our root filesystem onto an overlay
-    (
-        LOG_LEVEL=5
-        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
-
-        setup_basic_environment
-        mask_supporting_services
-        dracut_install true rm socat
-
-        # setup the testsuite service
-        cat >$initdir/etc/systemd/system/testsuite.service <<'EOF'
-[Unit]
-Description=Testsuite service
-
-[Service]
-Type=oneshot
-ExecStart=/bin/sh -e -x -c 'rm -f /tmp/nonexistent; systemctl start test.socket; printf x > test.file; socat -t20 OPEN:test.file UNIX-CONNECT:/run/test.ctl; >/testok'
-EOF
-
-       cat  >$initdir/etc/systemd/system/test.socket <<'EOF'
-[Socket]
-ListenStream=/run/test.ctl
-EOF
-
-       cat > $initdir/etc/systemd/system/test.service <<'EOF'
-[Unit]
-Requires=test.socket
-ConditionPathExistsGlob=/tmp/nonexistent
-
-[Service]
-ExecStart=/bin/true
-EOF
-
-        setup_testsuite
-    )
-    setup_nspawn_root
-}
-
-do_test "$@"
+do_test "$@" 10
index e444414a90fe13e2a7fecb311200b2ee6b193102..da003c90d512dc4d0e68468a29dbdea0f4f9fafa 100755 (executable)
@@ -5,58 +5,4 @@ TEST_NO_NSPAWN=1
 
 . $TEST_BASE_DIR/test-functions
 
-test_setup() {
-    create_empty_image_rootdir
-
-    # Create what will eventually be our root filesystem onto an overlay
-    (
-        LOG_LEVEL=5
-        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
-
-        setup_basic_environment
-        mask_supporting_services
-        dracut_install false touch
-
-        # setup the testsuite service
-        cat >$initdir/etc/systemd/system/testsuite.service <<EOF
-[Unit]
-Description=Testsuite service
-
-[Service]
-ExecStart=/test-fail-on-restart.sh
-Type=oneshot
-EOF
-
-        cat >$initdir/etc/systemd/system/fail-on-restart.service <<EOF
-[Unit]
-Description=Fail on restart
-StartLimitIntervalSec=1m
-StartLimitBurst=3
-
-[Service]
-Type=simple
-ExecStart=/bin/false
-Restart=always
-EOF
-
-
-        cat >$initdir/test-fail-on-restart.sh <<'EOF'
-#!/usr/bin/env bash
-set -x
-
-systemctl start fail-on-restart.service
-active_state=$(systemctl show --property ActiveState fail-on-restart.service)
-while [[ "$active_state" == "ActiveState=activating" || "$active_state" == "ActiveState=active" ]]; do
-    sleep 1
-    active_state=$(systemctl show --property ActiveState fail-on-restart.service)
-done
-systemctl is-failed fail-on-restart.service || exit 1
-touch /testok
-EOF
-
-        chmod 0755 $initdir/test-fail-on-restart.sh
-        setup_testsuite
-    )
-}
-
-do_test "$@"
+do_test "$@" 11
index e30c36ed860eaf4166c5791c6e250e067ea6b817..c8abefbd866641e0df6206920c241435308d3a36 100755 (executable)
@@ -5,85 +5,4 @@ TEST_NO_QEMU=1
 
 . $TEST_BASE_DIR/test-functions
 
-test_setup() {
-    create_empty_image_rootdir
-
-    # Create what will eventually be our root filesystem onto an overlay
-    (
-        LOG_LEVEL=5
-        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
-
-        setup_basic_environment
-        mask_supporting_services
-        dracut_install cat mv stat nc
-
-        # setup the testsuite service
-        cat >$initdir/etc/systemd/system/testsuite.service <<EOF
-[Unit]
-Description=Testsuite service
-After=multi-user.target
-
-[Service]
-ExecStart=/test-socket-group.sh
-Type=oneshot
-EOF
-
-        cat >$initdir/test-socket-group.sh <<'EOF'
-#!/usr/bin/env bash
-set -x
-set -e
-set -o pipefail
-
-U=/run/systemd/system/test.socket
-cat <<'EOL' >$U
-[Unit]
-Description=Test socket
-[Socket]
-Accept=yes
-ListenStream=/run/test.socket
-SocketGroup=adm
-SocketMode=0660
-EOL
-
-cat <<'EOL' > /run/systemd/system/test@.service
-[Unit]
-Description=Test service
-[Service]
-StandardInput=socket
-ExecStart=/bin/sh -x -c cat
-EOL
-
-systemctl start test.socket
-systemctl is-active test.socket
-[[ "$(stat --format='%G' /run/test.socket)" == adm ]]
-echo A | nc -w1 -U /run/test.socket
-
-mv $U ${U}.disabled
-systemctl daemon-reload
-systemctl is-active test.socket
-[[ "$(stat --format='%G' /run/test.socket)" == adm ]]
-echo B | nc -w1 -U /run/test.socket && exit 1
-
-mv ${U}.disabled $U
-systemctl daemon-reload
-systemctl is-active test.socket
-echo C | nc -w1 -U /run/test.socket && exit 1
-[[ "$(stat --format='%G' /run/test.socket)" == adm ]]
-
-systemctl restart test.socket
-systemctl is-active test.socket
-echo D | nc -w1 -U /run/test.socket
-[[ "$(stat --format='%G' /run/test.socket)" == adm ]]
-
-
-touch /testok
-EOF
-
-        chmod 0755 $initdir/test-socket-group.sh
-        setup_testsuite
-    )
-
-    setup_nspawn_root
-}
-
-do_test "$@"
+do_test "$@" 12
deleted file mode 100644 (file)
index e5e3350211bdfe821ab61a17555dbcea0a4ffc7d..0000000000000000000000000000000000000000
+++ /dev/null
@@ -1,10 +0,0 @@
-BUILD_DIR=$(shell ../../tools/find-build-dir.sh)
-
-all setup run:
-       @basedir=../.. TEST_BASE_DIR=../ BUILD_DIR=$(BUILD_DIR) ./test.sh --$@
-
-clean clean-again:
-       @basedir=../.. TEST_BASE_DIR=../ ./test.sh --clean
-       @rm -f has-overflow
-
-.PHONY: all setup run clean clean-again
new file mode 120000 (symlink)
index 0000000000000000000000000000000000000000..e9f93b1104cd21161bfe11fb1b3b534ce2ae82b5
--- /dev/null
@@ -0,0 +1 @@
+../TEST-01-BASIC/Makefile
\ No newline at end of file
diff --git a/test/TEST-13-NSPAWN-SMOKE/create-busybox-container b/test/TEST-13-NSPAWN-SMOKE/create-busybox-container
deleted file mode 100755 (executable)
index 08fb5d4..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-set -u
-set -o pipefail
-
-root="${1:?Usage $0 container-root}"
-mkdir -p "$root"
-mkdir "$root/bin"
-cp $(type -P busybox) "$root/bin"
-
-mkdir -p "$root/usr/lib"
-touch "$root/usr/lib/os-release"
-
-ln -s busybox "$root/bin/sh"
-ln -s busybox "$root/bin/cat"
-ln -s busybox "$root/bin/tr"
-ln -s busybox "$root/bin/ps"
-ln -s busybox "$root/bin/ip"
-
-mkdir -p "$root/sbin"
-cat <<'EOF' >"$root/sbin/init"
-#!/bin/sh
-
-printf "ps aufx:\n"
-ps aufx
-
-printf "/proc/1/cmdline:\n"
-printf "%s\n\n" "$(tr '\0' ' ' </proc/1/cmdline)"
-
-printf "/proc/1/environ:\n"
-printf "%s\n\n" "$(tr '\0' '\n' </proc/1/environ)"
-
-printf "/proc/1/mountinfo:\n"
-cat /proc/self/mountinfo
-printf "\n"
-
-printf "/proc/1/cgroup:\n"
-printf "%s\n\n" "$(cat /proc/1/cgroup)"
-
-printf "/proc/1/uid_map:\n"
-printf "%s\n\n" "$(cat /proc/1/uid_map)"
-
-printf "/proc/1/setgroups:\n"
-printf "%s\n\n" "$(cat /proc/1/setgroups)"
-
-printf "/proc/1/gid_map:\n"
-printf "%s\n\n" "$(cat /proc/1/gid_map)"
-
-printf "ip link:\n"
-ip link
-EOF
-chmod +x "$root/sbin/init"
index 974b239d80a842f807fb4fc2c155a3da53ba5d99..236378a3802c7347b7f85a99d3a4ca47b899e4a4 100755 (executable)
 #!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="systemd-nspawn smoke test"
+IMAGE_NAME="nspawn"
 TEST_NO_NSPAWN=1
 
 . $TEST_BASE_DIR/test-functions
 
-test_setup() {
+test_create_image() {
     create_empty_image_rootdir
 
     # Create what will eventually be our root filesystem onto an overlay
     (
         LOG_LEVEL=5
-        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
-
         setup_basic_environment
         mask_supporting_services
-        dracut_install busybox chmod rmdir unshare ip sysctl
-
-        cp create-busybox-container $initdir/
-
-        ./create-busybox-container $initdir/nc-container
-        initdir="$initdir/nc-container" dracut_install nc ip
-
-        # setup the testsuite service
-        cat >$initdir/etc/systemd/system/testsuite.service <<EOF
-[Unit]
-Description=Testsuite service
-
-[Service]
-ExecStart=/test-nspawn.sh
-Type=oneshot
-EOF
-
-        cat >$initdir/test-nspawn.sh <<'EOF'
-#!/usr/bin/env bash
-set -x
-set -e
-set -u
-set -o pipefail
-
-export SYSTEMD_LOG_LEVEL=debug
-
-# check cgroup-v2
-is_v2_supported=no
-mkdir -p /tmp/cgroup2
-if mount -t cgroup2 cgroup2 /tmp/cgroup2; then
-    is_v2_supported=yes
-    umount /tmp/cgroup2
-fi
-rmdir /tmp/cgroup2
-
-# check cgroup namespaces
-is_cgns_supported=no
-if [[ -f /proc/1/ns/cgroup ]]; then
-    is_cgns_supported=yes
-fi
-
-is_user_ns_supported=no
-# On some systems (e.g. CentOS 7) the default limit for user namespaces
-# is set to 0, which causes the following unshare syscall to fail, even
-# with enabled user namespaces support. By setting this value explicitly
-# we can ensure the user namespaces support to be detected correctly.
-sysctl -w user.max_user_namespaces=10000
-if unshare -U sh -c :; then
-    is_user_ns_supported=yes
-fi
-
-function check_bind_tmp_path {
-    # https://github.com/systemd/systemd/issues/4789
-    local _root="/var/lib/machines/bind-tmp-path"
-    /create-busybox-container "$_root"
-    >/tmp/bind
-    systemd-nspawn --register=no -D "$_root" --bind=/tmp/bind /bin/sh -c 'test -e /tmp/bind'
-}
-
-function check_norbind {
-    # https://github.com/systemd/systemd/issues/13170
-    local _root="/var/lib/machines/norbind-path"
-    mkdir -p /tmp/binddir/subdir
-    echo -n "outer" > /tmp/binddir/subdir/file
-    mount -t tmpfs tmpfs /tmp/binddir/subdir
-    echo -n "inner" > /tmp/binddir/subdir/file
-    /create-busybox-container "$_root"
-    systemd-nspawn --register=no -D "$_root" --bind=/tmp/binddir:/mnt:norbind /bin/sh -c 'CONTENT=$(cat /mnt/subdir/file); if [[ $CONTENT != "outer" ]]; then echo "*** unexpected content: $CONTENT"; return 1; fi'
-}
-
-function check_notification_socket {
-    # https://github.com/systemd/systemd/issues/4944
-    local _cmd='echo a | $(busybox which nc) -U -u -w 1 /run/systemd/nspawn/notify'
-    systemd-nspawn --register=no -D /nc-container /bin/sh -x -c "$_cmd"
-    systemd-nspawn --register=no -D /nc-container -U /bin/sh -x -c "$_cmd"
-}
-
-function run {
-    if [[ "$1" = "yes" && "$is_v2_supported" = "no" ]]; then
-        printf "Unified cgroup hierarchy is not supported. Skipping.\n" >&2
-        return 0
-    fi
-    if [[ "$2" = "yes" && "$is_cgns_supported" = "no" ]];  then
-        printf "CGroup namespaces are not supported. Skipping.\n" >&2
-        return 0
-    fi
-
-    local _root="/var/lib/machines/unified-$1-cgns-$2-api-vfs-writable-$3"
-    /create-busybox-container "$_root"
-    SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" -b
-    SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" --private-network -b
-
-    if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" -U -b; then
-       [[ "$is_user_ns_supported" = "yes" && "$3" = "network" ]] && return 1
-    else
-       [[ "$is_user_ns_supported" = "no" && "$3" = "network" ]] && return 1
-    fi
-
-    if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" --private-network -U -b; then
-       [[ "$is_user_ns_supported" = "yes" && "$3" = "yes" ]] && return 1
-    else
-       [[ "$is_user_ns_supported" = "no" && "$3" = "yes" ]] && return 1
-    fi
-
-    local _netns_opt="--network-namespace-path=/proc/self/ns/net"
-
-    # --network-namespace-path and network-related options cannot be used together
-    if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-interface=lo -b; then
-       return 1
-    fi
-
-    if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-macvlan=lo -b; then
-       return 1
-    fi
-
-    if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-ipvlan=lo -b; then
-       return 1
-    fi
-
-    if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-veth -b; then
-       return 1
-    fi
-
-    if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-veth-extra=lo -b; then
-       return 1
-    fi
-
-    if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-bridge=lo -b; then
-       return 1
-    fi
-
-    if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-zone=zone -b; then
-       return 1
-    fi
-
-    # allow combination of --network-namespace-path and --private-network
-    if ! SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --private-network -b; then
-       return 1
-    fi
-
-    # test --network-namespace-path works with a network namespace created by "ip netns"
-    ip netns add nspawn_test
-    _netns_opt="--network-namespace-path=/run/netns/nspawn_test"
-    SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" /bin/ip a | grep -v -E '^1: lo.*UP'
-    local r=$?
-    ip netns del nspawn_test
-
-    if [ $r -ne 0 ]; then
-       return 1
-    fi
-
-    return 0
-}
-
-check_bind_tmp_path
-
-check_norbind
-
-check_notification_socket
-
-for api_vfs_writable in yes no network; do
-    run no no $api_vfs_writable
-    run yes no $api_vfs_writable
-    run no yes $api_vfs_writable
-    run yes yes $api_vfs_writable
-done
-
-touch /testok
-EOF
 
-        chmod 0755 $initdir/test-nspawn.sh
-        setup_testsuite
+        ../create-busybox-container $initdir/testsuite-13.nc-container
+        initdir="$initdir/testsuite-13.nc-container" dracut_install nc ip md5sum
     )
 }
 
-do_test "$@"
+do_test "$@" 13
index 74cabf86aaaa51830e1c6bfffef532913b8c44a6..e8dbf23c580c954de3306d4a37ef9d12e9f61cc9 100755 (executable)
@@ -1,78 +1,21 @@
 #!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="/etc/machine-id testing"
+IMAGE_NAME="badid"
 TEST_NO_NSPAWN=1
 
 . $TEST_BASE_DIR/test-functions
 
-test_setup() {
+test_create_image() {
     create_empty_image_rootdir
 
     # Create what will eventually be our root filesystem onto an overlay
     (
         LOG_LEVEL=5
-        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
-
         setup_basic_environment
         mask_supporting_services
         printf "556f48e837bc4424a710fa2e2c9d3e3c\ne3d\n" >$initdir/etc/machine-id
-        dracut_install mount cmp
-
-        # setup the testsuite service
-        cat >$initdir/etc/systemd/system/testsuite.service <<EOF
-[Unit]
-Description=Testsuite service
-
-[Service]
-ExecStart=/bin/sh -e -x -c '/test-machine-id-setup.sh; systemctl --state=failed --no-legend --no-pager > /failed ; echo OK > /testok'
-Type=oneshot
-EOF
-
-cat >$initdir/test-machine-id-setup.sh <<'EOF'
-#!/usr/bin/env bash
-
-set -e
-set -x
-
-function setup_root {
-    local _root="$1"
-    mkdir -p "$_root"
-    mount -t tmpfs tmpfs "$_root"
-    mkdir -p "$_root/etc" "$_root/run"
-}
-
-function check {
-    printf "Expected\n"
-    cat "$1"
-    printf "\nGot\n"
-    cat "$2"
-    cmp "$1" "$2"
-}
-
-r="$(pwd)/overwrite-broken-machine-id"
-setup_root "$r"
-systemd-machine-id-setup --print --root "$r"
-echo abc >>"$r/etc/machine-id"
-id=$(systemd-machine-id-setup --print --root "$r")
-echo $id >expected
-check expected "$r/etc/machine-id"
-
-r="$(pwd)/transient-machine-id"
-setup_root "$r"
-systemd-machine-id-setup --print --root "$r"
-echo abc >>"$r/etc/machine-id"
-mount -o remount,ro "$r"
-mount -t tmpfs tmpfs "$r/run"
-transient_id=$(systemd-machine-id-setup --print --root "$r")
-mount -o remount,rw "$r"
-commited_id=$(systemd-machine-id-setup --print --commit --root "$r")
-[[ "$transient_id" = "$commited_id" ]]
-check "$r/etc/machine-id" "$r/run/machine-id"
-EOF
-chmod +x $initdir/test-machine-id-setup.sh
-
-        setup_testsuite
     )
 }
 
-do_test "$@"
+do_test "$@" 14
diff --git a/test/TEST-15-DROPIN/test-dropin.sh b/test/TEST-15-DROPIN/test-dropin.sh
deleted file mode 100755 (executable)
index f80c9df..0000000
+++ /dev/null
@@ -1,466 +0,0 @@
-#! /bin/bash
-set -e
-set -x
-
-_clear_service () {
-    systemctl stop $1.service 2>/dev/null || :
-    rm -f  /{etc,run,usr/lib}/systemd/system/$1.service
-    rm -fr /{etc,run,usr/lib}/systemd/system/$1.service.d
-    rm -fr /{etc,run,usr/lib}/systemd/system/$1.service.{wants,requires}
-}
-
-clear_services () {
-    for u in $*; do
-        _clear_service $u
-    done
-    systemctl daemon-reload
-}
-
-create_service () {
-    clear_services $1
-
-    cat >/etc/systemd/system/$1.service<<EOF
-[Unit]
-Description=$1 unit
-
-[Service]
-ExecStart=/bin/sleep 100000
-EOF
-    mkdir -p /{etc,run,usr/lib}/systemd/system/$1.service.d
-    mkdir -p /etc/systemd/system/$1.service.{wants,requires}
-    mkdir -p /run/systemd/system/$1.service.{wants,requires}
-    mkdir -p /usr/lib/systemd/system/$1.service.{wants,requires}
-}
-
-create_services () {
-    for u in $*; do
-        create_service $u
-    done
-}
-
-check_ok () {
-    [ $# -eq 3 ] || return
-
-    x="$(systemctl show --value -p $2 $1)"
-    case "$x" in
-        *$3*) return 0 ;;
-        *)    return 1 ;;
-    esac
-}
-
-check_ko () {
-    ! check_ok "$@"
-}
-
-test_basic_dropins () {
-    echo "Testing basic dropins..."
-
-    echo "*** test a wants b wants c"
-    create_services a b c
-    ln -s ../b.service /etc/systemd/system/a.service.wants/
-    ln -s ../c.service /etc/systemd/system/b.service.wants/
-    check_ok a Wants b.service
-    check_ok b Wants c.service
-
-    echo "*** test a wants,requires b"
-    create_services a b c
-    ln -s ../b.service /etc/systemd/system/a.service.wants/
-    ln -s ../b.service /etc/systemd/system/a.service.requires/
-    check_ok a Wants b.service
-    check_ok a Requires b.service
-
-    echo "*** test a wants nonexistent"
-    create_service a
-    ln -s ../nonexistent.service /etc/systemd/system/a.service.wants/
-    check_ok a Wants nonexistent.service
-    systemctl start a
-    systemctl stop  a
-
-    echo "*** test a requires nonexistent"
-    ln -sf ../nonexistent.service /etc/systemd/system/a.service.requires/
-    systemctl daemon-reload
-    check_ok a Requires nonexistent.service
-
-    # 'b' is already loaded when 'c' pulls it in via a dropin.
-    echo "*** test a,c require b"
-    create_services a b c
-    ln -sf ../b.service /etc/systemd/system/a.service.requires/
-    ln -sf ../b.service /etc/systemd/system/c.service.requires/
-    systemctl start a
-    check_ok c Requires b.service
-    systemctl stop a b
-
-    # 'b'  is already loaded when 'c' pulls it in via an alias dropin.
-    echo "*** test a wants alias"
-    create_services a b c
-    ln -sf c.service /etc/systemd/system/c1.service
-    ln -sf ../c.service  /etc/systemd/system/a.service.wants/
-    ln -sf ../c1.service /etc/systemd/system/b.service.wants/
-    systemctl start a
-    check_ok a Wants c.service
-    check_ok b Wants c.service
-    systemctl stop a c
-
-    echo "*** test service.d/ top level drop-in"
-    create_services a b
-    check_ko a ExecCondition "/bin/echo a"
-    check_ko b ExecCondition "/bin/echo b"
-    mkdir -p /usr/lib/systemd/system/service.d
-    cat >/usr/lib/systemd/system/service.d/override.conf <<EOF
-[Service]
-ExecCondition=/bin/echo %n
-EOF
-    check_ok a ExecCondition "/bin/echo a"
-    check_ok b ExecCondition "/bin/echo b"
-    rm -rf /usr/lib/systemd/system/service.d
-
-    clear_services a b c
-}
-
-test_hierarchical_dropins () {
-    echo "Testing hierarchical dropins..."
-    echo "*** test service.d/ top level drop-in"
-    create_services a-b-c
-    check_ko a-b-c ExecCondition "/bin/echo service.d"
-    check_ko a-b-c ExecCondition "/bin/echo a-.service.d"
-    check_ko a-b-c ExecCondition "/bin/echo a-b-.service.d"
-    check_ko a-b-c ExecCondition "/bin/echo a-b-c.service.d"
-
-    for dropin in service.d a-.service.d a-b-.service.d a-b-c.service.d; do
-        mkdir -p /usr/lib/systemd/system/$dropin
-        echo "
-[Service]
-ExecCondition=/bin/echo $dropin
-        " > /usr/lib/systemd/system/$dropin/override.conf
-        check_ok a-b-c ExecCondition "/bin/echo $dropin"
-    done
-    for dropin in service.d a-.service.d a-b-.service.d a-b-c.service.d; do
-        rm -rf /usr/lib/systemd/system/$dropin
-    done
-
-    clear_services a-b-c
-}
-
-test_template_dropins () {
-    echo "Testing template dropins..."
-
-    create_services foo bar@ yup@
-
-    # Declare some deps to check if the body was loaded
-    cat >>/etc/systemd/system/bar@.service <<EOF
-[Unit]
-After=bar-template-after.device
-EOF
-
-    cat >>/etc/systemd/system/yup@.service <<EOF
-[Unit]
-After=yup-template-after.device
-EOF
-
-    ln -s /etc/systemd/system/bar@.service /etc/systemd/system/foo.service.wants/bar@1.service
-    check_ok foo Wants bar@1.service
-
-    echo "*** test bar-alias@.service→bar@.service, but instance symlinks point to yup@.service ***"
-    ln -s bar@.service  /etc/systemd/system/bar-alias@.service
-    ln -s bar@1.service /etc/systemd/system/bar-alias@1.service
-    ln -s yup@.service  /etc/systemd/system/bar-alias@2.service
-    ln -s yup@3.service /etc/systemd/system/bar-alias@3.service
-
-    # create some dropin deps
-    mkdir -p /etc/systemd/system/bar@{,0,1,2,3}.service.requires/
-    mkdir -p /etc/systemd/system/yup@{,0,1,2,3}.service.requires/
-    mkdir -p /etc/systemd/system/bar-alias@{,0,1,2,3}.service.requires/
-
-    ln -s ../bar-template-requires.device /etc/systemd/system/bar@.service.requires/
-    ln -s ../bar-0-requires.device /etc/systemd/system/bar@0.service.requires/
-    ln -s ../bar-1-requires.device /etc/systemd/system/bar@1.service.requires/
-    ln -s ../bar-2-requires.device /etc/systemd/system/bar@2.service.requires/
-    ln -s ../bar-3-requires.device /etc/systemd/system/bar@3.service.requires/
-
-    ln -s ../yup-template-requires.device /etc/systemd/system/yup@.service.requires/
-    ln -s ../yup-0-requires.device /etc/systemd/system/yup@0.service.requires/
-    ln -s ../yup-1-requires.device /etc/systemd/system/yup@1.service.requires/
-    ln -s ../yup-2-requires.device /etc/systemd/system/yup@2.service.requires/
-    ln -s ../yup-3-requires.device /etc/systemd/system/yup@3.service.requires/
-
-    ln -s ../bar-alias-template-requires.device /etc/systemd/system/bar-alias@.service.requires/
-    ln -s ../bar-alias-0-requires.device /etc/systemd/system/bar-alias@0.service.requires/
-    ln -s ../bar-alias-1-requires.device /etc/systemd/system/bar-alias@1.service.requires/
-    ln -s ../bar-alias-2-requires.device /etc/systemd/system/bar-alias@2.service.requires/
-    ln -s ../bar-alias-3-requires.device /etc/systemd/system/bar-alias@3.service.requires/
-
-    systemctl daemon-reload
-
-    echo '*** bar@0 is aliased by bar-alias@0 ***'
-    systemctl show -p Names,Requires bar@0
-    systemctl show -p Names,Requires bar-alias@0
-    check_ok bar@0 Names bar@0
-    check_ok bar@0 Names bar-alias@0
-
-    check_ok bar@0 After bar-template-after.device
-
-    check_ok bar@0 Requires bar-0-requires.device
-    check_ok bar@0 Requires bar-alias-0-requires.device
-    check_ok bar@0 Requires bar-template-requires.device
-    check_ok bar@0 Requires bar-alias-template-requires.device
-    check_ko bar@0 Requires yup-template-requires.device
-
-    check_ok bar-alias@0 After bar-template-after.device
-
-    check_ok bar-alias@0 Requires bar-0-requires.device
-    check_ok bar-alias@0 Requires bar-alias-0-requires.device
-    check_ok bar-alias@0 Requires bar-template-requires.device
-    check_ok bar-alias@0 Requires bar-alias-template-requires.device
-    check_ko bar-alias@0 Requires yup-template-requires.device
-    check_ko bar-alias@0 Requires yup-0-requires.device
-
-    echo '*** bar@1 is aliased by bar-alias@1 ***'
-    systemctl show -p Names,Requires bar@1
-    systemctl show -p Names,Requires bar-alias@1
-    check_ok bar@1 Names bar@1
-    check_ok bar@1 Names bar-alias@1
-
-    check_ok bar@1 After bar-template-after.device
-
-    check_ok bar@1 Requires bar-1-requires.device
-    check_ok bar@1 Requires bar-alias-1-requires.device
-    check_ok bar@1 Requires bar-template-requires.device
-    # See https://github.com/systemd/systemd/pull/13119#discussion_r308145418
-    check_ok bar@1 Requires bar-alias-template-requires.device
-    check_ko bar@1 Requires yup-template-requires.device
-    check_ko bar@1 Requires yup-1-requires.device
-
-    check_ok bar-alias@1 After bar-template-after.device
-
-    check_ok bar-alias@1 Requires bar-1-requires.device
-    check_ok bar-alias@1 Requires bar-alias-1-requires.device
-    check_ok bar-alias@1 Requires bar-template-requires.device
-    check_ok bar-alias@1 Requires bar-alias-template-requires.device
-    check_ko bar-alias@1 Requires yup-template-requires.device
-    check_ko bar-alias@1 Requires yup-1-requires.device
-
-    echo '*** bar-alias@2 aliases yup@2, bar@2 is independent ***'
-    systemctl show -p Names,Requires bar@2
-    systemctl show -p Names,Requires bar-alias@2
-    check_ok bar@2 Names bar@2
-    check_ko bar@2 Names bar-alias@2
-
-    check_ok bar@2 After bar-template-after.device
-
-    check_ok bar@2 Requires bar-2-requires.device
-    check_ko bar@2 Requires bar-alias-2-requires.device
-    check_ok bar@2 Requires bar-template-requires.device
-    check_ko bar@2 Requires bar-alias-template-requires.device
-    check_ko bar@2 Requires yup-template-requires.device
-    check_ko bar@2 Requires yup-2-requires.device
-
-    check_ko bar-alias@2 After bar-template-after.device
-
-    check_ko bar-alias@2 Requires bar-2-requires.device
-    check_ok bar-alias@2 Requires bar-alias-2-requires.device
-    check_ko bar-alias@2 Requires bar-template-requires.device
-    check_ok bar-alias@2 Requires bar-alias-template-requires.device
-    check_ok bar-alias@2 Requires yup-template-requires.device
-    check_ok bar-alias@2 Requires yup-2-requires.device
-
-    echo '*** bar-alias@3 aliases yup@3, bar@3 is independent ***'
-    systemctl show -p Names,Requires bar@3
-    systemctl show -p Names,Requires bar-alias@3
-    check_ok bar@3 Names bar@3
-    check_ko bar@3 Names bar-alias@3
-
-    check_ok bar@3 After bar-template-after.device
-
-    check_ok bar@3 Requires bar-3-requires.device
-    check_ko bar@3 Requires bar-alias-3-requires.device
-    check_ok bar@3 Requires bar-template-requires.device
-    check_ko bar@3 Requires bar-alias-template-requires.device
-    check_ko bar@3 Requires yup-template-requires.device
-    check_ko bar@3 Requires yup-3-requires.device
-
-    check_ko bar-alias@3 After bar-template-after.device
-
-    check_ko bar-alias@3 Requires bar-3-requires.device
-    check_ok bar-alias@3 Requires bar-alias-3-requires.device
-    check_ko bar-alias@3 Requires bar-template-requires.device
-    check_ok bar-alias@3 Requires bar-alias-template-requires.device
-    check_ok bar-alias@3 Requires yup-template-requires.device
-    check_ok bar-alias@3 Requires yup-3-requires.device
-
-    clear_services foo {bar,yup,bar-alias}@{,1,2,3}
-}
-
-test_alias_dropins () {
-    echo "Testing alias dropins..."
-
-    echo "*** test a wants b1 alias of b"
-    create_services a b
-    ln -sf b.service /etc/systemd/system/b1.service
-    ln -sf ../b1.service /etc/systemd/system/a.service.wants/
-    check_ok a Wants b.service
-    systemctl start a
-    systemctl --quiet is-active b
-    systemctl stop a b
-    rm /etc/systemd/system/b1.service
-    clear_services a b
-
-    # Check that dependencies don't vary.
-    echo "*** test 2"
-    create_services a x y
-    mkdir -p /etc/systemd/system/a1.service.wants/
-    ln -sf a.service /etc/systemd/system/a1.service
-    ln -sf ../x.service /etc/systemd/system/a.service.wants/
-    ln -sf ../y.service /etc/systemd/system/a1.service.wants/
-    check_ok a1 Wants x.service # see [1]
-    check_ok a1 Wants y.service
-    systemctl start a
-    check_ok a1 Wants x.service # see [2]
-    check_ok a1 Wants y.service
-    systemctl stop a x y
-    rm /etc/systemd/system/a1.service
-
-    clear_services a x y
-}
-
-test_masked_dropins () {
-    echo "Testing masked dropins..."
-
-    create_services a b
-
-    # 'b' is masked for both deps
-    echo "*** test a wants,requires b is masked"
-    ln -sf /dev/null /etc/systemd/system/a.service.wants/b.service
-    ln -sf /dev/null /etc/systemd/system/a.service.requires/b.service
-    check_ko a Wants b.service
-    check_ko a Requires b.service
-
-    # 'a' wants 'b' and 'b' is masked at a lower level
-    echo "*** test a wants b, mask override"
-    ln -sf ../b.service /etc/systemd/system/a.service.wants/b.service
-    ln -sf /dev/null /usr/lib/systemd/system/a.service.wants/b.service
-    check_ok a Wants b.service
-
-    # 'a' wants 'b' and 'b' is masked at a higher level
-    echo "*** test a wants b, mask"
-    ln -sf /dev/null /etc/systemd/system/a.service.wants/b.service
-    ln -sf ../b.service /usr/lib/systemd/system/a.service.wants/b.service
-    check_ko a Wants b.service
-
-    # 'a' is masked but has an override config file
-    echo "*** test a is masked but has an override"
-    create_services a b
-    ln -sf /dev/null /etc/systemd/system/a.service
-    cat >/usr/lib/systemd/system/a.service.d/override.conf <<EOF
-[Unit]
-After=b.service
-EOF
-    check_ok a UnitFileState masked
-
-    # 'b1' is an alias for 'b': masking 'b' dep should not influence 'b1' dep
-    echo "*** test a wants b, b1, and one is masked"
-    create_services a b
-    ln -sf b.service /etc/systemd/system/b1.service
-    ln -sf /dev/null /etc/systemd/system/a.service.wants/b.service
-    ln -sf ../b1.service /usr/lib/systemd/system/a.service.wants/b1.service
-    systemctl cat a
-    systemctl show -p Wants,Requires a
-    systemctl cat b1
-    systemctl show -p Wants,Requires b1
-    check_ok a Wants b.service
-    check_ko a Wants b1.service # the alias does not show up in the list of units
-    rm /etc/systemd/system/b1.service
-
-    # 'b1' is an alias for 'b': masking 'b1' should not influence 'b' dep
-    echo "*** test a wants b, alias dep is masked"
-    create_services a b
-    ln -sf b.service /etc/systemd/system/b1.service
-    ln -sf /dev/null /etc/systemd/system/a.service.wants/b1.service
-    ln -sf ../b.service /usr/lib/systemd/system/a.service.wants/b.service
-    check_ok a Wants b.service
-    check_ko a Wants b1.service # the alias does not show up in the list of units
-    rm /etc/systemd/system/b1.service
-
-    # 'a' has Wants=b.service but also has a masking
-    # dropin 'b': 'b' should still be pulled in.
-    echo "*** test a wants b both ways"
-    create_services a b
-    ln -sf /dev/null /etc/systemd/system/a.service.wants/b.service
-    cat >/usr/lib/systemd/system/a.service.d/wants-b.conf<<EOF
-[Unit]
-Wants=b.service
-EOF
-    check_ok a Wants b.service
-
-    # mask a dropin that points to an nonexistent unit.
-    echo "*** test a wants nonexistent is masked"
-    create_services a
-    ln -sf /dev/null /etc/systemd/system/a.service.requires/nonexistent.service
-    ln -sf ../nonexistent.service /usr/lib/systemd/system/a.service.requires/
-    check_ko a Requires nonexistent.service
-
-    # 'b' is already loaded when 'c' pulls it in via a dropin but 'b' is
-    # masked at a higher level.
-    echo "*** test a wants b is masked"
-    create_services a b c
-    ln -sf ../b.service /etc/systemd/system/a.service.requires/
-    ln -sf ../b.service /run/systemd/system/c.service.requires/
-    ln -sf /dev/null /etc/systemd/system/c.service.requires/b.service
-    systemctl start a
-    check_ko c Requires b.service
-    systemctl stop a b
-
-    # 'b' is already loaded when 'c' pulls it in via a dropin but 'b' is
-    # masked at a lower level.
-    echo "*** test a requires b is masked"
-    create_services a b c
-    ln -sf ../b.service /etc/systemd/system/a.service.requires/
-    ln -sf ../b.service /etc/systemd/system/c.service.requires/
-    ln -sf /dev/null /run/systemd/system/c.service.requires/b.service
-    systemctl start a
-    check_ok c Requires b.service
-    systemctl stop a b
-
-    # 'a' requires 2 aliases of 'b' and one of them is a mask.
-    echo "*** test a requires alias of b, other alias masked"
-    create_services a b
-    ln -sf b.service /etc/systemd/system/b1.service
-    ln -sf b.service /etc/systemd/system/b2.service
-    ln -sf /dev/null /etc/systemd/system/a.service.requires/b1.service
-    ln -sf ../b1.service /run/systemd/system/a.service.requires/
-    ln -sf ../b2.service /usr/lib/systemd/system/a.service.requires/
-    check_ok a Requires b
-
-    # Same as above but now 'b' is masked.
-    echo "*** test a requires alias of b, b dep masked"
-    create_services a b
-    ln -sf b.service /etc/systemd/system/b1.service
-    ln -sf b.service /etc/systemd/system/b2.service
-    ln -sf ../b1.service /run/systemd/system/a.service.requires/
-    ln -sf ../b2.service /usr/lib/systemd/system/a.service.requires/
-    ln -sf /dev/null /etc/systemd/system/a.service.requires/b.service
-    check_ok a Requires b
-
-    clear_services a b
-}
-
-test_invalid_dropins () {
-    echo "Testing invalid dropins..."
-    # Assertion failed on earlier versions, command exits unsuccessfully on later versions
-    systemctl cat nonexistent@.service || true
-    create_services a
-    systemctl daemon-reload
-    # Assertion failed on earlier versions, command exits unsuccessfully on later versions
-    systemctl cat a@.service || true
-    systemctl stop a
-    clear_services a
-    return 0
-}
-
-test_basic_dropins
-test_hierarchical_dropins
-test_template_dropins
-test_alias_dropins
-test_masked_dropins
-test_invalid_dropins
-
-touch /testok
index 63bbd3505174f4c7b1c1857bf803d4a4a0905f44..1540e2e1f115dd1803226a367d52444ba8b7939d 100755 (executable)
@@ -5,18 +5,4 @@ TEST_NO_QEMU=1
 
 . $TEST_BASE_DIR/test-functions
 
-test_setup() {
-    # create the basic filesystem layout
-    setup_basic_environment
-    mask_supporting_services
-
-    # import the test scripts in the rootfs and plug them in systemd
-    cp testsuite.service $initdir/etc/systemd/system/
-    cp test-dropin.sh    $initdir/
-    setup_testsuite
-
-    # create dedicated rootfs for nspawn (located in $TESTDIR/nspawn-root)
-    setup_nspawn_root
-}
-
-do_test "$@"
+do_test "$@" 15
diff --git a/test/TEST-15-DROPIN/testsuite.service b/test/TEST-15-DROPIN/testsuite.service
deleted file mode 100644 (file)
index 4c9f65e..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-[Unit]
-Description=Testsuite service
-
-[Service]
-ExecStart=/test-dropin.sh
-Type=oneshot
diff --git a/test/TEST-16-EXTEND-TIMEOUT/assess.sh b/test/TEST-16-EXTEND-TIMEOUT/assess.sh
deleted file mode 100755 (executable)
index 6f98810..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-#!/usr/bin/env bash
-set -v -x
-
-rm -f /test.log
-
-TL=/test.log.XXXXXXXX
-
-function wait_for()
-{
-    service=${1}
-    result=${2:-success}
-    time=${3:-45}
-
-    while [[ ! -f /${service}.terminated && ! -f /${service}.success && $time -gt 0  ]]
-    do
-        sleep 1
-        time=$(( $time - 1 ))
-    done
-
-    if [[ ! -f /${service}.${result} ]]
-    then
-        journalctl -u testsuite-${service/_/-}.service >> "${TL}"
-    fi
-}
-
-# This checks all stages, start, runtime and stop, can be extended by
-# EXTEND_TIMEOUT_USEC
-
-wait_for success_all
-
-# These check that EXTEND_TIMEOUT_USEC that occurs at greater than the
-# extend timeout interval but less then the stage limit (TimeoutStartSec,
-# RuntimeMaxSec, TimeoutStopSec) still succeed.
-
-wait_for success_start
-wait_for success_runtime
-wait_for success_stop
-
-# These ensure that EXTEND_TIMEOUT_USEC will still timeout in the
-# approprate stage, after the stage limit, when the EXTEND_TIMEOUT_USEC
-# message isn't sent within the extend timeout interval.
-
-wait_for fail_start startfail
-wait_for fail_stop stopfail
-wait_for fail_runtime runtimefail
-
-if [[ -f "${TL}" ]]
-then
-    # no mv
-    cp "${TL}" /test.log
-    exit 1
-else
-    touch /testok
-    exit 0
-fi
diff --git a/test/TEST-16-EXTEND-TIMEOUT/extend_timeout_test_service.sh b/test/TEST-16-EXTEND-TIMEOUT/extend_timeout_test_service.sh
deleted file mode 100755 (executable)
index 40bf046..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-#!/usr/bin/env bash
-set -x
-set -e
-set -o pipefail
-
-# sleep interval (seconds)
-sleep_interval=1
-# extend_timeout_interval second(s)
-extend_timeout_interval=1
-# number of sleep_intervals before READY=1
-start_intervals=10
-# number of sleep_intervals before exiting
-stop_intervals=10
-# run intervals, number of sleep_intervals to run
-run_intervals=7
-# service name
-SERVICE=unknown
-
-while [ $# -gt 0 ];
-do
-    eval ${1%=*}=${1#*=}
-    shift
-done
-
-# We convert to usec
-extend_timeout_interval=$(( $extend_timeout_interval * 1000000 ))
-
-trap "{ touch /${SERVICE}.terminated; exit 1; }"  SIGTERM SIGABRT
-
-rm -f /${SERVICE}.*
-touch /${SERVICE}.startfail
-
-systemd-notify EXTEND_TIMEOUT_USEC=$extend_timeout_interval
-while [ $start_intervals -gt 0 ]
-do
-    sleep $sleep_interval
-    start_intervals=$(( $start_intervals - 1 ))
-    systemd-notify EXTEND_TIMEOUT_USEC=$extend_timeout_interval
-done
-
-systemd-notify --ready --status="Waiting for your request"
-
-touch /${SERVICE}.runtimefail
-rm /${SERVICE}.startfail
-
-systemd-notify EXTEND_TIMEOUT_USEC=$extend_timeout_interval
-while [ $run_intervals -gt 0 ]
-do
-    sleep $sleep_interval
-    run_intervals=$(( $run_intervals - 1 ))
-    systemd-notify EXTEND_TIMEOUT_USEC=$extend_timeout_interval
-done
-
-systemd-notify STOPPING=1
-
-touch /${SERVICE}.stopfail
-rm /${SERVICE}.runtimefail
-
-systemd-notify EXTEND_TIMEOUT_USEC=$extend_timeout_interval
-while [ $stop_intervals -gt 0 ]
-do
-    sleep $sleep_interval
-    stop_intervals=$(( $stop_intervals - 1 ))
-    systemd-notify EXTEND_TIMEOUT_USEC=$extend_timeout_interval
-done
-
-touch /${SERVICE}.success
-rm /${SERVICE}.stopfail
-
-exit 0
index 43d9f1278b3c8658b3f5ce5c4aae08ab9ab4340a..e1e2a68fa9e009dd651c09f0b6d93d47c0ee1f96 100755 (executable)
@@ -6,30 +6,4 @@ TEST_NO_QEMU=1
 
 . $TEST_BASE_DIR/test-functions
 
-test_setup() {
-    create_empty_image
-
-    # Create what will eventually be our root filesystem onto an overlay
-    (
-        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
-
-        setup_basic_environment
-        mask_supporting_services
-
-        for s in success-all success-start success-stop success-runtime \
-                 fail-start fail-stop fail-runtime
-        do
-            cp testsuite-${s}.service ${initdir}/etc/systemd/system
-        done
-        cp testsuite.service ${initdir}/etc/systemd/system
-
-        cp extend_timeout_test_service.sh ${initdir}/
-        cp assess.sh ${initdir}/
-
-        setup_testsuite
-    )
-
-    setup_nspawn_root
-}
-
-do_test "$@"
+do_test "$@" 16
diff --git a/test/TEST-16-EXTEND-TIMEOUT/testsuite-fail-runtime.service b/test/TEST-16-EXTEND-TIMEOUT/testsuite-fail-runtime.service
deleted file mode 100644 (file)
index e0b9f6a..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-
-[Unit]
-Description=Testsuite: Fail Runtime (EXTEND_TIMEOUT_USEC Didn't occur in sufficient time after RuntimeSecMax.)
-
-[Service]
-
-# EXTEND_TIMEOUT_USEC on runtime start (0) and 7 seconds after. Systemd will expect one at 7+5 (extend_timeout_interval)
-# seconds this won't happen until 7 + 7 (sleep interval) seconds. Therefore timeout at 12 seconds.
-Type=notify
-TimeoutStartSec=4
-TimeoutStopSec=4
-RuntimeMaxSec=10
-ExecStart=/extend_timeout_test_service.sh SERVICE=fail_runtime extend_timeout_interval=5 sleep_interval=7 start_intervals=0 run_intervals=2 stop_intervals=0
diff --git a/test/TEST-16-EXTEND-TIMEOUT/testsuite-fail-start.service b/test/TEST-16-EXTEND-TIMEOUT/testsuite-fail-start.service
deleted file mode 100644 (file)
index c3fcf23..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-
-[Unit]
-Description=Testsuite: Fail Start (EXTEND_TIMEOUT_USEC Didn't occur in sufficient time after TimeoutStartSec.)
-
-[Service]
-
-# EXTEND_TIMEOUT_USEC on startup and 7 seconds from start. Systemd will expect one at 7+5 (extend_timeout_interval)
-# seconds  this won't happen until 7 + 7 (sleep interval) seconds. Therefore timeout at 12 seconds.
-Type=notify
-TimeoutStartSec=10
-TimeoutStopSec=4
-RuntimeMaxSec=4
-ExecStart=/extend_timeout_test_service.sh SERVICE=fail_start extend_timeout_interval=5 sleep_interval=7 start_intervals=2 run_intervals=0 stop_intervals=0
diff --git a/test/TEST-16-EXTEND-TIMEOUT/testsuite-fail-stop.service b/test/TEST-16-EXTEND-TIMEOUT/testsuite-fail-stop.service
deleted file mode 100644 (file)
index ce76d10..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-
-[Unit]
-Description=Testsuite: Fail Stop (EXTEND_TIMEOUT_USEC Didn't occur in sufficient time after TimeoutStopSec.)
-
-[Service]
-
-# EXTEND_TIMEOUT_USEC on stop (0) and 7 seconds after. Systemd will expect one at 7+5 (extend_timeout_interval)
-# seconds this won't happen until 7 + 7 (sleep interval) seconds. Therefore timeout at 12 seconds.
-Type=notify
-TimeoutStartSec=4
-TimeoutStopSec=10
-RuntimeMaxSec=4
-ExecStart=/extend_timeout_test_service.sh SERVICE=fail_stop extend_timeout_interval=5 sleep_interval=7 start_intervals=0 run_intervals=0 stop_intervals=2
-# Due to 6041a7ee2c1bbff6301082f192fc1b0882400d42 SIGTERM isn't sent as the service shuts down with STOPPING=1
-# This file makes the test assess.sh quicker by notifing it that this test has finished.
-ExecStopPost=/bin/bash -c '[[ $SERVICE_RESULT == timeout && $EXIT_CODE == killed ]] && touch /fail_runtime.terminated'
diff --git a/test/TEST-16-EXTEND-TIMEOUT/testsuite-success-all.service b/test/TEST-16-EXTEND-TIMEOUT/testsuite-success-all.service
deleted file mode 100644 (file)
index 666f422..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-
-[Unit]
-Description=Testsuite: EXTEND_TIMEOUT_USEC Success - extend timeout on all services
-
-[Service]
-
-# Normal success - startup / runtime / shutdown all take 8 seconds which is within the EXTEND_TIMEOUT_USEC=4 seconds interval
-# runtime is 8+8+8 seconds. so we are relying on the EXTEND_TIMEOUT_USEC to exceed all stages, Start, Runtime and Stop.
-# success occurs after 24 seconds
-Type=notify
-TimeoutStartSec=4
-TimeoutStopSec=4
-RuntimeMaxSec=4
-ExecStart=/extend_timeout_test_service.sh SERVICE=success_all extend_timeout_interval=4 sleep_interval=2 start_intervals=3 run_intervals=3 stop_intervals=3
diff --git a/test/TEST-16-EXTEND-TIMEOUT/testsuite-success-runtime.service b/test/TEST-16-EXTEND-TIMEOUT/testsuite-success-runtime.service
deleted file mode 100644 (file)
index dc226f5..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-
-[Unit]
-Description=Testsuite: Success Runtime (EXTEND_TIMEOUT_USEC > WATCHDOG_USEC however < RuntimeMaxSec)
-
-[Service]
-
-# EXTEND_TIMEOUT_USEC=4 second once during runtime, but sleep for 6 seconds.
-# Runtime is 6 seconds and < RuntimeMaxSec so still successful.
-Type=notify
-TimeoutStartSec=4
-TimeoutStopSec=4
-RuntimeMaxSec=8
-ExecStart=/extend_timeout_test_service.sh SERVICE=success_runtime extend_timeout_interval=4 sleep_interval=6 start_intervals=0 run_intervals=1 stop_intervals=0
diff --git a/test/TEST-16-EXTEND-TIMEOUT/testsuite-success-start.service b/test/TEST-16-EXTEND-TIMEOUT/testsuite-success-start.service
deleted file mode 100644 (file)
index 228eece..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-
-[Unit]
-Description=Testsuite: Success Start (EXTEND_TIMEOUT_USEC > WATCHDOG_USEC however < TimeoutStartSec)
-
-[Service]
-
-# EXTEND_TIMEOUT_USEC=4 second interval once at startup, but sleep 6 seconds.
-# Therefore startup is 6 seconds and < TimeoutStartSec so still successful.
-Type=notify
-TimeoutStartSec=8
-TimeoutStopSec=4
-RuntimeMaxSec=4
-ExecStart=/extend_timeout_test_service.sh SERVICE=success_start extend_timeout_interval=4 sleep_interval=6 start_intervals=1 run_intervals=0 stop_intervals=0
diff --git a/test/TEST-16-EXTEND-TIMEOUT/testsuite-success-stop.service b/test/TEST-16-EXTEND-TIMEOUT/testsuite-success-stop.service
deleted file mode 100644 (file)
index b809397..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-
-[Unit]
-Description=Testsuite: Success Stop (EXTEND_TIMEOUT_USEC > WATCHDOG_USEC however < TimeoutStopSec)
-
-[Service]
-
-# EXTEND_TIMEOUT_USEC=4 seconds once during shutdown, but sleep for 6 seconds.
-# Therefore stop time is 6 seconds and < TimeoutStopSec so still successful.
-Type=notify
-TimeoutStartSec=4
-TimeoutStopSec=8
-RuntimeMaxSec=4
-ExecStart=/extend_timeout_test_service.sh SERVICE=success_stop extend_timeout_interval=4 sleep_interval=6 start_intervals=0 run_intervals=0 stop_intervals=1
diff --git a/test/TEST-16-EXTEND-TIMEOUT/testsuite.service b/test/TEST-16-EXTEND-TIMEOUT/testsuite.service
deleted file mode 100644 (file)
index 7512ba9..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-
-[Unit]
-Description=Testsuite: Assess all other testsuite-*.services worked as expected
-
-Wants=testsuite-success-all.service
-Wants=testsuite-success-start.service
-Wants=testsuite-success-runtime.service
-Wants=testsuite-success-stop.service
-Wants=testsuite-fail-start.service
-Wants=testsuite-fail-stop.service
-Wants=testsuite-fail-runtime.service
-StopWhenUnneeded=yes
-
-[Service]
-Type=simple
-TimeoutStartSec=infinity
-ExecStartPre=/assess.sh
-ExecStart=/bin/true
index e196003e808b67a0db533c87d8dc169875c72bab..5b8f22cbaae7beea187d3b00122217fc09c2e5e8 100755 (executable)
@@ -6,29 +6,4 @@ TEST_NO_NSPAWN=1
 . $TEST_BASE_DIR/test-functions
 QEMU_TIMEOUT=300
 
-test_setup() {
-    create_empty_image_rootdir
-
-    (
-        LOG_LEVEL=5
-        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
-
-        setup_basic_environment
-        mask_supporting_services
-
-        # setup the testsuite service
-        cat >$initdir/etc/systemd/system/testsuite.service <<EOF
-[Unit]
-Description=Testsuite service
-
-[Service]
-ExecStart=/bin/bash -x /testsuite.sh
-Type=oneshot
-EOF
-        cp testsuite.sh $initdir/
-
-        setup_testsuite
-    )
-}
-
-do_test "$@"
+do_test "$@" 17
diff --git a/test/TEST-17-UDEV-WANTS/testsuite.sh b/test/TEST-17-UDEV-WANTS/testsuite.sh
deleted file mode 100755 (executable)
index 989c190..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-#!/usr/bin/env bash
-set -ex
-set -o pipefail
-
-mkdir -p /run/udev/rules.d/
-
-rm -f /run/udev/rules.d/50-testsuite.rules
-udevadm control --reload
-udevadm trigger /dev/sda
-
-while : ; do
-    (
-        udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=foobar.service
-        udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=waldo.service
-        systemctl show -p WantedBy foobar.service | grep -q -v sda
-        systemctl show -p WantedBy waldo.service | grep -q -v sda
-    ) && break
-
-    sleep .5
-done
-
-cat > /run/udev/rules.d/50-testsuite.rules <<EOF
-ACTION!="remove", SUBSYSTEM=="block", KERNEL=="sda", ENV{SYSTEMD_WANTS}="foobar.service"
-EOF
-udevadm control --reload
-udevadm trigger /dev/sda
-
-while : ; do
-    (
-        udevadm info /dev/sda | grep -q SYSTEMD_WANTS=foobar.service
-        udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=waldo.service
-        systemctl show -p WantedBy foobar.service | grep -q sda
-        systemctl show -p WantedBy waldo.service | grep -q -v sda
-    ) && break
-
-    sleep .5
-done
-
-cat > /run/udev/rules.d/50-testsuite.rules <<EOF
-ACTION!="remove", SUBSYSTEM=="block", KERNEL=="sda", ENV{SYSTEMD_WANTS}="waldo.service"
-EOF
-udevadm control --reload
-udevadm trigger /dev/sda
-
-while : ; do
-    (
-        udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=foobar.service
-        udevadm info /dev/sda | grep -q SYSTEMD_WANTS=waldo.service
-        systemctl show -p WantedBy foobar.service | grep -q -v sda
-        systemctl show -p WantedBy waldo.service | grep -q sda
-    ) && break
-
-    sleep .5
-done
-
-rm /run/udev/rules.d/50-testsuite.rules
-
-udevadm control --reload
-udevadm trigger /dev/sda
-
-while : ; do
-    (
-        udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=foobar.service
-        udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=waldo.service
-        systemctl show -p WantedBy foobar.service | grep -q -v sda
-        systemctl show -p WantedBy waldo.service | grep -q -v sda
-    ) && break
-
-    sleep .5
-done
-
-echo OK > /testok
-
-exit 0
index b6231e6f5aec8f1422b018699302e9644f03bf00..5c386b8ea209aae9079510d15a44bc91fd8ec89a 100755 (executable)
@@ -5,31 +5,4 @@ TEST_DESCRIPTION="FailureAction= operation"
 . $TEST_BASE_DIR/test-functions
 QEMU_TIMEOUT=600
 
-test_setup() {
-    create_empty_image_rootdir
-
-    (
-        LOG_LEVEL=5
-        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
-
-        setup_basic_environment
-        mask_supporting_services
-
-        # setup the testsuite service
-        cat >$initdir/etc/systemd/system/testsuite.service <<EOF
-[Unit]
-Description=Testsuite service
-
-[Service]
-ExecStart=/bin/bash -x /testsuite.sh
-Type=oneshot
-EOF
-        cp testsuite.sh $initdir/
-
-        setup_testsuite
-    )
-
-    setup_nspawn_root
-}
-
-do_test "$@"
+do_test "$@" 18
diff --git a/test/TEST-18-FAILUREACTION/testsuite.sh b/test/TEST-18-FAILUREACTION/testsuite.sh
deleted file mode 100755 (executable)
index e471cda..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/usr/bin/env bash
-set -ex
-set -o pipefail
-
-systemd-run --wait -p FailureAction=poweroff true
-! systemd-run --wait -p SuccessAction=poweroff false
-
-if ! test -f /firstphase ; then
-    echo OK > /firstphase
-    systemd-run --wait -p SuccessAction=reboot true
-else
-    echo OK > /testok
-    systemd-run --wait -p FailureAction=poweroff false
-fi
-
-sleep infinity
index 2fbfef30628580eb36236afcf2abaeebdb068527..03c7760bfd14d099ded8f87fd97383a6e68c36c2 100755 (executable)
@@ -7,29 +7,4 @@ TEST_NO_NSPAWN=1
 QEMU_TIMEOUT=600
 UNIFIED_CGROUP_HIERARCHY=yes
 
-test_setup() {
-    create_empty_image_rootdir
-
-    (
-        LOG_LEVEL=5
-        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
-
-        setup_basic_environment
-        mask_supporting_services
-
-        # setup the testsuite service
-        cat >$initdir/etc/systemd/system/testsuite.service <<EOF
-[Unit]
-Description=Testsuite service
-
-[Service]
-ExecStart=/bin/bash -x /testsuite.sh
-Type=oneshot
-EOF
-        cp testsuite.sh $initdir/
-
-        setup_testsuite
-    )
-}
-
-do_test "$@"
+do_test "$@" 19
diff --git a/test/TEST-19-DELEGATE/testsuite.sh b/test/TEST-19-DELEGATE/testsuite.sh
deleted file mode 100755 (executable)
index 57831c2..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-#!/usr/bin/env bash
-set -ex
-set -o pipefail
-
-if grep -q cgroup2 /proc/filesystems ; then
-    systemd-run --wait --unit=test0.service -p "DynamicUser=1" -p "Delegate=" \
-                test -w /sys/fs/cgroup/system.slice/test0.service/ -a \
-                -w /sys/fs/cgroup/system.slice/test0.service/cgroup.procs -a \
-                -w /sys/fs/cgroup/system.slice/test0.service/cgroup.subtree_control
-
-    systemd-run --wait --unit=test1.service -p "DynamicUser=1" -p "Delegate=memory pids" \
-                grep -q memory /sys/fs/cgroup/system.slice/test1.service/cgroup.controllers
-
-    systemd-run --wait --unit=test2.service -p "DynamicUser=1" -p "Delegate=memory pids" \
-                grep -q pids /sys/fs/cgroup/system.slice/test2.service/cgroup.controllers
-
-    # "io" is not among the controllers enabled by default for all units, verify that
-    grep -qv io /sys/fs/cgroup/system.slice/cgroup.controllers
-
-    # Run a service with "io" enabled, and verify it works
-    systemd-run --wait --unit=test3.service -p "IOAccounting=yes" -p "Slice=system-foo-bar-baz.slice"  \
-                grep -q io /sys/fs/cgroup/system.slice/system-foo.slice/system-foo-bar.slice/system-foo-bar-baz.slice/test3.service/cgroup.controllers
-
-    # We want to check if "io" is removed again from the controllers
-    # list. However, PID 1 (rightfully) does this asynchronously. In order
-    # to force synchronization on this, let's start a short-lived service
-    # which requires PID 1 to refresh the cgroup tree, so that we can
-    # verify that this all works.
-    systemd-run --wait --unit=test4.service true
-
-    # And now check again, "io" should have vanished
-    grep -qv io /sys/fs/cgroup/system.slice/cgroup.controllers
-else
-    echo "Skipping TEST-19-DELEGATE, as the kernel doesn't actually support cgroup v2" >&2
-fi
-
-echo OK > /testok
-
-exit 0
index 4cf1b79f9a00fa2c14c4d2bf811cbdca6c1debee..50724b3f3c0bca3aa19e185ba1167ea5c8211bf5 100755 (executable)
@@ -4,34 +4,4 @@ TEST_DESCRIPTION="test changing main PID"
 
 . $TEST_BASE_DIR/test-functions
 
-test_setup() {
-    create_empty_image_rootdir
-
-    (
-        LOG_LEVEL=5
-        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
-
-        setup_basic_environment
-        mask_supporting_services
-
-        # setup the testsuite service
-        cat >$initdir/etc/systemd/system/testsuite.service <<EOF
-[Unit]
-Description=Testsuite service
-Before=getty-pre.target
-Wants=getty-pre.target
-
-[Service]
-ExecStart=/bin/bash -x /testsuite.sh
-Type=oneshot
-NotifyAccess=all
-EOF
-        cp testsuite.sh $initdir/
-
-        setup_testsuite
-    )
-
-    setup_nspawn_root
-}
-
-do_test "$@"
+do_test "$@" 20
diff --git a/test/TEST-20-MAINPIDGAMES/testsuite.sh b/test/TEST-20-MAINPIDGAMES/testsuite.sh
deleted file mode 100755 (executable)
index f894026..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-#!/usr/bin/env bash
-set -ex
-set -o pipefail
-
-systemd-analyze log-level debug
-systemd-analyze log-target console
-
-test `systemctl show -p MainPID --value testsuite.service` -eq $$
-
-# Start a test process inside of our own cgroup
-sleep infinity &
-INTERNALPID=$!
-disown
-
-# Start a test process outside of our own cgroup
-systemd-run -p DynamicUser=1 --unit=sleep.service /bin/sleep infinity
-EXTERNALPID=`systemctl show -p MainPID --value sleep.service`
-
-# Update our own main PID to the external test PID, this should work
-systemd-notify MAINPID=$EXTERNALPID
-test `systemctl show -p MainPID --value testsuite.service` -eq $EXTERNALPID
-
-# Update our own main PID to the internal test PID, this should work, too
-systemd-notify MAINPID=$INTERNALPID
-test `systemctl show -p MainPID --value testsuite.service` -eq $INTERNALPID
-
-# Update it back to our own PID, this should also work
-systemd-notify MAINPID=$$
-test `systemctl show -p MainPID --value testsuite.service` -eq $$
-
-# Try to set it to PID 1, which it should ignore, because that's the manager
-systemd-notify MAINPID=1
-test `systemctl show -p MainPID --value testsuite.service` -eq $$
-
-# Try to set it to PID 0, which is invalid and should be ignored
-systemd-notify MAINPID=0
-test `systemctl show -p MainPID --value testsuite.service` -eq $$
-
-# Try to set it to a valid but non-existing PID, which should be ignored. (Note
-# that we set the PID to a value well above any known /proc/sys/kernel/pid_max,
-# which means we can be pretty sure it doesn't exist by coincidence)
-systemd-notify MAINPID=1073741824
-test `systemctl show -p MainPID --value testsuite.service` -eq $$
-
-# Change it again to the external PID, without privileges this time. This should be ignored, because the PID is from outside of our cgroup and we lack privileges.
-systemd-notify --uid=1000 MAINPID=$EXTERNALPID
-test `systemctl show -p MainPID --value testsuite.service` -eq $$
-
-# Change it again to the internal PID, without privileges this time. This should work, as the process is on our cgroup, and that's enough even if we lack privileges.
-systemd-notify --uid=1000 MAINPID=$INTERNALPID
-test `systemctl show -p MainPID --value testsuite.service` -eq $INTERNALPID
-
-# Update it back to our own PID, this should also work
-systemd-notify --uid=1000 MAINPID=$$
-test `systemctl show -p MainPID --value testsuite.service` -eq $$
-
-cat >/tmp/mainpid.sh <<EOF
-#!/usr/bin/env bash
-
-set -eux
-set -o pipefail
-
-# Create a number of children, and make one the main one
-sleep infinity &
-disown
-
-sleep infinity &
-MAINPID=\$!
-disown
-
-sleep infinity &
-disown
-
-echo \$MAINPID > /run/mainpidsh/pid
-EOF
-chmod +x /tmp/mainpid.sh
-
-systemd-run --unit=mainpidsh.service -p StandardOutput=tty -p StandardError=tty -p Type=forking -p RuntimeDirectory=mainpidsh -p PIDFile=/run/mainpidsh/pid /tmp/mainpid.sh
-test `systemctl show -p MainPID --value mainpidsh.service` -eq `cat /run/mainpidsh/pid`
-
-cat >/tmp/mainpid2.sh <<EOF
-#!/usr/bin/env bash
-
-set -eux
-set -o pipefail
-
-# Create a number of children, and make one the main one
-sleep infinity &
-disown
-
-sleep infinity &
-MAINPID=\$!
-disown
-
-sleep infinity &
-disown
-
-echo \$MAINPID > /run/mainpidsh2/pid
-chown 1001:1001 /run/mainpidsh2/pid
-EOF
-chmod +x /tmp/mainpid2.sh
-
-systemd-run --unit=mainpidsh2.service -p StandardOutput=tty -p StandardError=tty -p Type=forking -p RuntimeDirectory=mainpidsh2 -p PIDFile=/run/mainpidsh2/pid /tmp/mainpid2.sh
-test `systemctl show -p MainPID --value mainpidsh2.service` -eq `cat /run/mainpidsh2/pid`
-
-cat >/dev/shm/mainpid3.sh <<EOF
-#!/usr/bin/env bash
-
-set -eux
-set -o pipefail
-
-sleep infinity &
-disown
-
-sleep infinity &
-disown
-
-sleep infinity &
-disown
-
-# Let's try to play games, and link up a privileged PID file
-ln -s ../mainpidsh/pid /run/mainpidsh3/pid
-
-# Quick assertion that the link isn't dead
-test -f /run/mainpidsh3/pid
-EOF
-chmod 755 /dev/shm/mainpid3.sh
-
-# This has to fail, as we shouldn't accept the dangerous PID file, and then inotify-wait on it to be corrected which we never do
-! systemd-run --unit=mainpidsh3.service -p StandardOutput=tty -p StandardError=tty -p Type=forking -p RuntimeDirectory=mainpidsh3 -p PIDFile=/run/mainpidsh3/pid -p DynamicUser=1 -p TimeoutStartSec=2s /dev/shm/mainpid3.sh
-
-# Test that this failed due to timeout, and not some other error
-test `systemctl show -p Result --value mainpidsh3.service` = timeout
-
-systemd-analyze log-level info
-
-echo OK > /testok
-
-exit 0
index c4b221af8a6738f5749c9cf69c76ecc260183643..2992beaa0b88213775bce9618860d3d7be42e132 100755 (executable)
@@ -1,7 +1,7 @@
 #!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="Sysuser-related tests"
-
+IMAGE_NAME="sysusers"
 . $TEST_BASE_DIR/test-functions
 
 test_setup() {
@@ -115,9 +115,7 @@ test_run() {
         echo "*** Running test $f"
         prepare_testdir ${f%.input}
         cp $f $TESTDIR/usr/lib/sysusers.d/test.conf
-        systemd-sysusers --root=$TESTDIR 2> /dev/null
-        journalctl --sync
-        journalctl -t systemd-sysusers -o cat | tail -n1 > $TESTDIR/tmp/err
+        systemd-sysusers --root=$TESTDIR 2>&1 | tail -n1 > $TESTDIR/tmp/err
         if ! diff -u $TESTDIR/tmp/err  ${f%.*}.expected-err; then
             echo "**** Unexpected error output for $f"
             cat $TESTDIR/tmp/err
diff --git a/test/TEST-22-TMPFILES/run-tmpfiles-tests.sh b/test/TEST-22-TMPFILES/run-tmpfiles-tests.sh
deleted file mode 100755 (executable)
index a0158f0..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/usr/bin/env bash
-
-set -x
-set -e
-
->/failed
-
-for t in test-*.sh; do
-        echo "Running $t"; ./$t
-done
-
-touch /testok
-rm /failed
diff --git a/test/TEST-22-TMPFILES/test-01.sh b/test/TEST-22-TMPFILES/test-01.sh
deleted file mode 100755 (executable)
index d233e37..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-#! /bin/bash
-#
-# With "e" don't attempt to set permissions when file doesn't exist, see
-# https://github.com/systemd/systemd/pull/6682.
-#
-
-set -e
-
-rm -fr /tmp/test
-
-echo "e /tmp/test - root root 1d" | systemd-tmpfiles --create -
-
-! test -e /tmp/test
diff --git a/test/TEST-22-TMPFILES/test-02.sh b/test/TEST-22-TMPFILES/test-02.sh
deleted file mode 100755 (executable)
index d1bf1ea..0000000
+++ /dev/null
@@ -1,122 +0,0 @@
-#! /bin/bash
-#
-# Basic tests for types creating directories
-#
-
-set -e
-set -x
-
-rm -fr /tmp/{C,d,D,e}
-mkdir  /tmp/{C,d,D,e}
-
-#
-# 'd'
-#
-mkdir /tmp/d/2
-chmod 777 /tmp/d/2
-
-systemd-tmpfiles --create - <<EOF
-d     /tmp/d/1    0755 daemon daemon - -
-d     /tmp/d/2    0755 daemon daemon - -
-EOF
-
-test -d /tmp/d/1
-test $(stat -c %U:%G:%a /tmp/d/1) = "daemon:daemon:755"
-
-test -d /tmp/d/2
-test $(stat -c %U:%G:%a /tmp/d/2) = "daemon:daemon:755"
-
-#
-# 'D'
-#
-mkdir /tmp/D/2
-chmod 777 /tmp/D/2
-touch /tmp/D/2/foo
-
-systemd-tmpfiles --create - <<EOF
-D     /tmp/D/1    0755 daemon daemon - -
-D     /tmp/D/2    0755 daemon daemon - -
-EOF
-
-test -d /tmp/D/1
-test $(stat -c %U:%G:%a /tmp/D/1) = "daemon:daemon:755"
-
-test -d /tmp/D/2
-test $(stat -c %U:%G:%a /tmp/D/2) = "daemon:daemon:755"
-
-systemd-tmpfiles --remove - <<EOF
-D     /tmp/D/2    0755 daemon daemon - -
-EOF
-
-# the content of '2' should be removed
-test "$(echo /tmp/D/2/*)" = "/tmp/D/2/*"
-
-#
-# 'e'
-#
-mkdir -p /tmp/e/2/{d1,d2}
-chmod 777 /tmp/e/2
-chmod 777 /tmp/e/2/d*
-
-systemd-tmpfiles --create - <<EOF
-e     /tmp/e/1     0755 daemon daemon - -
-e     /tmp/e/2/*   0755 daemon daemon - -
-EOF
-
-! test -d /tmp/e/1
-
-test -d /tmp/e/2
-test $(stat -c %U:%G:%a /tmp/e/2) = "root:root:777"
-
-test -d /tmp/e/2/d1
-test $(stat -c %U:%G:%a /tmp/e/2/d1) = "daemon:daemon:755"
-test -d /tmp/e/2/d2
-test $(stat -c %U:%G:%a /tmp/e/2/d2) = "daemon:daemon:755"
-
-# 'e' operates on directories only
-mkdir -p /tmp/e/3/{d1,d2}
-chmod 777 /tmp/e/3
-chmod 777 /tmp/e/3/d*
-touch /tmp/e/3/f1
-chmod 644 /tmp/e/3/f1
-
-! systemd-tmpfiles --create - <<EOF
-e     /tmp/e/3/*   0755 daemon daemon - -
-EOF
-
-# the directories should have been processed although systemd-tmpfiles failed
-# previously due to the presence of a file.
-test -d /tmp/e/3/d1
-test $(stat -c %U:%G:%a /tmp/e/3/d1) = "daemon:daemon:755"
-test -d /tmp/e/3/d2
-test $(stat -c %U:%G:%a /tmp/e/3/d2) = "daemon:daemon:755"
-
-test -f /tmp/e/3/f1
-test $(stat -c %U:%G:%a /tmp/e/3/f1) = "root:root:644"
-
-#
-# 'C'
-#
-
-mkdir /tmp/C/{1,2,3}-origin
-touch /tmp/C/{1,2,3}-origin/f1
-chmod 755 /tmp/C/{1,2,3}-origin/f1
-
-mkdir /tmp/C/{2,3}
-touch /tmp/C/3/f1
-
-systemd-tmpfiles --create - <<EOF
-C     /tmp/C/1    0755 daemon daemon - /tmp/C/1-origin
-C     /tmp/C/2    0755 daemon daemon - /tmp/C/2-origin
-EOF
-
-test -d /tmp/C/1
-test $(stat -c %U:%G:%a /tmp/C/1/f1) = "daemon:daemon:755"
-test -d /tmp/C/2
-test $(stat -c %U:%G:%a /tmp/C/2/f1) = "daemon:daemon:755"
-
-! systemd-tmpfiles --create - <<EOF
-C     /tmp/C/3    0755 daemon daemon - /tmp/C/3-origin
-EOF
-
-test $(stat -c %U:%G:%a /tmp/C/3/f1) = "root:root:644"
diff --git a/test/TEST-22-TMPFILES/test-03.sh b/test/TEST-22-TMPFILES/test-03.sh
deleted file mode 100755 (executable)
index 8d009fb..0000000
+++ /dev/null
@@ -1,236 +0,0 @@
-#! /bin/bash
-#
-# Basic tests for types creating/writing files
-#
-
-set -e
-set -x
-
-rm -fr /tmp/{f,F,w}
-mkdir  /tmp/{f,F,w}
-touch /tmp/file-owned-by-root
-
-#
-# 'f'
-#
-systemd-tmpfiles --create - <<EOF
-f     /tmp/f/1    0644 - - - -
-f     /tmp/f/2    0644 - - - This string should be written
-EOF
-
-### '1' should exist and be empty
-test -f /tmp/f/1; ! test -s /tmp/f/1
-test $(stat -c %U:%G:%a /tmp/f/1) = "root:root:644"
-
-test $(stat -c %U:%G:%a /tmp/f/2) = "root:root:644"
-test "$(< /tmp/f/2)" = "This string should be written"
-
-### The perms are supposed to be updated even if the file already exists.
-systemd-tmpfiles --create - <<EOF
-f     /tmp/f/1    0666 daemon daemon - This string should not be written
-EOF
-
-# file should be empty
-! test -s /tmp/f/1
-test $(stat -c %U:%G:%a /tmp/f/1) = "daemon:daemon:666"
-
-### But we shouldn't try to set perms on an existing file which is not a
-### regular one.
-mkfifo /tmp/f/fifo
-chmod 644 /tmp/f/fifo
-
-! systemd-tmpfiles --create - <<EOF
-f     /tmp/f/fifo    0666 daemon daemon - This string should not be written
-EOF
-
-test -p /tmp/f/fifo
-test $(stat -c %U:%G:%a /tmp/f/fifo) = "root:root:644"
-
-### 'f' should not follow symlinks.
-ln -s missing /tmp/f/dangling
-ln -s /tmp/file-owned-by-root /tmp/f/symlink
-
-! systemd-tmpfiles --create - <<EOF
-f     /tmp/f/dangling    0644 daemon daemon - -
-f     /tmp/f/symlink     0644 daemon daemon - -
-EOF
-! test -e /tmp/f/missing
-test $(stat -c %U:%G:%a /tmp/file-owned-by-root) = "root:root:644"
-
-### Handle read-only filesystem gracefully: we shouldn't fail if the target
-### already exists and have the correct perms.
-mkdir /tmp/f/rw-fs
-mkdir /tmp/f/ro-fs
-
-touch /tmp/f/rw-fs/foo
-chmod 644 /tmp/f/rw-fs/foo
-
-mount -o bind,ro /tmp/f/rw-fs /tmp/f/ro-fs
-
-systemd-tmpfiles --create - <<EOF
-f     /tmp/f/ro-fs/foo    0644 - - - - This string should not be written
-EOF
-test -f /tmp/f/ro-fs/foo; ! test -s /tmp/f/ro-fs/foo
-
-! systemd-tmpfiles --create - <<EOF
-f     /tmp/f/ro-fs/foo    0666 - - - -
-EOF
-test $(stat -c %U:%G:%a /tmp/f/fifo) = "root:root:644"
-
-! systemd-tmpfiles --create - <<EOF
-f     /tmp/f/ro-fs/bar    0644 - - - -
-EOF
-! test -e /tmp/f/ro-fs/bar
-
-### 'f' shouldn't follow unsafe paths.
-mkdir /tmp/f/daemon
-ln -s /root /tmp/f/daemon/unsafe-symlink
-chown -R --no-dereference daemon:daemon /tmp/f/daemon
-
-! systemd-tmpfiles --create - <<EOF
-f     /tmp/f/daemon/unsafe-symlink/exploit    0644 daemon daemon - -
-EOF
-! test -e /tmp/f/daemon/unsafe-symlink/exploit
-
-#
-# 'F'
-#
-echo "This should be truncated" >/tmp/F/truncated
-echo "This should be truncated" >/tmp/F/truncated-with-content
-
-systemd-tmpfiles --create - <<EOF
-F     /tmp/F/created                0644 - - - -
-F     /tmp/F/created-with-content   0644 - - - new content
-F     /tmp/F/truncated              0666 daemon daemon - -
-F     /tmp/F/truncated-with-content 0666 daemon daemon - new content
-EOF
-
-test -f /tmp/F/created; ! test -s /tmp/F/created
-test -f /tmp/F/created-with-content
-test "$(< /tmp/F/created-with-content)" = "new content"
-test -f /tmp/F/truncated; ! test -s /tmp/F/truncated
-test $(stat -c %U:%G:%a /tmp/F/truncated) = "daemon:daemon:666"
-test -s /tmp/F/truncated-with-content
-test $(stat -c %U:%G:%a /tmp/F/truncated-with-content) = "daemon:daemon:666"
-
-### We shouldn't try to truncate anything but regular files since the behavior is
-### unspecified in the other cases.
-mkfifo /tmp/F/fifo
-
-! systemd-tmpfiles --create - <<EOF
-F     /tmp/F/fifo                0644 - - - -
-EOF
-
-test -p /tmp/F/fifo
-
-### 'F' should not follow symlinks.
-ln -s missing /tmp/F/dangling
-ln -s /tmp/file-owned-by-root /tmp/F/symlink
-
-! systemd-tmpfiles --create - <<EOF
-f     /tmp/F/dangling    0644 daemon daemon - -
-f     /tmp/F/symlink     0644 daemon daemon - -
-EOF
-! test -e /tmp/F/missing
-test $(stat -c %U:%G:%a /tmp/file-owned-by-root) = "root:root:644"
-
-### Handle read-only filesystem gracefully: we shouldn't fail if the target
-### already exists and is empty.
-mkdir /tmp/F/rw-fs
-mkdir /tmp/F/ro-fs
-
-touch /tmp/F/rw-fs/foo
-chmod 644 /tmp/F/rw-fs/foo
-
-mount -o bind,ro /tmp/F/rw-fs /tmp/F/ro-fs
-
-systemd-tmpfiles --create - <<EOF
-F     /tmp/F/ro-fs/foo    0644 - - - -
-EOF
-test -f /tmp/F/ro-fs/foo; ! test -s /tmp/F/ro-fs/foo
-
-echo "truncating is not allowed anymore" >/tmp/F/rw-fs/foo
-! systemd-tmpfiles --create - <<EOF
-F     /tmp/F/ro-fs/foo    0644 - - - -
-EOF
-
-! systemd-tmpfiles --create - <<EOF
-F     /tmp/F/ro-fs/foo    0644 - - - - This string should not be written
-EOF
-test -f /tmp/F/ro-fs/foo; ! test -s /tmp/F/ro-fs/foo
-
-# Trying to change the perms should fail.
->/tmp/F/rw-fs/foo
-! systemd-tmpfiles --create - <<EOF
-F     /tmp/F/ro-fs/foo    0666 - - - -
-EOF
-test $(stat -c %U:%G:%a /tmp/F/ro-fs/foo) = "root:root:644"
-
-### Try to create a new file.
-! systemd-tmpfiles --create - <<EOF
-F     /tmp/F/ro-fs/bar    0644 - - - -
-EOF
-! test -e /tmp/F/ro-fs/bar
-
-### 'F' shouldn't follow unsafe paths.
-mkdir /tmp/F/daemon
-ln -s /root /tmp/F/daemon/unsafe-symlink
-chown -R --no-dereference daemon:daemon /tmp/F/daemon
-
-! systemd-tmpfiles --create - <<EOF
-F     /tmp/F/daemon/unsafe-symlink/exploit    0644 daemon daemon - -
-EOF
-! test -e /tmp/F/daemon/unsafe-symlink/exploit
-
-#
-# 'w'
-#
-touch /tmp/w/overwritten
-
-### nop if the target does not exist.
-systemd-tmpfiles --create - <<EOF
-w     /tmp/w/unexistent    0644 - - - new content
-EOF
-! test -e /tmp/w/unexistent
-
-### no argument given -> fails.
-! systemd-tmpfiles --create - <<EOF
-w     /tmp/w/unexistent    0644 - - - -
-EOF
-
-### write into an empty file.
-systemd-tmpfiles --create - <<EOF
-w     /tmp/w/overwritten    0644 - - - old content
-EOF
-test -f /tmp/w/overwritten
-test "$(< /tmp/w/overwritten)" = "old content"
-
-### new content is overwritten
-systemd-tmpfiles --create - <<EOF
-w     /tmp/w/overwritten    0644 - - - new content
-EOF
-test -f /tmp/w/overwritten
-test "$(< /tmp/w/overwritten)" = "new content"
-
-### writing into an 'exotic' file should be allowed.
-systemd-tmpfiles --create - <<EOF
-w     /dev/null    - - - - new content
-EOF
-
-### 'w' follows symlinks
-ln -s ./overwritten /tmp/w/symlink
-systemd-tmpfiles --create - <<EOF
-w     /tmp/w/symlink    - - - - $(readlink -e /tmp/w/symlink)
-EOF
-readlink -e /tmp/w/symlink
-test "$(< /tmp/w/overwritten)" = "/tmp/w/overwritten"
-
-### 'w' shouldn't follow unsafe paths.
-mkdir /tmp/w/daemon
-ln -s /root /tmp/w/daemon/unsafe-symlink
-chown -R --no-dereference daemon:daemon /tmp/w/daemon
-
-! systemd-tmpfiles --create - <<EOF
-f     /tmp/w/daemon/unsafe-symlink/exploit    0644 daemon daemon - -
-EOF
-! test -e /tmp/w/daemon/unsafe-symlink/exploit
diff --git a/test/TEST-22-TMPFILES/test-04.sh b/test/TEST-22-TMPFILES/test-04.sh
deleted file mode 100755 (executable)
index f916086..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-#! /bin/bash
-#
-# Basic tests for types creating fifos
-#
-
-set -e
-set -x
-
-rm -fr /tmp/p
-mkdir  /tmp/p
-touch  /tmp/p/f1
-
-systemd-tmpfiles --create - <<EOF
-p     /tmp/p/fifo1    0666 - - - -
-EOF
-
-test -p /tmp/p/fifo1
-test $(stat -c %U:%G:%a /tmp/p/fifo1) = "root:root:666"
-
-# it should refuse to overwrite an existing file
-! systemd-tmpfiles --create - <<EOF
-p     /tmp/p/f1    0666 - - - -
-EOF
-
-test -f /tmp/p/f1
-
-# unless '+' prefix is used
-systemd-tmpfiles --create - <<EOF
-p+     /tmp/p/f1    0666 - - - -
-EOF
-
-test -p /tmp/p/f1
-test $(stat -c %U:%G:%a /tmp/p/f1) = "root:root:666"
-
-#
-# Must be fixed
-#
-# mkdir /tmp/p/daemon
-# #ln -s /root /tmp/F/daemon/unsafe-symlink
-# chown -R --no-dereference daemon:daemon /tmp/p/daemon
-#
-# systemd-tmpfiles --create - <<EOF
-# p      /tmp/p/daemon/fifo2    0666 daemon daemon - -
-# EOF
diff --git a/test/TEST-22-TMPFILES/test-05.sh b/test/TEST-22-TMPFILES/test-05.sh
deleted file mode 100755 (executable)
index 13c4ac8..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-#! /bin/bash
-
-set -e
-set -x
-
-rm -fr /tmp/{z,Z}
-mkdir  /tmp/{z,Z}
-
-#
-# 'z'
-#
-mkdir /tmp/z/d{1,2}
-touch /tmp/z/f1 /tmp/z/d1/f11 /tmp/z/d2/f21
-
-systemd-tmpfiles --create - <<EOF
-z     /tmp/z/f1    0755 daemon daemon - -
-z     /tmp/z/d1    0755 daemon daemon - -
-EOF
-
-test $(stat -c %U:%G /tmp/z/f1) = "daemon:daemon"
-test $(stat -c %U:%G /tmp/z/d1) = "daemon:daemon"
-test $(stat -c %U:%G /tmp/z/d1/f11) = "root:root"
-
-systemd-tmpfiles --create - <<EOF
-z     /tmp/z/d2/*    0755 daemon daemon - -
-EOF
-
-test $(stat -c %U:%G /tmp/z/d2/f21) = "daemon:daemon"
-
-#
-# 'Z'
-#
-mkdir /tmp/Z/d1 /tmp/Z/d1/d11
-touch /tmp/Z/f1 /tmp/Z/d1/f11 /tmp/Z/d1/d11/f111
-
-systemd-tmpfiles --create - <<EOF
-Z     /tmp/Z/f1    0755 daemon daemon - -
-Z     /tmp/Z/d1    0755 daemon daemon - -
-EOF
-
-test $(stat -c %U:%G /tmp/Z/f1) = "daemon:daemon"
-test $(stat -c %U:%G /tmp/Z/d1) = "daemon:daemon"
-test $(stat -c %U:%G /tmp/Z/d1/d11) = "daemon:daemon"
-test $(stat -c %U:%G /tmp/Z/d1/f11) = "daemon:daemon"
-test $(stat -c %U:%G /tmp/Z/d1/d11/f111) = "daemon:daemon"
diff --git a/test/TEST-22-TMPFILES/test-06.sh b/test/TEST-22-TMPFILES/test-06.sh
deleted file mode 100755 (executable)
index cd65ba6..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-#! /bin/bash
-#
-# Inspired by https://github.com/systemd/systemd/issues/9508
-#
-
-set -e
-
-test_snippet() {
-        systemd-tmpfiles "$@" - <<EOF
-d /var/tmp/foobar-test-06
-d /var/tmp/foobar-test-06/important
-R /var/tmp/foobar-test-06
-EOF
-}
-
-test_snippet --create --remove
-test -d /var/tmp/foobar-test-06
-test -d /var/tmp/foobar-test-06/important
-
-test_snippet --remove
-! test -f /var/tmp/foobar-test-06
-! test -f /var/tmp/foobar-test-06/important
-
-test_snippet --create
-test -d /var/tmp/foobar-test-06
-test -d /var/tmp/foobar-test-06/important
-
-touch /var/tmp/foobar-test-06/something-else
-
-test_snippet --create
-test -d /var/tmp/foobar-test-06
-test -d /var/tmp/foobar-test-06/important
-test -f /var/tmp/foobar-test-06/something-else
-
-test_snippet --create --remove
-test -d /var/tmp/foobar-test-06
-test -d /var/tmp/foobar-test-06/important
-! test -f /var/tmp/foobar-test-06/something-else
diff --git a/test/TEST-22-TMPFILES/test-07.sh b/test/TEST-22-TMPFILES/test-07.sh
deleted file mode 100755 (executable)
index 39c04b9..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-#! /bin/bash
-#
-# Verifies the issues described by https://github.com/systemd/systemd/issues/10191
-#
-
-set -e
-set -x
-
-rm -rf /tmp/test-prefix
-
-mkdir /tmp/test-prefix
-touch /tmp/test-prefix/file
-
-systemd-tmpfiles --remove - <<EOF
-r /tmp/test-prefix
-r /tmp/test-prefix/file
-EOF
-
-! test -f /tmp/test-prefix/file
-! test -f /tmp/test-prefix
-
-mkdir /tmp/test-prefix
-touch /tmp/test-prefix/file
-
-systemd-tmpfiles --remove - <<EOF
-r /tmp/test-prefix/file
-r /tmp/test-prefix
-EOF
-
-! test -f /tmp/test-prefix/file
-! test -f /tmp/test-prefix
diff --git a/test/TEST-22-TMPFILES/test-08.sh b/test/TEST-22-TMPFILES/test-08.sh
deleted file mode 100755 (executable)
index e7bf044..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-#! /bin/bash
-#
-# Verify tmpfiles can run in a root directory under a path prefix that contains
-# directories owned by unprivileged users, for example when a root file system
-# is mounted in a regular user's home directory.
-#
-# https://github.com/systemd/systemd/pull/11820
-#
-
-set -e
-
-rm -fr /tmp/root /tmp/user
-mkdir -p /tmp/root /tmp/user/root
-chown daemon:daemon /tmp/user
-
-# Verify the command works as expected with no prefix or a root-owned prefix.
-echo 'd /tmp/root/test1' | systemd-tmpfiles --create -
-test -d /tmp/root/test1
-echo 'd /test2' | systemd-tmpfiles --root=/tmp/root --create -
-test -d /tmp/root/test2
-
-# Verify the command fails to write to a root-owned subdirectory under an
-# unprivileged user's directory when it's not part of the prefix, as expected
-# by the unsafe_transition function.
-! echo 'd /tmp/user/root/test' | systemd-tmpfiles --create -
-! test -e /tmp/user/root/test
-! echo 'd /user/root/test' | systemd-tmpfiles --root=/tmp --create -
-! test -e /tmp/user/root/test
-
-# Verify the above works when all user-owned directories are in the prefix.
-echo 'd /test' | systemd-tmpfiles --root=/tmp/user/root --create -
-test -d /tmp/user/root/test
diff --git a/test/TEST-22-TMPFILES/test-09.sh b/test/TEST-22-TMPFILES/test-09.sh
deleted file mode 100755 (executable)
index c558dfd..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-#!/usr/bin/env bash
-
-set -e
-set -x
-
-# Make sure that the "stat" output is not locale dependent.
-export LANG=C LC_ALL=C
-
-# first, create file without suid/sgid
-systemd-tmpfiles --create - <<EOF
-f     /tmp/xxx    0755 1 1 - -
-f     /tmp/yyy    0755 1 1 - -
-EOF
-
-test "$(stat -c %F:%u:%g:%a /tmp/xxx)" = "regular empty file:1:1:755"
-test "$(stat -c %F:%u:%g:%a /tmp/yyy)" = "regular empty file:1:1:755"
-
-# then, add suid/sgid
-systemd-tmpfiles --create - <<EOF
-f     /tmp/xxx    04755
-f     /tmp/yyy    02755
-EOF
-
-test "$(stat -c %F:%u:%g:%a /tmp/xxx)" = "regular empty file:1:1:4755"
-test "$(stat -c %F:%u:%g:%a /tmp/yyy)" = "regular empty file:1:1:2755"
-
-# then, chown the files to somebody else
-systemd-tmpfiles --create - <<EOF
-f     /tmp/xxx    - 2 2
-f     /tmp/yyy    - 2 2
-EOF
-
-test "$(stat -c %F:%u:%g:%a /tmp/xxx)" = "regular empty file:2:2:4755"
-test "$(stat -c %F:%u:%g:%a /tmp/yyy)" = "regular empty file:2:2:2755"
-
-# then, chown the files to a third user/group but also drop to a mask that has
-# both more and fewer bits set
-systemd-tmpfiles --create - <<EOF
-f     /tmp/xxx    0770 3 3
-f     /tmp/yyy    0770 3 3
-EOF
-
-test "$(stat -c %F:%u:%g:%a /tmp/xxx)" = "regular empty file:3:3:770"
-test "$(stat -c %F:%u:%g:%a /tmp/yyy)" = "regular empty file:3:3:770"
-
-# return to the beginning
-systemd-tmpfiles --create - <<EOF
-f     /tmp/xxx    0755 1 1 - -
-f     /tmp/yyy    0755 1 1 - -
-EOF
-
-test "$(stat -c %F:%u:%g:%a /tmp/xxx)" = "regular empty file:1:1:755"
-test "$(stat -c %F:%u:%g:%a /tmp/yyy)" = "regular empty file:1:1:755"
-
-# remove everything
-systemd-tmpfiles --remove - <<EOF
-r /tmp/xxx
-r /tmp/yyy
-EOF
index aa6efcfb48fc206dbd6788a1c4688a8fca5a6338..317e4a88f787f78f21f37ee7df6691e68afdce0a 100755 (executable)
@@ -2,30 +2,6 @@
 set -e
 TEST_DESCRIPTION="Tmpfiles related tests"
 TEST_NO_QEMU=1
-
 . $TEST_BASE_DIR/test-functions
 
-test_setup() {
-    # create the basic filesystem layout
-    setup_basic_environment
-    mask_supporting_services
-    inst_binary mv
-    inst_binary stat
-    inst_binary seq
-    inst_binary xargs
-    inst_binary mkfifo
-    inst_binary readlink
-
-    # setup the testsuite service
-    cp testsuite.service $initdir/etc/systemd/system/
-    setup_testsuite
-
-    mkdir -p $initdir/testsuite
-    cp run-tmpfiles-tests.sh $initdir/testsuite/
-    cp test-*.sh $initdir/testsuite/
-
-    # create dedicated rootfs for nspawn (located in $TESTDIR/nspawn-root)
-    setup_nspawn_root
-}
-
-do_test "$@"
+do_test "$@" 22
diff --git a/test/TEST-22-TMPFILES/testsuite.service b/test/TEST-22-TMPFILES/testsuite.service
deleted file mode 100644 (file)
index 2f1b15c..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-[Unit]
-Description=Testsuite service
-After=systemd-tmpfiles-setup.service
-Before=getty-pre.target
-Wants=getty-pre.target
-
-[Service]
-WorkingDirectory=/testsuite
-ExecStart=/testsuite/run-tmpfiles-tests.sh
-Type=oneshot
-StandardOutput=tty
-StandardError=tty
index ebc9fe4c856d6a82cfcecc676de3aad4d1c72e8d..1b0d25a721ba9dbb091575f3edea4b982ff4eeef 100755 (executable)
@@ -1,33 +1,6 @@
 #!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="test Type=exec"
-
 . $TEST_BASE_DIR/test-functions
 
-test_setup() {
-    create_empty_image_rootdir
-
-    (
-        LOG_LEVEL=5
-        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
-
-        setup_basic_environment
-        mask_supporting_services
-
-        # setup the testsuite service
-        cat >$initdir/etc/systemd/system/testsuite.service <<EOF
-[Unit]
-Description=Testsuite service
-
-[Service]
-ExecStart=/testsuite.sh
-Type=oneshot
-EOF
-        cp testsuite.sh $initdir/
-
-        setup_testsuite
-    )
-    setup_nspawn_root
-}
-
-do_test "$@"
+do_test "$@" 23
diff --git a/test/TEST-23-TYPE-EXEC/testsuite.sh b/test/TEST-23-TYPE-EXEC/testsuite.sh
deleted file mode 100755 (executable)
index 5e2966f..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-#!/usr/bin/env bash
-set -ex
-set -o pipefail
-
-systemd-analyze log-level debug
-systemd-analyze log-target console
-
-# Create a binary for which execve() will fail
-touch /tmp/brokenbinary
-chmod +x /tmp/brokenbinary
-
-# These three commands should succeed.
-systemd-run --unit=one -p Type=simple /bin/sleep infinity
-systemd-run --unit=two -p Type=simple -p User=idontexist /bin/sleep infinity
-systemd-run --unit=three -p Type=simple /tmp/brokenbinary
-
-# And now, do the same with Type=exec, where the latter two should fail
-systemd-run --unit=four -p Type=exec /bin/sleep infinity
-! systemd-run --unit=five -p Type=exec -p User=idontexist /bin/sleep infinity
-! systemd-run --unit=six -p Type=exec /tmp/brokenbinary
-
-systemd-run --unit=seven -p KillSignal=SIGTERM -p RestartKillSignal=SIGINT -p Type=exec /bin/sleep infinity
-# Both TERM and SIGINT happen to have the same number on all architectures
-test $(systemctl show --value -p KillSignal seven.service) -eq 15
-test $(systemctl show --value -p RestartKillSignal seven.service) -eq 2
-
-systemctl restart seven.service
-systemctl stop seven.service
-
-systemd-analyze log-level info
-
-echo OK > /testok
-
-exit 0
index c9198fb6c7157bf8beeeda255e591585342b1881..e5553b0fb541f4502fef2d3556eac8b336597c5c 100755 (executable)
@@ -3,34 +3,42 @@ set -e
 TEST_DESCRIPTION="Run unit tests under containers"
 RUN_IN_UNPRIVILEGED_CONTAINER=yes
 
+# embed some newlines in the kernel command line to stress our test suite
+KERNEL_APPEND="
+
+frobnicate!
+
+$KERNEL_APPEND
+"
+
 . $TEST_BASE_DIR/test-functions
 
 check_result_nspawn() {
     local _ret=1
-    [[ -e $TESTDIR/$1/testok ]] && _ret=0
-    if [[ -s $TESTDIR/$1/failed ]]; then
+    [[ -e $1/testok ]] && _ret=0
+    if [[ -s $1/failed ]]; then
         _ret=$(($_ret+1))
         echo "=== Failed test log ==="
-        cat $TESTDIR/$1/failed
+        cat $1/failed
     else
-        if [[ -s $TESTDIR/$1/skipped ]]; then
+        if [[ -s $1/skipped ]]; then
             echo "=== Skipped test log =="
-            cat $TESTDIR/$1/skipped
+            cat $1/skipped
         fi
-        if [[ -s $TESTDIR/$1/testok ]]; then
+        if [[ -s $1/testok ]]; then
             echo "=== Passed tests ==="
-            cat $TESTDIR/$1/testok
+            cat $1/testok
         fi
     fi
-    cp -a $TESTDIR/$1/var/log/journal $TESTDIR
+    save_journal $1/var/log/journal
+    _umount_dir $initdir
     [[ -n "$TIMED_OUT" ]] && _ret=$(($_ret+1))
     return $_ret
 }
 
 check_result_qemu() {
     local _ret=1
-    mkdir -p $initdir
-    mount ${LOOPDEV}p1 $initdir
+    mount_initdir
     [[ -e $initdir/testok ]] && _ret=0
     if [[ -s $initdir/failed ]]; then
         _ret=$(($_ret+1))
@@ -46,56 +54,10 @@ check_result_qemu() {
             cat $initdir/testok
         fi
     fi
-    cp -a $initdir/var/log/journal $TESTDIR
-    umount $initdir
+    save_journal $initdir/var/log/journal
+    _umount_dir $initdir
     [[ -n "$TIMED_OUT" ]] && _ret=$(($_ret+1))
     return $_ret
 }
 
-test_setup() {
-    if type -P meson && [[ "$(meson configure $BUILD_DIR | grep install-tests | awk '{ print $2 }')" != "true" ]]; then
-        dfatal "Needs to be built with -Dinstall-tests=true"
-        exit 1
-    fi
-
-    create_empty_image_rootdir
-
-    # Create what will eventually be our root filesystem onto an overlay
-    (
-        LOG_LEVEL=5
-        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
-
-        for i in getfacl dirname basename capsh cut rev stat mktemp rmdir ionice unshare uname tr awk getent diff xzcat lz4cat; do
-            inst_binary $i
-        done
-
-        inst /etc/hosts
-
-        setup_basic_environment
-        install_keymaps yes
-        install_zoneinfo
-        # Install nproc to determine # of CPUs for correct parallelization
-        inst_binary nproc
-
-        # setup the testsuite service
-        cat >$initdir/etc/systemd/system/testsuite.service <<EOF
-[Unit]
-Description=Testsuite service
-
-[Service]
-ExecStart=/testsuite.sh
-Type=oneshot
-EOF
-        cp testsuite.sh $initdir/
-
-        setup_testsuite
-    )
-    setup_nspawn_root
-
-    # mask some services that we do not want to run in these tests
-    ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.service
-    ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.socket
-    ln -fs /dev/null $initdir/etc/systemd/system/systemd-resolved.service
-}
-
-do_test "$@"
+do_test "$@" 24
diff --git a/test/TEST-24-UNIT-TESTS/testsuite.sh b/test/TEST-24-UNIT-TESTS/testsuite.sh
deleted file mode 100755 (executable)
index cc78adb..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-#!/usr/bin/env bash
-#set -ex
-#set -o pipefail
-
-NPROC=$(nproc)
-MAX_QUEUE_SIZE=${NPROC:-2}
-IFS=$'\n' TEST_LIST=($(ls /usr/lib/systemd/tests/test-*))
-
-# Check & report test results
-# Arguments:
-#   $1: test path
-#   $2: test exit code
-function report_result() {
-    if [[ $# -ne 2 ]]; then
-        echo >&2 "check_result: missing arguments"
-        exit 1
-    fi
-
-    local name="${1##*/}"
-    local ret=$2
-
-    if [[ $ret -ne 0 && $ret != 77 ]]; then
-        echo "$name failed with $ret"
-        echo "$name" >> /failed-tests
-        {
-            echo "--- $name begin ---"
-            cat "/$name.log"
-            echo "--- $name end ---"
-        } >> /failed
-    elif [[ $ret == 77 ]]; then
-        echo "$name skipped"
-        echo "$name" >> /skipped-tests
-        {
-            echo "--- $name begin ---"
-            cat "/$name.log"
-            echo "--- $name end ---"
-        } >> /skipped
-    else
-        echo "$name OK"
-        echo "$name" >> /testok
-    fi
-
-    systemd-cat echo "--- $name ---"
-    systemd-cat cat "/$name.log"
-}
-
-# Associative array for running tasks, where running[test-path]=PID
-declare -A running=()
-for task in "${TEST_LIST[@]}"; do
-    # If there's MAX_QUEUE_SIZE running tasks, keep checking the running queue
-    # until one of the tasks finishes, so we can replace it.
-    while [[ ${#running[@]} -ge $MAX_QUEUE_SIZE ]]; do
-        for key in "${!running[@]}"; do
-            if ! kill -0 ${running[$key]} &>/dev/null; then
-                # Task has finished, report its result and drop it from the queue
-                wait ${running[$key]}
-                ec=$?
-                report_result "$key" $ec
-                unset running["$key"]
-                # Break from inner for loop and outer while loop to skip
-                # the sleep below when we find a free slot in the queue
-                break 2
-            fi
-        done
-
-        # Precisely* calculated constant to keep the spinlock from burning the CPU(s)
-        sleep 0.01
-    done
-
-    if [[ -x $task ]]; then
-        log_file="/${task##*/}.log"
-        $task &> "$log_file" &
-        running[$task]=$!
-    fi
-done
-
-# Wait for remaining running tasks
-for key in "${!running[@]}"; do
-    wait ${running[$key]}
-    ec=$?
-    report_result "$key" $ec
-    unset running["$key"]
-done
-
-exit 0
index 14265cdfc5d246766895873891d8f867ae6c40ca..034b94ca7992515df260a9c62331a94943a7b0a9 100755 (executable)
@@ -4,30 +4,4 @@ TEST_DESCRIPTION="test importd"
 
 . $TEST_BASE_DIR/test-functions
 
-test_setup() {
-    create_empty_image_rootdir
-
-    (
-        LOG_LEVEL=5
-        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
-
-        setup_basic_environment
-        dracut_install dd gunzip mv tar diff
-
-        # setup the testsuite service
-        cat >$initdir/etc/systemd/system/testsuite.service <<EOF
-[Unit]
-Description=Testsuite service
-
-[Service]
-ExecStart=/testsuite.sh
-Type=oneshot
-EOF
-        cp testsuite.sh $initdir/
-
-        setup_testsuite
-    )
-    setup_nspawn_root
-}
-
-do_test "$@"
+do_test "$@" 25
diff --git a/test/TEST-25-IMPORT/testsuite.sh b/test/TEST-25-IMPORT/testsuite.sh
deleted file mode 100755 (executable)
index 6dcb780..0000000
+++ /dev/null
@@ -1,138 +0,0 @@
-#!/usr/bin/env bash
-set -ex
-set -o pipefail
-
-export SYSTEMD_PAGER=cat
-
-dd if=/dev/urandom of=/var/tmp/testimage.raw bs=$((1024*1024+7)) count=5
-
-# Test import
-machinectl import-raw /var/tmp/testimage.raw
-machinectl image-status testimage
-test -f /var/lib/machines/testimage.raw
-cmp /var/tmp/testimage.raw /var/lib/machines/testimage.raw
-
-# Test export
-machinectl export-raw testimage /var/tmp/testimage2.raw
-cmp /var/tmp/testimage.raw /var/tmp/testimage2.raw
-rm /var/tmp/testimage2.raw
-
-# Test compressed export (gzip)
-machinectl export-raw testimage /var/tmp/testimage2.raw.gz
-gunzip /var/tmp/testimage2.raw.gz
-cmp /var/tmp/testimage.raw /var/tmp/testimage2.raw
-rm /var/tmp/testimage2.raw
-
-# Test clone
-machinectl clone testimage testimage3
-test -f /var/lib/machines/testimage3.raw
-machinectl image-status testimage3
-test -f /var/lib/machines/testimage.raw
-machinectl image-status testimage
-cmp /var/tmp/testimage.raw /var/lib/machines/testimage.raw
-cmp /var/tmp/testimage.raw /var/lib/machines/testimage3.raw
-
-# Test removal
-machinectl remove testimage
-! test -f /var/lib/machines/testimage.raw
-! machinectl image-status testimage
-
-# Test export of clone
-machinectl export-raw testimage3 /var/tmp/testimage3.raw
-cmp /var/tmp/testimage.raw /var/tmp/testimage3.raw
-rm /var/tmp/testimage3.raw
-
-# Test rename
-machinectl rename testimage3 testimage4
-test -f /var/lib/machines/testimage4.raw
-machinectl image-status testimage4
-! test -f /var/lib/machines/testimage3.raw
-! machinectl image-status testimage3
-cmp /var/tmp/testimage.raw /var/lib/machines/testimage4.raw
-
-# Test export of rename
-machinectl export-raw testimage4 /var/tmp/testimage4.raw
-cmp /var/tmp/testimage.raw /var/tmp/testimage4.raw
-rm /var/tmp/testimage4.raw
-
-# Test removal
-machinectl remove testimage4
-! test -f /var/lib/machines/testimage4.raw
-! machinectl image-status testimage4
-
-# → And now, let's test directory trees ← #
-
-# Set up a directory we can import
-mkdir /var/tmp/scratch
-mv /var/tmp/testimage.raw /var/tmp/scratch/
-touch /var/tmp/scratch/anotherfile
-mkdir /var/tmp/scratch/adirectory
-echo "piep" > /var/tmp/scratch/adirectory/athirdfile
-
-# Test import-fs
-machinectl import-fs /var/tmp/scratch/
-test -d /var/lib/machines/scratch
-machinectl image-status scratch
-
-# Test export-tar
-machinectl export-tar scratch /var/tmp/scratch.tar.gz
-test -f /var/tmp/scratch.tar.gz
-mkdir /var/tmp/extract
-(cd /var/tmp/extract ; tar xzf /var/tmp/scratch.tar.gz)
-diff -r /var/tmp/scratch/ /var/tmp/extract/
-rm -rf /var/tmp/extract
-
-# Test import-tar
-machinectl import-tar /var/tmp/scratch.tar.gz scratch2
-test -d /var/lib/machines/scratch2
-machinectl image-status scratch2
-diff -r /var/tmp/scratch/ /var/lib/machines/scratch2
-
-# Test removal
-machinectl remove scratch
-! test -f /var/lib/machines/scratch
-! machinectl image-status scratch
-
-# Test clone
-machinectl clone scratch2 scratch3
-test -d /var/lib/machines/scratch2
-machinectl image-status scratch2
-test -d /var/lib/machines/scratch3
-machinectl image-status scratch3
-diff -r /var/tmp/scratch/ /var/lib/machines/scratch3
-
-# Test removal
-machinectl remove scratch2
-! test -f /var/lib/machines/scratch2
-! machinectl image-status scratch2
-
-# Test rename
-machinectl rename scratch3 scratch4
-test -d /var/lib/machines/scratch4
-machinectl image-status scratch4
-! test -f /var/lib/machines/scratch3
-! machinectl image-status scratch3
-diff -r /var/tmp/scratch/ /var/lib/machines/scratch4
-
-# Test removal
-machinectl remove scratch4
-! test -f /var/lib/machines/scratch4
-! machinectl image-status scratch4
-
-# Test import-tar hypen/stdin pipe behavior
-cat /var/tmp/scratch.tar.gz | machinectl import-tar - scratch5
-test -d /var/lib/machines/scratch5
-machinectl image-status scratch5
-diff -r /var/tmp/scratch/ /var/lib/machines/scratch5
-
-# Test export-tar hypen/stdout pipe behavior
-mkdir -p /var/tmp/extract
-machinectl export-tar scratch5 - | tar xvf - -C /var/tmp/extract/
-diff -r /var/tmp/scratch/ /var/tmp/extract/
-rm -rf /var/tmp/extract
-
-rm -rf /var/tmp/scratch
-
-echo OK > /testok
-
-exit 0
index ee0f562277b05c2a12cf9121fa6aae7f09b623b4..158fa6fdc04ac05330256ce1a5358818c0397650 100755 (executable)
@@ -4,29 +4,4 @@ TEST_DESCRIPTION="test setenv"
 
 . $TEST_BASE_DIR/test-functions
 
-test_setup() {
-    create_empty_image_rootdir
-
-    (
-        LOG_LEVEL=5
-        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
-
-        setup_basic_environment
-
-        # setup the testsuite service
-        cat >$initdir/etc/systemd/system/testsuite.service <<EOF
-[Unit]
-Description=Testsuite service
-
-[Service]
-ExecStart=/bin/bash -x /testsuite.sh
-Type=oneshot
-EOF
-        cp testsuite.sh $initdir/
-
-        setup_testsuite
-    )
-    setup_nspawn_root
-}
-
-do_test "$@"
+do_test "$@" 26
diff --git a/test/TEST-26-SETENV/testsuite.sh b/test/TEST-26-SETENV/testsuite.sh
deleted file mode 100755 (executable)
index 89c0937..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/usr/bin/env bash
-set -ex
-set -o pipefail
-
-# Make sure PATH is set
-systemctl show-environment | grep -q '^PATH='
-
-# Let's add an entry and override a built-in one
-systemctl set-environment PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/testaddition FOO=BAR
-
-# Check that both are set
-systemctl show-environment | grep -q '^PATH=.*testaddition$'
-systemctl show-environment | grep -q '^FOO=BAR$'
-
-systemctl daemon-reload
-
-# Check again after the reload
-systemctl show-environment | grep -q '^PATH=.*testaddition$'
-systemctl show-environment | grep -q '^FOO=BAR$'
-
-# Drop both
-systemctl unset-environment FOO PATH
-
-# Check that one is gone and the other reverted to the built-in
-! (systemctl show-environment | grep -q '^FOO=$')
-! (systemctl show-environment | grep -q '^PATH=.*testaddition$')
-systemctl show-environment | grep -q '^PATH='
-
-echo OK > /testok
-
-exit 0
index 66c98e5675a01fa82de921e8bde84b4ebf577f32..23aadf314e11554327833d95a4823acbdb81e413 100755 (executable)
@@ -4,31 +4,4 @@ TEST_DESCRIPTION="test StandardOutput=file:"
 
 . $TEST_BASE_DIR/test-functions
 
-test_setup() {
-    create_empty_image_rootdir
-
-    (
-        LOG_LEVEL=5
-        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
-
-        setup_basic_environment
-        mask_supporting_services
-        inst_binary cmp
-
-        # setup the testsuite service
-        cat >$initdir/etc/systemd/system/testsuite.service <<EOF
-[Unit]
-Description=Testsuite service
-
-[Service]
-ExecStart=/testsuite.sh
-Type=oneshot
-EOF
-        cp testsuite.sh $initdir/
-
-        setup_testsuite
-    )
-    setup_nspawn_root
-}
-
-do_test "$@"
+do_test "$@" 27
diff --git a/test/TEST-27-STDOUTFILE/testsuite.sh b/test/TEST-27-STDOUTFILE/testsuite.sh
deleted file mode 100755 (executable)
index c522f75..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-#!/usr/bin/env bash
-set -ex
-set -o pipefail
-
-systemd-analyze log-level debug
-systemd-analyze log-target console
-
-systemd-run --wait --unit=one \
-            -p StandardOutput=file:/tmp/stdout \
-            -p StandardError=file:/tmp/stderr \
-            -p Type=exec \
-            sh -c 'echo x ; echo y >&2'
-cmp /tmp/stdout <<EOF
-x
-EOF
-cmp /tmp/stderr <<EOF
-y
-EOF
-
-systemd-run --wait --unit=two \
-            -p StandardOutput=file:/tmp/stdout \
-            -p StandardError=file:/tmp/stderr \
-            -p Type=exec \
-            sh -c 'echo z ; echo a >&2'
-cmp /tmp/stdout <<EOF
-z
-EOF
-cmp /tmp/stderr <<EOF
-a
-EOF
-
-systemd-run --wait --unit=three \
-            -p StandardOutput=append:/tmp/stdout \
-            -p StandardError=append:/tmp/stderr \
-            -p Type=exec \
-            sh -c 'echo b ; echo c >&2'
-cmp /tmp/stdout <<EOF
-z
-b
-EOF
-cmp /tmp/stderr <<EOF
-a
-c
-EOF
-
-systemd-analyze log-level info
-
-echo OK > /testok
-
-exit 0
index 934e1bc70c3965fe3270279f1a919ccc5bbdfad7..09baf2277683a02e67ef67340e5a14339de2e89f 100755 (executable)
@@ -5,52 +5,4 @@ RUN_IN_UNPRIVILEGED_CONTAINER=yes
 
 . $TEST_BASE_DIR/test-functions
 
-test_setup() {
-    create_empty_image_rootdir
-
-    # Create what will eventually be our root filesystem onto an overlay
-    (
-        LOG_LEVEL=5
-        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
-
-        setup_basic_environment
-        mask_supporting_services
-
-        # Set up the services.
-        cat >$initdir/etc/systemd/system/specifier-j-wants.service << EOF
-[Unit]
-Description=Wants with percent-j specifier
-Wants=specifier-j-depends-%j.service
-After=specifier-j-depends-%j.service
-
-[Service]
-Type=oneshot
-ExecStart=test -f /tmp/test-specifier-j-%j
-ExecStart=/bin/sh -c 'echo OK > /testok'
-EOF
-        cat >$initdir/etc/systemd/system/specifier-j-depends-wants.service << EOF
-[Unit]
-Description=Dependent service for percent-j specifier
-
-[Service]
-Type=oneshot
-ExecStart=touch /tmp/test-specifier-j-wants
-EOF
-        cat >$initdir/etc/systemd/system/testsuite.service << EOF
-[Unit]
-Description=Testsuite: Ensure %j Wants directives work
-Wants=specifier-j-wants.service
-After=specifier-j-wants.service
-
-[Service]
-Type=oneshot
-ExecStart=/bin/true
-EOF
-
-        setup_testsuite
-    )
-
-    setup_nspawn_root
-}
-
-do_test "$@"
+do_test "$@" 28
index fb570b034701fecc5a2e1e1d34e04ea3846faddb..4feafc04d708246c4c659180e9bfa65d666c7075 100755 (executable)
@@ -6,29 +6,4 @@ TEST_NO_NSPAWN=1
 . $TEST_BASE_DIR/test-functions
 QEMU_TIMEOUT=300
 
-test_setup() {
-    create_empty_image_rootdir
-
-    (
-        LOG_LEVEL=5
-        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
-
-        setup_basic_environment
-        mask_supporting_services
-
-        # setup the testsuite service
-        cat >$initdir/etc/systemd/system/testsuite.service <<EOF
-[Unit]
-Description=Testsuite service
-
-[Service]
-ExecStart=/bin/bash -x /testsuite.sh
-Type=oneshot
-EOF
-        cp testsuite.sh $initdir/
-
-        setup_testsuite
-    )
-}
-
-do_test "$@"
+do_test "$@" 29
diff --git a/test/TEST-29-UDEV-ID_RENAMING/testsuite.sh b/test/TEST-29-UDEV-ID_RENAMING/testsuite.sh
deleted file mode 100755 (executable)
index 5abdb53..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env bash
-set -ex
-set -o pipefail
-
-mkdir -p /run/udev/rules.d/
-
-cat > /run/udev/rules.d/50-testsuite.rules <<EOF
-ACTION=="remove", GOTO="lo_end"
-
-SUBSYSTEM=="net", KERNEL=="lo", TAG+="systemd", ENV{SYSTEMD_ALIAS}+="/sys/subsystem/net/devices/lo"
-
-ACTION!="change", GOTO="lo_end"
-
-SUBSYSTEM=="net", KERNEL=="lo", ENV{ID_RENAMING}="1"
-
-LABEL="lo_end"
-EOF
-
-udevadm control --log-priority=debug --reload --timeout=600
-udevadm trigger --action=add --settle /sys/devices/virtual/net/lo
-udevadm info /sys/devices/virtual/net/lo
-sleep 1
-STATE=$(systemctl show --property=ActiveState --value sys-devices-virtual-net-lo.device)
-[[ $STATE == "active" ]] || exit 1
-
-udevadm trigger --action=change --settle /sys/devices/virtual/net/lo
-udevadm info /sys/devices/virtual/net/lo
-sleep 1
-STATE=$(systemctl show --property=ActiveState --value sys-devices-virtual-net-lo.device)
-[[ $STATE == "inactive" ]] || exit 1
-
-udevadm trigger --action=move --settle /sys/devices/virtual/net/lo
-udevadm info /sys/devices/virtual/net/lo
-sleep 1
-STATE=$(systemctl show --property=ActiveState --value sys-devices-virtual-net-lo.device)
-[[ $STATE == "active" ]] || exit 1
-
-rm -f /run/udev/rules.d/50-testsuite.rules
-udevadm control --reload --timeout=600
-
-echo OK > /testok
-
-exit 0
index 9e2c11238c7a505556197988654e57f67f3db6de..4723e7b0be81921cb3b9edfcefb59d40f8844a4e 100755 (executable)
@@ -2,42 +2,6 @@
 set -e
 TEST_DESCRIPTION="test OnClockChange= + OnTimezoneChange="
 TEST_NO_NSPAWN=1
-
 . $TEST_BASE_DIR/test-functions
 
-test_setup() {
-    create_empty_image_rootdir
-
-    (
-        LOG_LEVEL=5
-        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
-
-        inst_any /usr/share/zoneinfo/Europe/Kiev
-        inst_any /usr/share/zoneinfo/Europe/Berlin
-
-        setup_basic_environment
-        mask_supporting_services
-
-        # extend the watchdog
-        mkdir -p $initdir/etc/systemd/system/systemd-timedated.service.d
-        cat >$initdir/etc/systemd/system/systemd-timedated.service.d/watchdog.conf <<EOF
-[Service]
-WatchdogSec=10min
-EOF
-
-        # setup the testsuite service
-        cat >$initdir/etc/systemd/system/testsuite.service <<EOF
-[Unit]
-Description=Testsuite service
-
-[Service]
-ExecStart=/testsuite.sh
-Type=oneshot
-EOF
-        cp testsuite.sh $initdir/
-
-        setup_testsuite
-    )
-}
-
-do_test "$@"
+do_test "$@" 30
diff --git a/test/TEST-30-ONCLOCKCHANGE/testsuite.sh b/test/TEST-30-ONCLOCKCHANGE/testsuite.sh
deleted file mode 100755 (executable)
index a507ffc..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/usr/bin/env bash
-set -ex
-set -o pipefail
-
-systemd-analyze log-level debug
-systemd-analyze log-target console
-
-systemctl disable --now systemd-timesyncd.service
-
-timedatectl set-timezone Europe/Berlin
-timedatectl set-time 1980-10-15
-
-systemd-run --on-timezone-change touch /tmp/timezone-changed
-systemd-run --on-clock-change touch /tmp/clock-changed
-
-! test -f /tmp/timezone-changed
-! test -f /tmp/clock-changed
-
-timedatectl set-timezone Europe/Kiev
-
-while ! test -f /tmp/timezone-changed ; do sleep .5 ; done
-
-timedatectl set-time 2018-1-1
-
-while ! test -f /tmp/clock-changed ; do sleep .5 ; done
-
-systemd-analyze log-level info
-
-echo OK > /testok
-
-exit 0
index 7bc9298dc12c6720853889356c574549869a9d51..4fbd38d2d0dadf520dfcd5315952c86e547dfb6b 100755 (executable)
@@ -6,29 +6,4 @@ TEST_NO_NSPAWN=1
 . $TEST_BASE_DIR/test-functions
 QEMU_TIMEOUT=300
 
-test_setup() {
-    create_empty_image_rootdir
-
-    (
-        LOG_LEVEL=5
-        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
-
-        setup_basic_environment
-        mask_supporting_services
-
-        # setup the testsuite service
-        cat >$initdir/etc/systemd/system/testsuite.service <<EOF
-[Unit]
-Description=Testsuite service
-
-[Service]
-ExecStart=/bin/bash -x /testsuite.sh
-Type=oneshot
-EOF
-        cp testsuite.sh $initdir/
-
-        setup_testsuite
-    )
-}
-
-do_test "$@"
+do_test "$@" 31
diff --git a/test/TEST-31-DEVICE-ENUMERATION/testsuite.sh b/test/TEST-31-DEVICE-ENUMERATION/testsuite.sh
deleted file mode 100755 (executable)
index fcff82d..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/usr/bin/env bash
-set -e
-set -o pipefail
-
-if journalctl -b -t systemd --grep '\.device: Changed plugged -> dead'; then
-    exit 1
-fi
-
-echo OK > /testok
-exit 0
index 36aad419eac784f13e618c971b82ec1346079f42..6f2955cf62c303552d5c3b47fe33a518034a9b88 100755 (executable)
@@ -2,35 +2,8 @@
 set -e
 TEST_DESCRIPTION="test OOM killer logic"
 TEST_NO_NSPAWN=1
-
 . $TEST_BASE_DIR/test-functions
 
 UNIFIED_CGROUP_HIERARCHY=yes
 
-test_setup() {
-    create_empty_image_rootdir
-
-    (
-        LOG_LEVEL=5
-        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
-
-        setup_basic_environment
-        mask_supporting_services
-
-        # setup the testsuite service
-        cat >$initdir/etc/systemd/system/testsuite.service <<EOF
-[Unit]
-Description=Testsuite service
-
-[Service]
-ExecStart=/testsuite.sh
-Type=oneshot
-MemoryAccounting=yes
-EOF
-        cp testsuite.sh $initdir/
-
-        setup_testsuite
-    )
-}
-
-do_test "$@"
+do_test "$@" 32
diff --git a/test/TEST-32-OOMPOLICY/testsuite.sh b/test/TEST-32-OOMPOLICY/testsuite.sh
deleted file mode 100755 (executable)
index aafafc1..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/usr/bin/env bash
-set -ex
-set -o pipefail
-
-# Let's run this test only if the "memory.oom.group" cgroupfs attribute
-# exists. This test is a bit too strict, since the "memory.events"/"oom_kill"
-# logic has been around since a longer time than "memory.oom.group", but it's
-# an easier thing to test for, and also: let's not get confused by older
-# kernels where the concept was still new.
-
-if test -f /sys/fs/cgroup/system.slice/testsuite.service/memory.oom.group ; then
-
-    systemd-analyze log-level debug
-    systemd-analyze log-target console
-
-    # Run a service that is guaranteed to be the first candidate for OOM killing
-    systemd-run --unit=oomtest.service -p Type=exec -p OOMScoreAdjust=1000 -p OOMPolicy=stop -p MemoryAccounting=yes /bin/sleep infinity
-
-    # Trigger an OOM killer run
-    echo 1 > /proc/sys/kernel/sysrq
-    echo f > /proc/sysrq-trigger
-
-    while : ; do
-        STATE=`systemctl show -p ActiveState --value oomtest.service`
-        [ "$STATE" = "failed" ] && break
-        sleep .5
-    done
-
-    RESULT=`systemctl show -p Result --value oomtest.service`
-    test "$RESULT" = "oom-kill"
-
-    systemd-analyze log-level info
-fi
-
-echo OK > /testok
-
-exit 0
index 310cf7b1457b890d220e2812c5ae2a40602697b2..64cb6aee5938b4e9b548185cfba711dbaa2dc18c 100755 (executable)
@@ -3,33 +3,6 @@
 # ex: ts=8 sw=4 sts=4 et filetype=sh
 set -e
 TEST_DESCRIPTION="test CleanUnit"
-
 . $TEST_BASE_DIR/test-functions
 
-test_setup() {
-    create_empty_image_rootdir
-
-    (
-        LOG_LEVEL=5
-        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
-
-        setup_basic_environment
-        mask_supporting_services
-
-        # setup the testsuite service
-        cat >$initdir/etc/systemd/system/testsuite.service <<EOF
-[Unit]
-Description=Testsuite service
-
-[Service]
-ExecStart=/bin/bash -x /testsuite.sh
-Type=oneshot
-EOF
-        cp testsuite.sh $initdir/
-
-        setup_testsuite
-    ) || return 1
-    setup_nspawn_root
-}
-
-do_test "$@"
+do_test "$@" 33
diff --git a/test/TEST-33-CLEAN-UNIT/testsuite.sh b/test/TEST-33-CLEAN-UNIT/testsuite.sh
deleted file mode 100755 (executable)
index 0a6ee57..0000000
+++ /dev/null
@@ -1,319 +0,0 @@
-#!/usr/bin/env bash
-# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
-# ex: ts=8 sw=4 sts=4 et filetype=sh
-set -ex
-set -o pipefail
-
-cat > /etc/systemd/system/testservice.service <<EOF
-[Service]
-ConfigurationDirectory=testservice
-RuntimeDirectory=testservice
-StateDirectory=testservice
-CacheDirectory=testservice
-LogsDirectory=testservice
-RuntimeDirectoryPreserve=yes
-ExecStart=/bin/sleep infinity
-Type=exec
-EOF
-
-systemctl daemon-reload
-
-! test -e /etc/testservice
-! test -e /run/testservice
-! test -e /var/lib/testservice
-! test -e /var/cache/testservice
-! test -e /var/log/testservice
-
-systemctl start testservice
-
-test -d /etc/testservice
-test -d /run/testservice
-test -d /var/lib/testservice
-test -d /var/cache/testservice
-test -d /var/log/testservice
-
-! systemctl clean testservice
-
-systemctl stop testservice
-
-test -d /etc/testservice
-test -d /run/testservice
-test -d /var/lib/testservice
-test -d /var/cache/testservice
-test -d /var/log/testservice
-
-systemctl clean testservice --what=configuration
-
-! test -e /etc/testservice
-test -d /run/testservice
-test -d /var/lib/testservice
-test -d /var/cache/testservice
-test -d /var/log/testservice
-
-systemctl clean testservice
-
-! test -e /etc/testservice
-! test -e /run/testservice
-test -d /var/lib/testservice
-! test -e /var/cache/testservice
-test -d /var/log/testservice
-
-systemctl clean testservice --what=logs
-
-! test -e /etc/testservice
-! test -e /run/testservice
-test -d /var/lib/testservice
-! test -e /var/cache/testservice
-! test -e /var/log/testservice
-
-systemctl clean testservice --what=all
-
-! test -e /etc/testservice
-! test -e /run/testservice
-! test -e /var/lib/testservice
-! test -e /var/cache/testservice
-! test -e /var/log/testservice
-
-cat > /etc/systemd/system/testservice.service <<EOF
-[Service]
-DynamicUser=yes
-ConfigurationDirectory=testservice
-RuntimeDirectory=testservice
-StateDirectory=testservice
-CacheDirectory=testservice
-LogsDirectory=testservice
-RuntimeDirectoryPreserve=yes
-ExecStart=/bin/sleep infinity
-Type=exec
-EOF
-
-systemctl daemon-reload
-
-! test -e /etc/testservice
-! test -e /run/testservice
-! test -e /var/lib/testservice
-! test -e /var/cache/testservice
-! test -e /var/log/testservice
-
-systemctl restart testservice
-
-test -d /etc/testservice
-test -d /run/private/testservice
-test -d /var/lib/private/testservice
-test -d /var/cache/private/testservice
-test -d /var/log/private/testservice
-test -L /run/testservice
-test -L /var/lib/testservice
-test -L /var/cache/testservice
-test -L /var/log/testservice
-
-! systemctl clean testservice
-
-systemctl stop testservice
-
-test -d /etc/testservice
-test -d /run/private/testservice
-test -d /var/lib/private/testservice
-test -d /var/cache/private/testservice
-test -d /var/log/private/testservice
-test -L /run/testservice
-test -L /var/lib/testservice
-test -L /var/cache/testservice
-test -L /var/log/testservice
-
-systemctl clean testservice --what=configuration
-
-! test -d /etc/testservice
-test -d /run/private/testservice
-test -d /var/lib/private/testservice
-test -d /var/cache/private/testservice
-test -d /var/log/private/testservice
-test -L /run/testservice
-test -L /var/lib/testservice
-test -L /var/cache/testservice
-test -L /var/log/testservice
-
-systemctl clean testservice
-
-! test -d /etc/testservice
-! test -d /run/private/testservice
-test -d /var/lib/private/testservice
-! test -d /var/cache/private/testservice
-test -d /var/log/private/testservice
-! test -L /run/testservice
-test -L /var/lib/testservice
-! test -L /var/cache/testservice
-test -L /var/log/testservice
-
-systemctl clean testservice --what=logs
-
-! test -d /etc/testservice
-! test -d /run/private/testservice
-test -d /var/lib/private/testservice
-! test -d /var/cache/private/testservice
-! test -d /var/log/private/testservice
-! test -L /run/testservice
-test -L /var/lib/testservice
-! test -L /var/cache/testservice
-! test -L /var/log/testservice
-
-systemctl clean testservice --what=all
-
-! test -d /etc/testservice
-! test -d /run/private/testservice
-! test -d /var/lib/private/testservice
-! test -d /var/cache/private/testservice
-! test -d /var/log/private/testservice
-! test -L /run/testservice
-! test -L /var/lib/testservice
-! test -L /var/cache/testservice
-! test -L /var/log/testservice
-
-cat > /etc/systemd/system/tmp-hoge.mount <<EOF
-[Mount]
-What=tmpfs
-Type=tmpfs
-ConfigurationDirectory=hoge
-RuntimeDirectory=hoge
-StateDirectory=hoge
-CacheDirectory=hoge
-LogsDirectory=hoge
-EOF
-
-systemctl daemon-reload
-
-! test -e /etc/hoge
-! test -e /run/hoge
-! test -e /var/lib/hoge
-! test -e /var/cache/hoge
-! test -e /var/log/hoge
-
-systemctl start tmp-hoge.mount
-
-test -d /etc/hoge
-test -d /run/hoge
-test -d /var/lib/hoge
-test -d /var/cache/hoge
-test -d /var/log/hoge
-
-! systemctl clean tmp-hoge.mount
-
-test -d /etc/hoge
-test -d /run/hoge
-test -d /var/lib/hoge
-test -d /var/cache/hoge
-test -d /var/log/hoge
-
-systemctl stop tmp-hoge.mount
-
-test -d /etc/hoge
-! test -d /run/hoge
-test -d /var/lib/hoge
-test -d /var/cache/hoge
-test -d /var/log/hoge
-
-systemctl clean tmp-hoge.mount --what=configuration
-
-! test -d /etc/hoge
-! test -d /run/hoge
-test -d /var/lib/hoge
-test -d /var/cache/hoge
-test -d /var/log/hoge
-
-systemctl clean tmp-hoge.mount
-
-! test -d /etc/hoge
-! test -d /run/hoge
-test -d /var/lib/hoge
-! test -d /var/cache/hoge
-test -d /var/log/hoge
-
-systemctl clean tmp-hoge.mount --what=logs
-
-! test -d /etc/hoge
-! test -d /run/hoge
-test -d /var/lib/hoge
-! test -d /var/cache/hoge
-! test -d /var/log/hoge
-
-systemctl clean tmp-hoge.mount --what=all
-
-! test -d /etc/hoge
-! test -d /run/hoge
-! test -d /var/lib/hoge
-! test -d /var/cache/hoge
-! test -d /var/log/hoge
-
-cat > /etc/systemd/system/testservice.socket <<EOF
-[Socket]
-ListenSequentialPacket=/run/testservice.socket
-RemoveOnStop=yes
-ExecStartPre=true
-ConfigurationDirectory=testsocket
-RuntimeDirectory=testsocket
-StateDirectory=testsocket
-CacheDirectory=testsocket
-LogsDirectory=testsocket
-EOF
-
-systemctl daemon-reload
-
-! test -e /etc/testsocket
-! test -e /run/testsocket
-! test -e /var/lib/testsocket
-! test -e /var/cache/testsocket
-! test -e /var/log/testsocket
-
-systemctl start testservice.socket
-
-test -d /etc/testsocket
-! test -d /run/testsocket
-test -d /var/lib/testsocket
-test -d /var/cache/testsocket
-test -d /var/log/testsocket
-
-! systemctl clean testservice.socket
-
-systemctl stop testservice.socket
-
-test -d /etc/testsocket
-! test -d /run/testsocket
-test -d /var/lib/testsocket
-test -d /var/cache/testsocket
-test -d /var/log/testsocket
-
-systemctl clean testservice.socket --what=configuration
-
-! test -e /etc/testsocket
-! test -d /run/testsocket
-test -d /var/lib/testsocket
-test -d /var/cache/testsocket
-test -d /var/log/testsocket
-
-systemctl clean testservice.socket
-
-! test -e /etc/testsocket
-! test -e /run/testsocket
-test -d /var/lib/testsocket
-! test -e /var/cache/testsocket
-test -d /var/log/testsocket
-
-systemctl clean testservice.socket --what=logs
-
-! test -e /etc/testsocket
-! test -e /run/testsocket
-test -d /var/lib/testsocket
-! test -e /var/cache/testsocket
-! test -e /var/log/testsocket
-
-systemctl clean testservice.socket --what=all
-
-! test -e /etc/testsocket
-! test -e /run/testsocket
-! test -e /var/lib/testsocket
-! test -e /var/cache/testsocket
-! test -e /var/log/testsocket
-
-echo OK > /testok
-
-exit 0
index ad299df42053c559b76331f9e001a9a15f7a0425..0b2174a894b0b15a0c0dd6d889420f042e5afb0a 100755 (executable)
@@ -1,33 +1,6 @@
 #!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="test migrating state directory from DynamicUser=1 to DynamicUser=0 and back"
-
 . $TEST_BASE_DIR/test-functions
 
-test_setup() {
-    create_empty_image_rootdir
-
-    (
-        LOG_LEVEL=5
-        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
-
-        setup_basic_environment
-        mask_supporting_services
-
-        # setup the testsuite service
-        cat >$initdir/etc/systemd/system/testsuite.service <<EOF
-[Unit]
-Description=Testsuite service
-
-[Service]
-ExecStart=/bin/bash -x /testsuite.sh
-Type=oneshot
-EOF
-        cp testsuite.sh $initdir/
-
-        setup_testsuite
-    )
-    setup_nspawn_root
-}
-
-do_test "$@"
+do_test "$@" 34
diff --git a/test/TEST-34-DYNAMICUSERMIGRATE/testsuite.sh b/test/TEST-34-DYNAMICUSERMIGRATE/testsuite.sh
deleted file mode 100755 (executable)
index 6d94886..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/usr/bin/env bash
-set -ex
-set -o pipefail
-
-systemd-analyze log-level debug
-systemd-analyze log-target console
-
-# Set everything up without DynamicUser=1
-
-systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz touch /var/lib/zzz/test
-systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz test -f /var/lib/zzz/test
-! systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz test -f /var/lib/zzz/test-missing
-
-test -d /var/lib/zzz
-! test -L /var/lib/zzz
-! test -e /var/lib/private/zzz
-test -f /var/lib/zzz/test
-! test -f /var/lib/zzz/test-missing
-
-# Convert to DynamicUser=1
-
-systemd-run --wait -p DynamicUser=1 -p StateDirectory=zzz test -f /var/lib/zzz/test
-! systemd-run --wait -p DynamicUser=1 -p StateDirectory=zzz test -f /var/lib/zzz/test-missing
-
-test -L /var/lib/zzz
-test -d /var/lib/private/zzz
-
-test -f /var/lib/zzz/test
-! test -f /var/lib/zzz/test-missing
-
-# Convert back
-
-systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz test -f /var/lib/zzz/test
-! systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz test -f /var/lib/zzz/test-missing
-
-test -d /var/lib/zzz
-! test -L /var/lib/zzz
-! test -e /var/lib/private/zzz
-test -f /var/lib/zzz/test
-! test -f /var/lib/zzz/test-missing
-
-systemd-analyze log-level info
-
-echo OK > /testok
-
-exit 0
diff --git a/test/TEST-35-NETWORK-GENERATOR/Makefile b/test/TEST-35-NETWORK-GENERATOR/Makefile
deleted file mode 120000 (symlink)
index e9f93b1..0000000
+++ /dev/null
@@ -1 +0,0 @@
-../TEST-01-BASIC/Makefile
\ No newline at end of file
diff --git a/test/TEST-35-NETWORK-GENERATOR/test-01-dhcp.expected/91-default.network b/test/TEST-35-NETWORK-GENERATOR/test-01-dhcp.expected/91-default.network
deleted file mode 100644 (file)
index e42ce1e..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-# Automatically generated by systemd-network-generator
-
-[Match]
-Name=*
-
-[Link]
-
-[Network]
-DHCP=ipv4
-
-[DHCP]
diff --git a/test/TEST-35-NETWORK-GENERATOR/test-01-dhcp.input b/test/TEST-35-NETWORK-GENERATOR/test-01-dhcp.input
deleted file mode 100644 (file)
index e55893e..0000000
+++ /dev/null
@@ -1 +0,0 @@
-ip=dhcp
diff --git a/test/TEST-35-NETWORK-GENERATOR/test-02-bridge.expected/90-bridge99.netdev b/test/TEST-35-NETWORK-GENERATOR/test-02-bridge.expected/90-bridge99.netdev
deleted file mode 100644 (file)
index 97c2248..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-# Automatically generated by systemd-network-generator
-
-[NetDev]
-Kind=bridge
-Name=bridge99
diff --git a/test/TEST-35-NETWORK-GENERATOR/test-02-bridge.expected/90-bridge99.network b/test/TEST-35-NETWORK-GENERATOR/test-02-bridge.expected/90-bridge99.network
deleted file mode 100644 (file)
index f8d19ba..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-# Automatically generated by systemd-network-generator
-
-[Match]
-Name=bridge99
-
-[Link]
-MACAddress=00:11:22:33:44:55
-MTUBytes=1530
-
-[Network]
-DHCP=ipv4
-
-[DHCP]
diff --git a/test/TEST-35-NETWORK-GENERATOR/test-02-bridge.expected/90-eth0.network b/test/TEST-35-NETWORK-GENERATOR/test-02-bridge.expected/90-eth0.network
deleted file mode 100644 (file)
index 8842b57..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-# Automatically generated by systemd-network-generator
-
-[Match]
-Name=eth0
-
-[Link]
-
-[Network]
-DHCP=no
-DNS=10.10.10.10
-DNS=10.10.10.11
-Bridge=bridge99
-
-[DHCP]
-Hostname=hogehoge
-
-[Address]
-Address=192.168.0.10/24
-
-[Route]
-Gateway=192.168.0.1
diff --git a/test/TEST-35-NETWORK-GENERATOR/test-02-bridge.expected/90-eth1.network b/test/TEST-35-NETWORK-GENERATOR/test-02-bridge.expected/90-eth1.network
deleted file mode 100644 (file)
index feff4f5..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-# Automatically generated by systemd-network-generator
-
-[Match]
-Name=eth1
-
-[Link]
-
-[Network]
-DHCP=no
-DNS=10.10.10.10
-DNS=10.10.10.11
-Bridge=bridge99
-
-[DHCP]
-Hostname=hogehoge
-
-[Address]
-Address=192.168.0.11/24
-
-[Route]
-Gateway=192.168.0.1
diff --git a/test/TEST-35-NETWORK-GENERATOR/test-02-bridge.input b/test/TEST-35-NETWORK-GENERATOR/test-02-bridge.input
deleted file mode 100644 (file)
index 0c863fc..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-ip=192.168.0.10::192.168.0.1:255.255.255.0:hogehoge:eth0:off:10.10.10.10:10.10.10.11
-ip=192.168.0.11::192.168.0.1:255.255.255.0:hogehoge:eth1:off:10.10.10.10:10.10.10.11
-ip=bridge99:dhcp:1530:00:11:22:33:44:55
-bridge=bridge99:eth0,eth1
diff --git a/test/TEST-35-NETWORK-GENERATOR/test-03-issue-14319.expected/90-enp3s0.network b/test/TEST-35-NETWORK-GENERATOR/test-03-issue-14319.expected/90-enp3s0.network
deleted file mode 100644 (file)
index 28ccfdd..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-# Automatically generated by systemd-network-generator
-
-[Match]
-Name=enp3s0
-
-[Link]
-
-[Network]
-DHCP=no
-
-[DHCP]
-
-[Address]
-Address=10.99.37.44/16
-
-[Route]
-Gateway=10.99.10.1
diff --git a/test/TEST-35-NETWORK-GENERATOR/test-03-issue-14319.input b/test/TEST-35-NETWORK-GENERATOR/test-03-issue-14319.input
deleted file mode 100644 (file)
index 3be7520..0000000
+++ /dev/null
@@ -1 +0,0 @@
-root=/dev/nfs nfsroot=10.99.37.240:/srv/netroot,v3,tcp ip=10.99.37.44::10.99.10.1:255.255.0.0::enp3s0:off
diff --git a/test/TEST-35-NETWORK-GENERATOR/test.sh b/test/TEST-35-NETWORK-GENERATOR/test.sh
deleted file mode 100755 (executable)
index 7853281..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/usr/bin/env bash
-set -e
-TEST_DESCRIPTION="network-generator tests"
-
-. $TEST_BASE_DIR/test-functions
-
-test_setup() {
-    mkdir -p $TESTDIR/run/systemd/network
-}
-
-test_run() {
-    local generator
-
-    if [[ -x $BUILD_DIR/systemd-network-generator ]]; then
-        generator=$BUILD_DIR/systemd-network-generator
-    elif [[ -x /usr/lib/systemd/systemd-network-generator ]]; then
-        generator=/usr/lib/systemd/systemd-network-generator
-    elif [[ -x /lib/systemd/systemd-network-generator ]]; then
-        generator=/lib/systemd/systemd-network-generator
-    else
-        exit 1
-    fi
-
-    for f in test-*.input; do
-        echo "*** Running $f"
-        rm -f $TESTDIR/run/systemd/network/*
-        $generator --root $TESTDIR -- $(cat $f)
-
-        if ! diff -u $TESTDIR/run/systemd/network ${f%.input}.expected; then
-            echo "**** Unexpected output for $f"
-            exit 1
-        fi
-    done
-}
-
-do_test "$@"
index 29addc958b3eb5a337a23761f7c9df9ab5cd1933..02f013568cd44f532c873984b9f5b26ec9725140 100755 (executable)
@@ -4,34 +4,6 @@ set -e
 TEST_DESCRIPTION="test MUMAPolicy= and NUMAMask= options"
 TEST_NO_NSPAWN=1
 QEMU_OPTIONS="-numa node,nodeid=0"
-
 . $TEST_BASE_DIR/test-functions
 
-test_setup() {
-    create_empty_image_rootdir
-
-    (
-        LOG_LEVEL=5
-        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
-
-        setup_basic_environment
-        mask_supporting_services
-        dracut_install mktemp
-
-        # setup the testsuite service
-        cat >$initdir/etc/systemd/system/testsuite.service <<EOF
-[Unit]
-Description=Testsuite service
-
-[Service]
-ExecStart=/bin/bash -x /testsuite.sh
-Type=oneshot
-EOF
-        cp testsuite.sh $initdir/
-
-        setup_testsuite
-    )
-    setup_nspawn_root
-}
-
-do_test "$@"
+do_test "$@" 36
diff --git a/test/TEST-36-NUMAPOLICY/testsuite.sh b/test/TEST-36-NUMAPOLICY/testsuite.sh
deleted file mode 100755 (executable)
index b57b4a2..0000000
+++ /dev/null
@@ -1,341 +0,0 @@
-#!/usr/bin/env bash
-set -ex
-set -o pipefail
-
-at_exit() {
-    if [ $? -ne 0 ]; then
-        # We're exiting with a non-zero EC, let's dump test artifacts
-        # for easier debugging
-        [ -f "$straceLog" ] && cat "$straceLog"
-        [ -f "$journalLog" ] && cat "$journalLog"
-    fi
-}
-
-trap at_exit EXIT
-
-systemd-analyze log-level debug
-systemd-analyze log-target journal
-
-# Log files
-straceLog='strace.log'
-journalLog='journal.log'
-
-# Systemd config files
-testUnit='numa-test.service'
-testUnitFile="/etc/systemd/system/$testUnit"
-testUnitNUMAConf="$testUnitFile.d/numa.conf"
-
-# Sleep constants (we should probably figure out something better but nothing comes to mind)
-journalSleep=5
-sleepAfterStart=1
-
-# Journal cursor for easier navigation
-journalCursorFile="jounalCursorFile"
-
-startStrace() {
-    coproc strace -qq -p 1 -o $straceLog -e set_mempolicy -s 1024 $1
-    # Wait for strace to properly "initialize"
-    sleep $sleepAfterStart
-}
-
-stopStrace() {
-    kill -s TERM $COPROC_PID
-    # Make sure the strace process is indeed dead
-    while kill -0 $COPROC_PID 2>/dev/null; do sleep 0.1; done
-}
-
-startJournalctl() {
-    # Save journal's cursor for later navigation
-    journalctl --no-pager --cursor-file="$journalCursorFile" -n0 -ocat
-}
-
-stopJournalctl() {
-    local unit="${1:-init.scope}"
-    # Using journalctl --sync should be better than using SIGRTMIN+1, as
-    # the --sync wait until the synchronization is complete
-    echo "Force journald to write all queued messages"
-    journalctl --sync
-    journalctl -u $unit --cursor-file="$journalCursorFile" > "$journalLog"
-}
-
-checkNUMA() {
-    # NUMA enabled system should have at least NUMA node0
-    test -e /sys/devices/system/node/node0
-}
-
-writePID1NUMAPolicy() {
-    echo [Manager] > $confDir/numa.conf
-    echo NUMAPolicy=$1 >> $confDir/numa.conf
-    echo NUMAMask=$2>> $confDir/numa.conf
-}
-
-writeTestUnit() {
-    echo [Service] > $testUnitFile
-    echo ExecStart=/bin/sleep 3600 >> $testUnitFile
-    mkdir -p $testUnitFile.d/
-}
-
-writeTestUnitNUMAPolicy() {
-    echo [Service] > $testUnitNUMAConf
-    echo NUMAPolicy=$1 >> $testUnitNUMAConf
-    echo NUMAMask=$2>> $testUnitNUMAConf
-    systemctl daemon-reload
-}
-
-pid1ReloadWithStrace() {
-    startStrace
-    systemctl daemon-reload
-    sleep $sleepAfterStart
-    stopStrace
-}
-
-pid1ReloadWithJournal() {
-    startJournalctl
-    systemctl daemon-reload
-    stopJournalctl
-}
-
-pid1StartUnitWithStrace() {
-    startStrace '-f'
-    systemctl start $1
-    sleep $sleepAfterStart
-    stopStrace
-}
-
-pid1StartUnitWithJournal() {
-    startJournalctl
-    systemctl start $1
-    sleep $sleepAfterStart
-    stopJournalctl
-}
-
-pid1StopUnit() {
-    systemctl stop $1
-}
-
-systemctlCheckNUMAProperties() {
-    local LOGFILE="$(mktemp)"
-    systemctl show -p NUMAPolicy $1 > "$LOGFILE"
-    grep "NUMAPolicy=$2" "$LOGFILE"
-
-    > "$LOGFILE"
-
-    if [ -n "$3" ]; then
-        systemctl show -p NUMAMask $1 > "$LOGFILE"
-        grep "NUMAMask=$3" "$LOGFILE"
-    fi
-}
-
-writeTestUnit
-
-# Create systemd config drop-in directory
-confDir="/etc/systemd/system.conf.d/"
-mkdir -p "$confDir"
-
-if ! checkNUMA; then
-    echo >&2 "NUMA is not supported on this machine, switching to a simple sanity check"
-
-    echo "PID1 NUMAPolicy=default && NUMAMask=0 check without NUMA support"
-    writePID1NUMAPolicy "default" "0"
-    startJournalctl
-    systemctl daemon-reload
-    stopJournalctl
-    grep "NUMA support not available, ignoring" "$journalLog"
-
-    echo "systemd-run NUMAPolicy=default && NUMAMask=0 check without NUMA support"
-    runUnit='numa-systemd-run-test.service'
-    startJournalctl
-    systemd-run -p NUMAPolicy=default -p NUMAMask=0 --unit $runUnit sleep 1000
-    sleep $sleepAfterStart
-    pid1StopUnit $runUnit
-    stopJournalctl $runUnit
-    grep "NUMA support not available, ignoring" "$journalLog"
-
-else
-    echo "PID1 NUMAPolicy support - Default policy w/o mask"
-    writePID1NUMAPolicy "default"
-    pid1ReloadWithStrace
-    # Kernel requires that nodemask argument is set to NULL when setting default policy
-    grep "set_mempolicy(MPOL_DEFAULT, NULL" $straceLog
-
-    echo "PID1 NUMAPolicy support - Default policy w/ mask"
-    writePID1NUMAPolicy "default" "0"
-    pid1ReloadWithStrace
-    grep "set_mempolicy(MPOL_DEFAULT, NULL" $straceLog
-
-    echo "PID1 NUMAPolicy support - Bind policy w/o mask"
-    writePID1NUMAPolicy "bind"
-    pid1ReloadWithJournal
-    grep "Failed to set NUMA memory policy: Invalid argument" $journalLog
-
-    echo "PID1 NUMAPolicy support - Bind policy w/ mask"
-    writePID1NUMAPolicy "bind" "0"
-    pid1ReloadWithStrace
-    grep -P "set_mempolicy\(MPOL_BIND, \[0x0*1\]" $straceLog
-
-    echo "PID1 NUMAPolicy support - Interleave policy w/o mask"
-    writePID1NUMAPolicy "interleave"
-    pid1ReloadWithJournal
-    grep "Failed to set NUMA memory policy: Invalid argument" $journalLog
-
-    echo "PID1 NUMAPolicy support - Interleave policy w/ mask"
-    writePID1NUMAPolicy "interleave" "0"
-    pid1ReloadWithStrace
-    grep -P "set_mempolicy\(MPOL_INTERLEAVE, \[0x0*1\]" $straceLog
-
-    echo "PID1 NUMAPolicy support - Preferred policy w/o mask"
-    writePID1NUMAPolicy "preferred"
-    pid1ReloadWithJournal
-    # Preferred policy with empty node mask is actually allowed and should reset allocation policy to default
-    ! grep "Failed to set NUMA memory policy: Invalid argument" $journalLog
-
-    echo "PID1 NUMAPolicy support - Preferred policy w/ mask"
-    writePID1NUMAPolicy "preferred" "0"
-    pid1ReloadWithStrace
-    grep -P "set_mempolicy\(MPOL_PREFERRED, \[0x0*1\]" $straceLog
-
-    echo "PID1 NUMAPolicy support - Local policy w/o mask"
-    writePID1NUMAPolicy "local"
-    pid1ReloadWithStrace
-    # Kernel requires that nodemask argument is set to NULL when setting default policy
-    # The unpatched versions of strace don't recognize the MPOL_LOCAL constant and
-    # return a numerical constant instead (with a comment):
-    #   set_mempolicy(0x4 /* MPOL_??? */, NULL, 0) = 0
-    # Let's cover this scenario as well
-    grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" $straceLog
-
-    echo "PID1 NUMAPolicy support - Local policy w/ mask"
-    writePID1NUMAPolicy "local" "0"
-    pid1ReloadWithStrace
-    grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" $straceLog
-
-    echo "Unit file NUMAPolicy support - Default policy w/o mask"
-    writeTestUnitNUMAPolicy "default"
-    pid1StartUnitWithStrace $testUnit
-    systemctlCheckNUMAProperties $testUnit "default"
-    pid1StopUnit $testUnit
-    grep "set_mempolicy(MPOL_DEFAULT, NULL" $straceLog
-
-    echo "Unit file NUMAPolicy support - Default policy w/ mask"
-    writeTestUnitNUMAPolicy "default" "0"
-    pid1StartUnitWithStrace $testUnit
-    systemctlCheckNUMAProperties $testUnit "default" "0"
-    pid1StopUnit $testUnit
-    # Maks must be ignored
-    grep "set_mempolicy(MPOL_DEFAULT, NULL" $straceLog
-
-    echo "Unit file NUMAPolicy support - Bind policy w/o mask"
-    writeTestUnitNUMAPolicy "bind"
-    pid1StartUnitWithJournal $testUnit
-    pid1StopUnit $testUnit
-    grep "numa-test.service: Main process exited, code=exited, status=242/NUMA" $journalLog
-
-    echo "Unit file NUMAPolicy support - Bind policy w/ mask"
-    writeTestUnitNUMAPolicy "bind" "0"
-    pid1StartUnitWithStrace $testUnit
-    systemctlCheckNUMAProperties $testUnit "bind" "0"
-    pid1StopUnit $testUnit
-    grep -P "set_mempolicy\(MPOL_BIND, \[0x0*1\]" $straceLog
-
-    echo "Unit file NUMAPolicy support - Interleave policy w/o mask"
-    writeTestUnitNUMAPolicy "interleave"
-    pid1StartUnitWithStrace $testUnit
-    pid1StopUnit $testUnit
-    grep "numa-test.service: Main process exited, code=exited, status=242/NUMA" $journalLog
-
-    echo "Unit file NUMAPolicy support - Interleave policy w/ mask"
-    writeTestUnitNUMAPolicy "interleave" "0"
-    pid1StartUnitWithStrace $testUnit
-    systemctlCheckNUMAProperties $testUnit "interleave" "0"
-    pid1StopUnit $testUnit
-    grep -P "set_mempolicy\(MPOL_INTERLEAVE, \[0x0*1\]" $straceLog
-
-    echo "Unit file NUMAPolicy support - Preferred policy w/o mask"
-    writeTestUnitNUMAPolicy "preferred"
-    pid1StartUnitWithJournal $testUnit
-    systemctlCheckNUMAProperties $testUnit "preferred"
-    pid1StopUnit $testUnit
-    ! grep "numa-test.service: Main process exited, code=exited, status=242/NUMA" $journalLog
-
-    echo "Unit file NUMAPolicy support - Preferred policy w/ mask"
-    writeTestUnitNUMAPolicy "preferred" "0"
-    pid1StartUnitWithStrace $testUnit
-    systemctlCheckNUMAProperties $testUnit "preferred" "0"
-    pid1StopUnit $testUnit
-    grep -P "set_mempolicy\(MPOL_PREFERRED, \[0x0*1\]" $straceLog
-
-    echo "Unit file NUMAPolicy support - Local policy w/o mask"
-    writeTestUnitNUMAPolicy "local"
-    pid1StartUnitWithStrace $testUnit
-    systemctlCheckNUMAProperties $testUnit "local"
-    pid1StopUnit $testUnit
-    grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" $straceLog
-
-    echo "Unit file NUMAPolicy support - Local policy w/ mask"
-    writeTestUnitNUMAPolicy "local" "0"
-    pid1StartUnitWithStrace $testUnit
-    systemctlCheckNUMAProperties $testUnit "local" "0"
-    pid1StopUnit $testUnit
-    # Maks must be ignored
-    grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" $straceLog
-
-    echo "Unit file CPUAffinity=NUMA support"
-    writeTestUnitNUMAPolicy "bind" "0"
-    echo "CPUAffinity=numa" >> $testUnitNUMAConf
-    systemctl daemon-reload
-    systemctl start $testUnit
-    systemctlCheckNUMAProperties $testUnit "bind" "0"
-    pid=$(systemctl show --value -p MainPID $testUnit)
-    cpulist=$(cat /sys/devices/system/node/node0/cpulist)
-    affinity_systemd=$(systemctl show --value -p CPUAffinity $testUnit)
-    [ $cpulist = $affinity_systemd ]
-    pid1StopUnit $testUnit
-
-    echo "systemd-run NUMAPolicy support"
-    runUnit='numa-systemd-run-test.service'
-
-    systemd-run -p NUMAPolicy=default --unit $runUnit sleep 1000
-    systemctlCheckNUMAProperties $runUnit "default"
-    pid1StopUnit $runUnit
-
-    systemd-run -p NUMAPolicy=default -p NUMAMask=0 --unit $runUnit sleep 1000
-    systemctlCheckNUMAProperties $runUnit "default" ""
-    pid1StopUnit $runUnit
-
-    systemd-run -p NUMAPolicy=bind -p NUMAMask=0 --unit $runUnit sleep 1000
-    systemctlCheckNUMAProperties $runUnit "bind" "0"
-    pid1StopUnit $runUnit
-
-    systemd-run -p NUMAPolicy=interleave -p NUMAMask=0 --unit $runUnit sleep 1000
-    systemctlCheckNUMAProperties $runUnit "interleave" "0"
-    pid1StopUnit $runUnit
-
-    systemd-run -p NUMAPolicy=preferred -p NUMAMask=0 --unit $runUnit sleep 1000
-    systemctlCheckNUMAProperties $runUnit "preferred" "0"
-    pid1StopUnit $runUnit
-
-    systemd-run -p NUMAPolicy=local --unit $runUnit sleep 1000
-    systemctlCheckNUMAProperties $runUnit "local"
-    pid1StopUnit $runUnit
-
-    systemd-run -p NUMAPolicy=local -p NUMAMask=0 --unit $runUnit sleep 1000
-    systemctlCheckNUMAProperties $runUnit "local" ""
-    pid1StopUnit $runUnit
-
-    systemd-run -p NUMAPolicy=local -p NUMAMask=0 -p CPUAffinity=numa --unit $runUnit sleep 1000
-    systemctlCheckNUMAProperties $runUnit "local" ""
-    systemctl cat $runUnit | grep -q 'CPUAffinity=numa'
-    pid1StopUnit $runUnit
-
-fi
-
-# Cleanup
-rm -rf $testDir
-rm -rf $confDir
-systemctl daemon-reload
-
-systemd-analyze log-level info
-
-echo OK > /testok
-
-exit 0
index 2c5ad430b5e8f6da67358b5703b2119b96010bae..b5806c429f91aae4b18b820370fb72e477559283 100755 (executable)
@@ -3,33 +3,6 @@
 # ex: ts=8 sw=4 sts=4 et filetype=sh
 set -e
 TEST_DESCRIPTION="test RuntimeDirectoryPreserve=yes"
-
 . $TEST_BASE_DIR/test-functions
 
-test_setup() {
-    create_empty_image_rootdir
-
-    (
-        LOG_LEVEL=5
-        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
-
-        setup_basic_environment
-        mask_supporting_services
-
-        # setup the testsuite service
-        cat >$initdir/etc/systemd/system/testsuite.service <<EOF
-[Unit]
-Description=Testsuite service
-
-[Service]
-ExecStart=/bin/bash -x /testsuite.sh
-Type=oneshot
-EOF
-        cp testsuite.sh $initdir/
-
-        setup_testsuite
-    ) || return 1
-    setup_nspawn_root
-}
-
-do_test "$@"
+do_test "$@" 37
diff --git a/test/TEST-37-RUNTIMEDIRECTORYPRESERVE/testsuite.sh b/test/TEST-37-RUNTIMEDIRECTORYPRESERVE/testsuite.sh
deleted file mode 100755 (executable)
index 32a9dd8..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/usr/bin/env bash
-# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
-# ex: ts=8 sw=4 sts=4 et filetype=sh
-set -ex
-set -o pipefail
-
-systemd-mount -p RuntimeDirectory=hoge -p RuntimeDirectoryPreserve=yes -t tmpfs tmpfs /tmp/aaa
-
-touch /run/hoge/foo
-touch /tmp/aaa/bbb
-
-systemctl restart tmp-aaa.mount
-
-test -e /run/hoge/foo
-! test -e /tmp/aaa/bbb
-
-echo OK > /testok
-
-exit 0
diff --git a/test/TEST-38-FREEZER/Makefile b/test/TEST-38-FREEZER/Makefile
new file mode 120000 (symlink)
index 0000000..e9f93b1
--- /dev/null
@@ -0,0 +1 @@
+../TEST-01-BASIC/Makefile
\ No newline at end of file
diff --git a/test/TEST-38-FREEZER/test.sh b/test/TEST-38-FREEZER/test.sh
new file mode 100755 (executable)
index 0000000..3821db9
--- /dev/null
@@ -0,0 +1,7 @@
+#!/bin/bash
+set -e
+TEST_DESCRIPTION="test unit freezing and thawing via DBus and systemctl"
+TEST_NO_NSPAWN=1
+. $TEST_BASE_DIR/test-functions
+
+do_test "$@" 38
index ac1d0c9cf6d173635fab7e19fbb418f084821d94..e38a9902ce072b8d4d186ecb452fd1a17d7dbbae 100755 (executable)
@@ -1,35 +1,6 @@
 #!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="Test ExecReload= (PR #13098)"
-
 . $TEST_BASE_DIR/test-functions
 
-test_setup() {
-    create_empty_image_rootdir
-
-    # Create what will eventually be our root filesystem onto an overlay
-    (
-        LOG_LEVEL=5
-        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
-
-        setup_basic_environment
-        mask_supporting_services
-        dracut_install mktemp
-
-        # setup the testsuite service
-        cat >$initdir/etc/systemd/system/testsuite.service <<EOF
-[Unit]
-Description=Testsuite service
-
-[Service]
-ExecStart=/bin/bash -x /testsuite.sh
-Type=oneshot
-EOF
-        cp testsuite.sh $initdir/
-
-        setup_testsuite
-    )
-    setup_nspawn_root
-}
-
-do_test "$@"
+do_test "$@" 39
diff --git a/test/TEST-39-EXECRELOAD/testsuite.sh b/test/TEST-39-EXECRELOAD/testsuite.sh
deleted file mode 100644 (file)
index eb7363f..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-#!/usr/bin/env bash
-
-set -ex
-set -o pipefail
-
-systemd-analyze log-level debug
-systemd-analyze log-target console
-
-export SYSTEMD_PAGER=
-SERVICE_PATH="$(mktemp /etc/systemd/system/execreloadXXX.service)"
-SERVICE_NAME="${SERVICE_PATH##*/}"
-
-echo "[#1] Failing ExecReload= should not kill the service"
-cat > "$SERVICE_PATH" << EOF
-[Service]
-ExecStart=/bin/sleep infinity
-ExecReload=/bin/false
-EOF
-
-systemctl daemon-reload
-systemctl start $SERVICE_NAME
-systemctl status $SERVICE_NAME
-# The reload SHOULD fail but SHOULD NOT affect the service state
-! systemctl reload $SERVICE_NAME
-systemctl status $SERVICE_NAME
-systemctl stop $SERVICE_NAME
-
-
-echo "[#2] Failing ExecReload= should not kill the service (multiple ExecReload=)"
-cat > "$SERVICE_PATH" << EOF
-[Service]
-ExecStart=/bin/sleep infinity
-ExecReload=/bin/true
-ExecReload=/bin/false
-ExecReload=/bin/true
-EOF
-
-systemctl daemon-reload
-systemctl start $SERVICE_NAME
-systemctl status $SERVICE_NAME
-# The reload SHOULD fail but SHOULD NOT affect the service state
-! systemctl reload $SERVICE_NAME
-systemctl status $SERVICE_NAME
-systemctl stop $SERVICE_NAME
-
-echo "[#3] Failing ExecReload=- should not affect reload's exit code"
-cat > "$SERVICE_PATH" << EOF
-[Service]
-ExecStart=/bin/sleep infinity
-ExecReload=-/bin/false
-EOF
-
-systemctl daemon-reload
-systemctl start $SERVICE_NAME
-systemctl status $SERVICE_NAME
-systemctl reload $SERVICE_NAME
-systemctl status $SERVICE_NAME
-systemctl stop $SERVICE_NAME
-
-systemd-analyze log-level info
-
-echo OK > /testok
-
-exit 0
index a7933b007095c12aa354e5f7e7a006d62035e3ef..4ee84d4d35ddf7d0c9c167a6545fab482b3f0833 100755 (executable)
@@ -1,33 +1,6 @@
 #!/usr/bin/env bash
 set -e
 TEST_DESCRIPTION="test ExecXYZEx= service unit dbus hookups"
-
 . $TEST_BASE_DIR/test-functions
 
-test_setup() {
-    create_empty_image_rootdir
-
-    (
-        LOG_LEVEL=5
-        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
-
-        setup_basic_environment
-        mask_supporting_services
-
-        # setup the testsuite service
-        cat >$initdir/etc/systemd/system/testsuite.service <<EOF
-[Unit]
-Description=Testsuite service
-
-[Service]
-ExecStart=/testsuite.sh
-Type=oneshot
-EOF
-        cp testsuite.sh $initdir/
-
-        setup_testsuite
-    )
-    setup_nspawn_root
-}
-
-do_test "$@"
+do_test "$@" 40
diff --git a/test/TEST-40-EXEC-COMMAND-EX/testsuite.sh b/test/TEST-40-EXEC-COMMAND-EX/testsuite.sh
deleted file mode 100755 (executable)
index 957d220..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/usr/bin/env bash
-set -ex
-set -o pipefail
-
-systemd-analyze log-level debug
-systemd-analyze log-target console
-
-declare -A property
-
-property[1_one]=ExecCondition
-property[2_two]=ExecStartPre
-property[3_three]=ExecStart
-property[4_four]=ExecStartPost
-property[5_five]=ExecReload
-property[6_six]=ExecStop
-property[7_seven]=ExecStopPost
-
-# These should all get upgraded to the corresponding Ex property as the non-Ex variant
-# does not support the ":" prefix (no-env-expand).
-for c in "${!property[@]}"; do
-    systemd-run --unit="$c" -r -p "Type=oneshot" -p "${property[$c]}=:/bin/echo \${$c}" /bin/true
-    systemctl show -p "${property[$c]}" "$c" | grep -F "path=/bin/echo ; argv[]=/bin/echo \${$c} ; ignore_errors=no"
-    systemctl show -p "${property[$c]}Ex" "$c" | grep -F "path=/bin/echo ; argv[]=/bin/echo \${$c} ; flags=no-env-expand"
-done
-
-declare -A property_ex
-
-property_ex[1_one_ex]=ExecConditionEx
-property_ex[2_two_ex]=ExecStartPreEx
-property_ex[3_three_ex]=ExecStartEx
-property_ex[4_four_ex]=ExecStartPostEx
-property_ex[5_five_ex]=ExecReloadEx
-property_ex[6_six_ex]=ExecStopEx
-property_ex[7_seven_ex]=ExecStopPostEx
-
-for c in "${!property_ex[@]}"; do
-    systemd-run --unit="$c" -r -p "Type=oneshot" -p "${property_ex[$c]}=:/bin/echo \${$c}" /bin/true
-    systemctl show -p "${property_ex[$c]%??}" "$c" | grep -F "path=/bin/echo ; argv[]=/bin/echo \${$c} ; ignore_errors=no"
-    systemctl show -p "${property_ex[$c]}" "$c" | grep -F "path=/bin/echo ; argv[]=/bin/echo \${$c} ; flags=no-env-expand"
-done
-
-systemd-analyze log-level info
-
-echo OK > /testok
-
-exit 0
index 54292c0ca22eeabaa2ff0e4abbed9198185c7d73..d3f96ae1071a9ffe5020ed72fe6d0e0c7df67d70 100755 (executable)
@@ -3,30 +3,4 @@ set -e
 TEST_DESCRIPTION="Test oneshot unit restart on failure"
 . $TEST_BASE_DIR/test-functions
 
-test_setup() {
-    create_empty_image_rootdir
-
-    (
-        LOG_LEVEL=5
-        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
-
-        setup_basic_environment
-        mask_supporting_services
-
-        # setup the testsuite service
-        cat >$initdir/etc/systemd/system/testsuite.service <<EOF
-[Unit]
-Description=Testsuite service
-
-[Service]
-ExecStart=/testsuite.sh
-Type=oneshot
-EOF
-        cp testsuite.sh $initdir/
-
-        setup_testsuite
-    )
-    setup_nspawn_root
-}
-
-do_test "$@"
+do_test "$@" 41
diff --git a/test/TEST-41-ONESHOT-RESTART/testsuite.sh b/test/TEST-41-ONESHOT-RESTART/testsuite.sh
deleted file mode 100755 (executable)
index 905f32e..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/usr/bin/env bash
-set -ex
-set -o pipefail
-
-# wait this many secs for each test service to succeed in what is being tested
-MAX_SECS=60
-
-systemd-analyze log-level debug
-systemd-analyze log-target console
-
-# test one: Restart=on-failure should restart the service
-! systemd-run --unit=one -p Type=oneshot -p Restart=on-failure /bin/bash -c "exit 1"
-
-for ((secs=0; secs<$MAX_SECS; secs++)); do
-  [[ "$(systemctl show one.service -p NRestarts --value)" -le 0 ]] || break
-  sleep 1
-done
-if [[ "$(systemctl show one.service -p NRestarts --value)" -le 0 ]]; then
-  exit 1
-fi
-
-TMP_FILE="/test-41-oneshot-restart-test"
-
-touch $TMP_FILE
-
-# test two: make sure StartLimitBurst correctly limits the number of restarts
-# and restarts execution of the unit from the first ExecStart=
-! systemd-run --unit=two -p StartLimitIntervalSec=120 -p StartLimitBurst=3 -p Type=oneshot -p Restart=on-failure -p ExecStart="/bin/bash -c \"printf a >>  $TMP_FILE\"" /bin/bash -c "exit 1"
-
-# wait for at least 3 restarts
-for ((secs=0; secs<$MAX_SECS; secs++)); do
-  [[ $(cat $TMP_FILE) != "aaa" ]] || break
-  sleep 1
-done
-if [[ $(cat $TMP_FILE) != "aaa" ]]; then
-  exit 1
-fi
-
-# wait for 5 more seconds to make sure there aren't excess restarts
-sleep 5
-if [[ $(cat $TMP_FILE) != "aaa" ]]; then
-  exit 1
-fi
-
-systemd-analyze log-level info
-
-echo OK > /testok
-
-exit 0
index 0c393597c79838d8645487b274cce213abb518fc..53e6fa3dd01da5ea040bb28816c1c211f0e83e03 100755 (executable)
@@ -4,46 +4,4 @@ TEST_DESCRIPTION="test that ExecStopPost= is always run"
 
 . $TEST_BASE_DIR/test-functions
 
-test_setup() {
-    create_empty_image_rootdir
-
-    (
-        LOG_LEVEL=5
-        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
-
-        setup_basic_environment
-
-        mask_supporting_services
-
-        # setup policy for Type=dbus test
-        mkdir -p $initdir/etc/dbus-1/system.d
-        cat > $initdir/etc/dbus-1/system.d/systemd.test.ExecStopPost.conf <<EOF
-<?xml version="1.0"?>
-<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
-        "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
-<busconfig>
-    <policy user="root">
-        <allow own="systemd.test.ExecStopPost"/>
-    </policy>
-</busconfig>
-EOF
-
-        # setup the testsuite service
-        cat >$initdir/etc/systemd/system/testsuite.service <<EOF
-[Unit]
-Description=Testsuite service
-Before=getty-pre.target
-Wants=getty-pre.target
-
-[Service]
-ExecStart=/testsuite.sh
-Type=oneshot
-EOF
-        cp testsuite.sh $initdir/
-
-        setup_testsuite
-    )
-    setup_nspawn_root
-}
-
-do_test "$@"
+do_test "$@" 42
diff --git a/test/TEST-42-EXECSTOPPOST/testsuite.sh b/test/TEST-42-EXECSTOPPOST/testsuite.sh
deleted file mode 100755 (executable)
index 154398d..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-#!/usr/bin/env bash
-set -ex
-
-systemd-analyze log-level debug
-
-systemd-run --unit=simple1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=simple -p ExecStopPost='/bin/touch /run/simple1' true
-test -f /run/simple1
-
-! systemd-run --unit=simple2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=simple -p ExecStopPost='/bin/touch /run/simple2' false
-test -f /run/simple2
-
-systemd-run --unit=exec1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=exec -p ExecStopPost='/bin/touch /run/exec1' sleep 1
-test -f /run/exec1
-
-! systemd-run --unit=exec2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=exec -p ExecStopPost='/bin/touch /run/exec2' sh -c 'sleep 1; false'
-test -f /run/exec2
-
-cat > /tmp/forking1.sh <<EOF
-#!/usr/bin/env bash
-
-set -eux
-
-sleep 4 &
-MAINPID=\$!
-disown
-
-systemd-notify MAINPID=\$MAINPID
-EOF
-chmod +x /tmp/forking1.sh
-
-systemd-run --unit=forking1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=forking -p NotifyAccess=exec -p ExecStopPost='/bin/touch /run/forking1' /tmp/forking1.sh
-test -f /run/forking1
-
-cat > /tmp/forking2.sh <<EOF
-#!/usr/bin/env bash
-
-set -eux
-
-( sleep 4; exit 1 ) &
-MAINPID=\$!
-disown
-
-systemd-notify MAINPID=\$MAINPID
-EOF
-chmod +x /tmp/forking2.sh
-
-! systemd-run --unit=forking2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=forking -p NotifyAccess=exec -p ExecStopPost='/bin/touch /run/forking2' /tmp/forking2.sh
-test -f /run/forking2
-
-systemd-run --unit=oneshot1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=oneshot -p ExecStopPost='/bin/touch /run/oneshot1' true
-test -f /run/oneshot1
-
-! systemd-run --unit=oneshot2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=oneshot -p ExecStopPost='/bin/touch /run/oneshot2' false
-test -f /run/oneshot2
-
-systemd-run --unit=dbus1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=dbus -p BusName=systemd.test.ExecStopPost -p ExecStopPost='/bin/touch /run/dbus1' \
-    busctl call org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus RequestName su systemd.test.ExecStopPost 4 \
-    || :
-test -f /run/dbus1
-
-! systemd-run --unit=dbus2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=dbus -p BusName=systemd.test.ExecStopPost -p ExecStopPost='/bin/touch /run/dbus2' true
-test -f /run/dbus2
-
-cat > /tmp/notify1.sh <<EOF
-#!/usr/bin/env bash
-
-set -eux
-
-systemd-notify --ready
-EOF
-chmod +x /tmp/notify1.sh
-
-systemd-run --unit=notify1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=notify -p ExecStopPost='/bin/touch /run/notify1' /tmp/notify1.sh
-test -f /run/notify1
-
-! systemd-run --unit=notify2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=notify -p ExecStopPost='/bin/touch /run/notify2' true
-test -f /run/notify2
-
-systemd-run --unit=idle1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=idle -p ExecStopPost='/bin/touch /run/idle1' true
-test -f /run/idle1
-
-! systemd-run --unit=idle2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=idle -p ExecStopPost='/bin/touch /run/idle2' false
-test -f /run/idle2
-
-systemd-analyze log-level info
-
-echo OK > /testok
-
-exit 0
index 3b40bf7d8bb5b4445fb33d311e6a22199ec9cebc..4749150ff125a448f38050741e7526d0ca21b3a3 100755 (executable)
@@ -3,47 +3,6 @@ set -e
 TEST_DESCRIPTION="Test PrivateUsers=yes on user manager"
 . $TEST_BASE_DIR/test-functions
 
-test_setup() {
-    create_empty_image_rootdir
-
-    (
-        LOG_LEVEL=5
-        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
-
-        setup_basic_environment
-        inst_binary stat
-
-        mask_supporting_services
-
-        # Allocate user for running test case under
-        mkdir -p $initdir/etc/sysusers.d
-        cat >$initdir/etc/sysusers.d/testuser.conf <<EOF
-u testuser    4711     "Test User" /home/testuser
-EOF
-
-        mkdir -p $initdir/home/testuser -m 0700
-        chown 4711:4711 $initdir/home/testuser
-
-        enable_user_manager testuser
-
-        # setup the testsuite service
-        cat >$initdir/etc/systemd/system/testsuite.service <<EOF
-[Unit]
-Description=Testsuite service
-After=systemd-logind.service user@4711.service
-Wants=user@4711.service
-
-[Service]
-ExecStart=/testsuite.sh
-Type=oneshot
-EOF
-        cp testsuite.sh $initdir/
-
-        setup_testsuite
-    )
-    setup_nspawn_root
-}
-
 has_user_dbus_socket || exit 0
 
-do_test "$@"
+do_test "$@" 43
diff --git a/test/TEST-43-PRIVATEUSER-UNPRIV/testsuite.sh b/test/TEST-43-PRIVATEUSER-UNPRIV/testsuite.sh
deleted file mode 100755 (executable)
index ff94ad4..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-#!/usr/bin/env bash
-set -ex
-set -o pipefail
-
-systemd-analyze log-level debug
-
-runas() {
-    declare userid=$1
-    shift
-    su "$userid" -s /bin/sh -c 'XDG_RUNTIME_DIR=/run/user/$UID exec "$@"' -- sh "$@"
-}
-
-runas testuser systemd-run --user --unit=test-private-users \
-    -p PrivateUsers=yes -P echo hello
-
-runas testuser systemd-run --user --unit=test-private-tmp-innerfile \
-    -p PrivateUsers=yes -p PrivateTmp=yes \
-    -P touch /tmp/innerfile.txt
-# File should not exist outside the job's tmp directory.
-test ! -e /tmp/innerfile.txt
-
-touch /tmp/outerfile.txt
-# File should not appear in unit's private tmp.
-runas testuser systemd-run --user --unit=test-private-tmp-outerfile \
-    -p PrivateUsers=yes -p PrivateTmp=yes \
-    -P test ! -e /tmp/outerfile.txt
-
-# Confirm that creating a file in home works
-runas testuser systemd-run --user --unit=test-unprotected-home \
-    -P touch /home/testuser/works.txt
-test -e /home/testuser/works.txt
-
-# Confirm that creating a file in home is blocked under read-only
-runas testuser systemd-run --user --unit=test-protect-home-read-only \
-    -p PrivateUsers=yes -p ProtectHome=read-only \
-    -P bash -c '
-        test -e /home/testuser/works.txt
-        ! touch /home/testuser/blocked.txt
-    '
-test ! -e /home/testuser/blocked.txt
-
-# Check that tmpfs hides the whole directory
-runas testuser systemd-run --user --unit=test-protect-home-tmpfs \
-    -p PrivateUsers=yes -p ProtectHome=tmpfs \
-    -P test ! -e /home/testuser
-
-# Confirm that home, /root, and /run/user are inaccessible under "yes"
-runas testuser systemd-run --user --unit=test-protect-home-yes \
-    -p PrivateUsers=yes -p ProtectHome=yes \
-    -P bash -c '
-        test "$(stat -c %a /home)" = "0"
-        test "$(stat -c %a /root)" = "0"
-        test "$(stat -c %a /run/user)" = "0"
-    '
-
-# Confirm we cannot change groups because we only have one mapping in the user
-# namespace (no CAP_SETGID in the parent namespace to write the additional
-# mapping of the user supplied group and thus cannot change groups to an
-# unmapped group ID)
-! runas testuser systemd-run --user --unit=test-group-fail \
-    -p PrivateUsers=yes -p Group=daemon \
-    -P true
-
-systemd-analyze log-level info
-
-echo OK > /testok
-
-exit 0
index c2070f2b611dc9218ef7c3213eb96cdcd00e3e96..26d863708e85833115b666f9898139f8c7d8dbea 100755 (executable)
@@ -4,36 +4,4 @@ TEST_DESCRIPTION="test log namespaces"
 
 . $TEST_BASE_DIR/test-functions
 
-test_setup() {
-    create_empty_image_rootdir
-
-    (
-        LOG_LEVEL=5
-        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
-
-        setup_basic_environment
-
-        mask_supporting_services
-
-        # setup the testsuite service
-        cat >$initdir/etc/systemd/system/testsuite.service <<EOF
-[Unit]
-Description=Testsuite service
-Before=getty-pre.target
-Wants=getty-pre.target
-Wants=systemd-journald@foobar.socket systemd-journald-varlink@foobar.socket
-After=systemd-journald@foobar.socket systemd-journald-varlink@foobar.socket
-
-[Service]
-ExecStart=/testsuite.sh
-Type=oneshot
-LogTarget=foobar
-EOF
-        cp testsuite.sh $initdir/
-
-        setup_testsuite
-    )
-    setup_nspawn_root
-}
-
-do_test "$@"
+do_test "$@" 44
diff --git a/test/TEST-44-LOG-NAMESPACE/testsuite.sh b/test/TEST-44-LOG-NAMESPACE/testsuite.sh
deleted file mode 100755 (executable)
index 9754163..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/usr/bin/env bash
-set -ex
-
-systemd-analyze log-level debug
-
-systemd-run -p LogNamespace=foobar echo "hello world"
-
-journalctl --namespace=foobar --sync
-journalctl --namespace=foobar > /tmp/hello-world
-journalctl > /tmp/no-hello-world
-
-grep "hello world" /tmp/hello-world
-! grep "hello world" /tmp/no-hello-world
-
-systemd-analyze log-level info
-
-echo OK > /testok
-
-exit 0
diff --git a/test/TEST-45-REPART/Makefile b/test/TEST-45-REPART/Makefile
deleted file mode 120000 (symlink)
index e9f93b1..0000000
+++ /dev/null
@@ -1 +0,0 @@
-../TEST-01-BASIC/Makefile
\ No newline at end of file
diff --git a/test/TEST-45-REPART/test.sh b/test/TEST-45-REPART/test.sh
deleted file mode 100755 (executable)
index e7015d5..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/usr/bin/env bash
-set -e
-TEST_DESCRIPTION="test systemd-repart"
-
-. $TEST_BASE_DIR/test-functions
-
-test_setup() {
-    create_empty_image_rootdir
-
-    (
-        LOG_LEVEL=5
-        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
-
-        setup_basic_environment
-
-        mask_supporting_services
-        dracut_install truncate sfdisk grep
-
-        # setup the testsuite service
-        cat >$initdir/etc/systemd/system/testsuite.service <<EOF
-[Unit]
-Description=Testsuite service
-Before=getty-pre.target
-Wants=getty-pre.target
-
-[Service]
-ExecStart=/testsuite.sh
-Type=oneshot
-EOF
-        cp testsuite.sh $initdir/
-
-        setup_testsuite
-    )
-    setup_nspawn_root
-}
-
-do_test "$@"
diff --git a/test/TEST-45-REPART/testsuite.sh b/test/TEST-45-REPART/testsuite.sh
deleted file mode 100755 (executable)
index 804faef..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-#!/usr/bin/env bash
-set -ex
-
-# Check if repart is installed, and if it isn't bail out early instead of failing
-if ! test -x /usr/bin/systemd-repart ; then
-    echo OK > /testok
-    exit 0
-fi
-
-systemd-analyze log-level debug
-
-truncate -s 1G /tmp/zzz
-
-SEED=e2a40bf9-73f1-4278-9160-49c031e7aef8
-
-systemd-repart /tmp/zzz --empty=force --dry-run=no --seed=$SEED
-
-sfdisk -d /tmp/zzz | grep -v -e 'sector-size' -e '^$' > /tmp/empty
-
-cmp /tmp/empty - <<EOF
-label: gpt
-label-id: EF7F7EE2-47B3-4251-B1A1-09EA8BF12D5D
-device: /tmp/zzz
-unit: sectors
-first-lba: 2048
-last-lba: 2097118
-EOF
-
-mkdir /tmp/definitions
-
-cat > /tmp/definitions/root.conf <<EOF
-[Partition]
-Type=root
-EOF
-
-ln -s root.conf /tmp/definitions/root2.conf
-
-cat > /tmp/definitions/home.conf <<EOF
-[Partition]
-Type=home
-EOF
-
-cat > /tmp/definitions/swap.conf <<EOF
-[Partition]
-Type=swap
-SizeMaxBytes=64M
-PaddingMinBytes=92M
-EOF
-
-systemd-repart /tmp/zzz --dry-run=no --seed=$SEED --definitions=/tmp/definitions
-
-sfdisk -d /tmp/zzz | grep -v -e 'sector-size' -e '^$' > /tmp/populated
-
-cmp /tmp/populated - <<EOF
-label: gpt
-label-id: EF7F7EE2-47B3-4251-B1A1-09EA8BF12D5D
-device: /tmp/zzz
-unit: sectors
-first-lba: 2048
-last-lba: 2097118
-/tmp/zzz1 : start=        2048, size=      591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=A6005774-F558-4330-A8E5-D6D2C01C01D6, name="home"
-/tmp/zzz2 : start=      593904, size=      591856, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=CE9C76EB-A8F1-40FF-813C-11DCA6C0A55B, name="root-x86-64"
-/tmp/zzz3 : start=     1185760, size=      591864, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=AC60A837-550C-43BD-B5C4-9CB73B884E79, name="root-x86-64-2"
-/tmp/zzz4 : start=     1777624, size=      131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=2AA78CDB-59C7-4173-AF11-C7453737A5D1, name="swap"
-EOF
-
-cat > /tmp/definitions/swap.conf <<EOF
-[Partition]
-Type=swap
-SizeMaxBytes=64M
-EOF
-
-cat > /tmp/definitions/extra.conf <<EOF
-[Partition]
-Type=linux-generic
-EOF
-
-systemd-repart /tmp/zzz --dry-run=no --seed=$SEED --definitions=/tmp/definitions
-
-sfdisk -d /tmp/zzz | grep -v -e 'sector-size' -e '^$' > /tmp/populated2
-
-cmp /tmp/populated2 - <<EOF
-label: gpt
-label-id: EF7F7EE2-47B3-4251-B1A1-09EA8BF12D5D
-device: /tmp/zzz
-unit: sectors
-first-lba: 2048
-last-lba: 2097118
-/tmp/zzz1 : start=        2048, size=      591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=A6005774-F558-4330-A8E5-D6D2C01C01D6, name="home"
-/tmp/zzz2 : start=      593904, size=      591856, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=CE9C76EB-A8F1-40FF-813C-11DCA6C0A55B, name="root-x86-64"
-/tmp/zzz3 : start=     1185760, size=      591864, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=AC60A837-550C-43BD-B5C4-9CB73B884E79, name="root-x86-64-2"
-/tmp/zzz4 : start=     1777624, size=      131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=2AA78CDB-59C7-4173-AF11-C7453737A5D1, name="swap"
-/tmp/zzz5 : start=     1908696, size=      188416, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=03477476-06AD-44E8-9EF4-BC2BD7771289, name="linux-generic"
-EOF
-
-truncate -s 2G /tmp/zzz
-
-systemd-repart /tmp/zzz --dry-run=no --seed=$SEED --definitions=/tmp/definitions
-
-sfdisk -d /tmp/zzz | grep -v -e 'sector-size' -e '^$' > /tmp/populated3
-
-cmp /tmp/populated3 - <<EOF
-label: gpt
-label-id: EF7F7EE2-47B3-4251-B1A1-09EA8BF12D5D
-device: /tmp/zzz
-unit: sectors
-first-lba: 2048
-last-lba: 4194270
-/tmp/zzz1 : start=        2048, size=      591856, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=A6005774-F558-4330-A8E5-D6D2C01C01D6, name="home"
-/tmp/zzz2 : start=      593904, size=      591856, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=CE9C76EB-A8F1-40FF-813C-11DCA6C0A55B, name="root-x86-64"
-/tmp/zzz3 : start=     1185760, size=      591864, type=4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709, uuid=AC60A837-550C-43BD-B5C4-9CB73B884E79, name="root-x86-64-2"
-/tmp/zzz4 : start=     1777624, size=      131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=2AA78CDB-59C7-4173-AF11-C7453737A5D1, name="swap"
-/tmp/zzz5 : start=     1908696, size=     2285568, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4, uuid=03477476-06AD-44E8-9EF4-BC2BD7771289, name="linux-generic"
-EOF
-
-systemd-analyze log-level info
-
-echo OK > /testok
-
-exit 0
index 99fd5b85b81dc904f1dfede3b4b486d638c87df8..877cbfefd035d02bce12f5037d93588bc32f6dd0 100755 (executable)
@@ -5,38 +5,4 @@ TEST_NO_QEMU=1
 
 . $TEST_BASE_DIR/test-functions
 
-test_setup() {
-    create_empty_image
-    mkdir -p $TESTDIR/root
-    mount ${LOOPDEV}p1 $TESTDIR/root
-
-    (
-        LOG_LEVEL=5
-        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
-
-        setup_basic_environment
-        mask_supporting_services
-
-        # setup the testsuite service
-        cat >$initdir/etc/systemd/system/testsuite.service <<EOF
-[Unit]
-Description=Testsuite service
-Before=getty-pre.target
-Wants=getty-pre.target
-
-[Service]
-ExecStart=/bin/bash -x /testsuite.sh
-Type=oneshot
-NotifyAccess=all
-EOF
-        cp testsuite.sh $initdir/
-
-        setup_testsuite
-    ) || return 1
-    setup_nspawn_root
-
-    ddebug "umount $TESTDIR/root"
-    umount $TESTDIR/root
-}
-
-do_test "$@"
+do_test "$@" 46
diff --git a/test/TEST-46-HOMED/testsuite.sh b/test/TEST-46-HOMED/testsuite.sh
deleted file mode 100755 (executable)
index 9ef9f30..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-#!/usr/bin/env bash
-set -ex
-set -o pipefail
-
-# Check if homectl is installed, and if it isn't bail out early instead of failing
-if ! test -x /usr/bin/homectl ; then
-        echo OK > /testok
-        exit 0
-fi
-
-inspect() {
-        homectl inspect $1 | tee /tmp/a
-        userdbctl user $1 | tee /tmp/b
-        cmp /tmp/a /tmp/b
-        rm /tmp/a /tmp/b
-}
-
-systemd-analyze log-level debug
-systemd-analyze log-target console
-
-NEWPASSWORD=xEhErW0ndafV4s homectl create test-user --disk-size=20M
-inspect test-user
-
-PASSWORD=xEhErW0ndafV4s homectl authenticate test-user
-
-PASSWORD=xEhErW0ndafV4s homectl activate test-user
-inspect test-user
-
-PASSWORD=xEhErW0ndafV4s homectl update test-user --real-name="Inline test"
-inspect test-user
-
-homectl deactivate test-user
-inspect test-user
-
-PASSWORD=xEhErW0ndafV4s NEWPASSWORD=yPN4N0fYNKUkOq homectl passwd test-user
-inspect test-user
-
-PASSWORD=yPN4N0fYNKUkOq homectl activate test-user
-inspect test-user
-
-SYSTEMD_LOG_LEVEL=debug PASSWORD=yPN4N0fYNKUkOq NEWPASSWORD=xEhErW0ndafV4s homectl passwd test-user
-inspect test-user
-
-homectl deactivate test-user
-inspect test-user
-
-PASSWORD=xEhErW0ndafV4s homectl activate test-user
-inspect test-user
-
-PASSWORD=xEhErW0ndafV4s homectl deactivate test-user
-inspect test-user
-
-PASSWORD=xEhErW0ndafV4s homectl update test-user --real-name="Offline test"
-inspect test-user
-
-PASSWORD=xEhErW0ndafV4s homectl activate test-user
-inspect test-user
-
-PASSWORD=xEhErW0ndafV4s homectl deactivate test-user
-inspect test-user
-
-! PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz
-PASSWORD=xEhErW0ndafV4s homectl with test-user -- touch /home/test-user/xyz
-PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz
-PASSWORD=xEhErW0ndafV4s homectl with test-user -- rm /home/test-user/xyz
-! PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz
-
-homectl remove test-user
-
-systemd-analyze log-level info
-
-echo OK > /testok
-
-exit 0
diff --git a/test/TEST-47-ISSUE-14566/repro.sh b/test/TEST-47-ISSUE-14566/repro.sh
deleted file mode 100755 (executable)
index 5217602..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/bash
-
-sleep infinity &
-echo $! > /leakedtestpid
-wait $!
index 35f862331c4a7b29e6b2cf88ecd5fe0e3e656270..4e80ec76ff58a4ce593075b348960070c709464b 100755 (executable)
@@ -1,43 +1,6 @@
-#!/bin/bash
+#!/usr/bin/env bash
 set -e
-TEST_DESCRIPTION="Test that KillMode=mixed does not leave left over proccesses with ExecStopPost="
+TEST_DESCRIPTION="Test that KillMode=mixed does not leave left over processes with ExecStopPost="
 . $TEST_BASE_DIR/test-functions
 
-test_setup() {
-    create_empty_image_rootdir
-
-    (
-        LOG_LEVEL=5
-        eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
-
-        setup_basic_environment
-        mask_supporting_services
-
-        # setup the testsuite service
-        cat >$initdir/etc/systemd/system/testsuite.service <<EOF
-[Unit]
-Description=Testsuite service
-
-[Service]
-ExecStart=/testsuite.sh
-Type=oneshot
-EOF
-        cat > $initdir/etc/systemd/system/issue_14566_test.service << EOF
-[Unit]
-Description=Issue 14566 Repro
-
-[Service]
-ExecStart=/repro.sh
-ExecStopPost=/bin/true
-KillMode=mixed
-EOF
-
-        cp testsuite.sh $initdir/
-        cp repro.sh $initdir/
-
-        setup_testsuite
-    )
-    setup_nspawn_root
-}
-
-do_test "$@"
+do_test "$@" 47
diff --git a/test/TEST-47-ISSUE-14566/testsuite.sh b/test/TEST-47-ISSUE-14566/testsuite.sh
deleted file mode 100755 (executable)
index df8d0e9..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/bash
-set -ex
-set -o pipefail
-
-systemd-analyze log-level debug
-systemd-analyze log-target console
-
-systemctl start issue_14566_test
-sleep 1
-systemctl status issue_14566_test
-
-leaked_pid=$(cat /leakedtestpid)
-
-systemctl stop issue_14566_test
-sleep 1
-
-# Leaked PID will still be around if we're buggy.
-# I personally prefer to see 42.
-ps -p "$leaked_pid" && exit 42
-
-systemd-analyze log-level info
-
-echo OK > /testok
-
-exit 0
diff --git a/test/TEST-48-START-STOP-NO-RELOAD/Makefile b/test/TEST-48-START-STOP-NO-RELOAD/Makefile
new file mode 120000 (symlink)
index 0000000..e9f93b1
--- /dev/null
@@ -0,0 +1 @@
+../TEST-01-BASIC/Makefile
\ No newline at end of file
diff --git a/test/TEST-48-START-STOP-NO-RELOAD/test.sh b/test/TEST-48-START-STOP-NO-RELOAD/test.sh
new file mode 100755 (executable)
index 0000000..f6638b3
--- /dev/null
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+set -e
+TEST_DESCRIPTION="test StartStopNoReload"
+. $TEST_BASE_DIR/test-functions
+
+do_test "$@" 48
diff --git a/test/TEST-49-UDEV-EVENT-TIMEOUT/Makefile b/test/TEST-49-UDEV-EVENT-TIMEOUT/Makefile
new file mode 120000 (symlink)
index 0000000..e9f93b1
--- /dev/null
@@ -0,0 +1 @@
+../TEST-01-BASIC/Makefile
\ No newline at end of file
diff --git a/test/TEST-49-UDEV-EVENT-TIMEOUT/test.sh b/test/TEST-49-UDEV-EVENT-TIMEOUT/test.sh
new file mode 100755 (executable)
index 0000000..249d8a2
--- /dev/null
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+set -e
+
+TEST_DESCRIPTION="test udev's event-timeout and timeout-signal options"
+TEST_NO_NSPAWN=1
+. $TEST_BASE_DIR/test-functions
+
+do_test "$@" 49
diff --git a/test/TEST-50-DISSECT/Makefile b/test/TEST-50-DISSECT/Makefile
new file mode 120000 (symlink)
index 0000000..e9f93b1
--- /dev/null
@@ -0,0 +1 @@
+../TEST-01-BASIC/Makefile
\ No newline at end of file
diff --git a/test/TEST-50-DISSECT/test.sh b/test/TEST-50-DISSECT/test.sh
new file mode 100755 (executable)
index 0000000..3882658
--- /dev/null
@@ -0,0 +1,52 @@
+#!/usr/bin/env bash
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+set -e
+TEST_DESCRIPTION="test systemd-dissect"
+IMAGE_NAME="dissect"
+TEST_NO_NSPAWN=1
+
+. $TEST_BASE_DIR/test-functions
+
+command -v mksquashfs >/dev/null 2>&1 || exit 0
+command -v veritysetup >/dev/null 2>&1 || exit 0
+command -v sfdisk >/dev/null 2>&1 || exit 0
+
+# Need loop devices for systemd-dissect
+test_create_image() {
+    create_empty_image_rootdir
+
+    # Create what will eventually be our root filesystem onto an overlay
+    # If some pieces are missing from the host, skip rather than fail
+    (
+        LOG_LEVEL=5
+        setup_basic_environment
+        mask_supporting_services
+
+        instmods loop =block
+        instmods squashfs =squashfs
+        instmods dm_verity =md
+        install_dmevent
+        generate_module_dependencies
+        inst_binary sfdisk
+        inst_binary losetup
+
+        BASICTOOLS=(
+            bash
+            cat
+        )
+        oldinitdir=$initdir
+        export initdir=$TESTDIR/minimal
+        mkdir -p $initdir
+        setup_basic_dirs
+        install_basic_tools
+        inst /usr/lib/os-release
+        ln -s ../usr/lib/os-release $initdir/etc/os-release
+        echo MARKER=1 >> $initdir/usr/lib/os-release
+        mksquashfs $initdir $oldinitdir/usr/share/minimal.raw
+        veritysetup format $oldinitdir/usr/share/minimal.raw $oldinitdir/usr/share/minimal.verity | grep '^Root hash:' | cut -f2 | tr -d '\n' > $oldinitdir/usr/share/minimal.roothash
+        export initdir=$oldinitdir
+    )
+}
+
+do_test "$@" 50
diff --git a/test/TEST-51-ISSUE-16115/Makefile b/test/TEST-51-ISSUE-16115/Makefile
new file mode 120000 (symlink)
index 0000000..e9f93b1
--- /dev/null
@@ -0,0 +1 @@
+../TEST-01-BASIC/Makefile
\ No newline at end of file
diff --git a/test/TEST-51-ISSUE-16115/test.sh b/test/TEST-51-ISSUE-16115/test.sh
new file mode 100755 (executable)
index 0000000..eca235c
--- /dev/null
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+set -e
+TEST_DESCRIPTION="Test ExecCondition= does not restart on abnormal or failure"
+. $TEST_BASE_DIR/test-functions
+
+do_test "$@" 51
diff --git a/test/TEST-52-HONORFIRSTSHUTDOWN/Makefile b/test/TEST-52-HONORFIRSTSHUTDOWN/Makefile
new file mode 100644 (file)
index 0000000..a065990
--- /dev/null
@@ -0,0 +1,16 @@
+BUILD_DIR=$(shell ../../tools/find-build-dir.sh)
+
+all setup run clean clean-again:
+       @basedir=../.. TEST_BASE_DIR=../ BUILD_DIR=$(BUILD_DIR) ./test.sh --$@
+
+# finish option is used to run checks that can only be run outside of
+# the test execution. Example case, honor first shutdown, proof is obtained
+# from the console output as the image shuts down. This does not show up in
+# the journal so the output from the do_test is captured in a file in /tmp.
+# Without the use of finish the test will still pass because if it fails
+# the test will loop and will be terminated via a command timeout.
+# This just provides concrete confirmation.
+finish:
+       @basedir=../.. TEST_BASE_DIR=../ BUILD_DIR=$(BUILD_DIR) ./fini.sh --$@
+
+.PHONY: all setup run clean clean-again finish
diff --git a/test/TEST-52-HONORFIRSTSHUTDOWN/fini.sh b/test/TEST-52-HONORFIRSTSHUTDOWN/fini.sh
new file mode 100755 (executable)
index 0000000..993ada0
--- /dev/null
@@ -0,0 +1,10 @@
+#!/bin/bash
+TEST_DESCRIPTION="test honor first shutdown"
+
+if grep -q "Shutdown is already active. Skipping emergency action request" /tmp/honorfirstshutdown.log; then
+    echo "$TEST_DESCRIPTION [pass]"
+    exit 0
+else
+    echo "$TEST_DESCRIPTION [fail]"
+    exit 1
+fi
diff --git a/test/TEST-52-HONORFIRSTSHUTDOWN/test.sh b/test/TEST-52-HONORFIRSTSHUTDOWN/test.sh
new file mode 100755 (executable)
index 0000000..a0848ef
--- /dev/null
@@ -0,0 +1,19 @@
+#!/bin/bash
+set -e
+. $TEST_BASE_DIR/test-functions
+TEST_REQUIRE_INSTALL_TESTS=0
+TEST_DESCRIPTION="testing honor first shutdown"
+#INTERACTIVE_DEBUG=1
+TEST_NO_QEMU=1
+
+#Using timeout because if the test fails it can loop.
+# The reason is because the poweroff executed by end.service
+# could turn into a reboot if the test fails.
+NSPAWN_TIMEOUT=20
+
+#Remove this file if it exists. this is used along with
+# the make target "finish". Since concrete confirmaion is
+# only found from the console during the poweroff.
+rm -f /tmp/honorfirstshutdown.log >/dev/null
+
+do_test "$@" 52 > /tmp/honorfirstshutdown.log
diff --git a/test/TEST-53-ISSUE-16347/Makefile b/test/TEST-53-ISSUE-16347/Makefile
new file mode 120000 (symlink)
index 0000000..e9f93b1
--- /dev/null
@@ -0,0 +1 @@
+../TEST-01-BASIC/Makefile
\ No newline at end of file
diff --git a/test/TEST-53-ISSUE-16347/test.sh b/test/TEST-53-ISSUE-16347/test.sh
new file mode 100755 (executable)
index 0000000..089768e
--- /dev/null
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+set -e
+
+TEST_DESCRIPTION="test timer units when initial clock is ahead"
+TEST_NO_NSPAWN=1
+
+future_date=$(date -u +%Y-%m-%dT%H:%M:%S -d '+3 days')
+QEMU_OPTIONS="-rtc base=${future_date}"
+. $TEST_BASE_DIR/test-functions
+
+do_test "$@" 53
diff --git a/test/a-conj.service b/test/a-conj.service
deleted file mode 100644 (file)
index db37ae7..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-[Unit]
-Description=A conjugate
-Requires=a.service
-After=a.service
-Before=a.service
-
-[Service]
-ExecStart=/bin/true
diff --git a/test/a.service b/test/a.service
deleted file mode 100644 (file)
index 4168d2d..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-[Unit]
-Description=A
-Requires=b.service
-Before=b.service
-
-[Service]
-ExecStart=/bin/true
diff --git a/test/b.service b/test/b.service
deleted file mode 100644 (file)
index e03bae3..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-[Unit]
-Description=B
-Wants=f.service
-
-[Service]
-ExecStart=/bin/true
diff --git a/test/basic.target b/test/basic.target
deleted file mode 120000 (symlink)
index 0612934..0000000
+++ /dev/null
@@ -1 +0,0 @@
-../units/basic.target
\ No newline at end of file
diff --git a/test/c.service b/test/c.service
deleted file mode 100644 (file)
index e2f60a8..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-[Unit]
-Description=C
-Requires=a.service
-
-[Service]
-ExecStart=/bin/true
diff --git a/test/create-busybox-container b/test/create-busybox-container
new file mode 100755 (executable)
index 0000000..08fb5d4
--- /dev/null
@@ -0,0 +1,53 @@
+#!/usr/bin/env bash
+
+set -e
+set -u
+set -o pipefail
+
+root="${1:?Usage $0 container-root}"
+mkdir -p "$root"
+mkdir "$root/bin"
+cp $(type -P busybox) "$root/bin"
+
+mkdir -p "$root/usr/lib"
+touch "$root/usr/lib/os-release"
+
+ln -s busybox "$root/bin/sh"
+ln -s busybox "$root/bin/cat"
+ln -s busybox "$root/bin/tr"
+ln -s busybox "$root/bin/ps"
+ln -s busybox "$root/bin/ip"
+
+mkdir -p "$root/sbin"
+cat <<'EOF' >"$root/sbin/init"
+#!/bin/sh
+
+printf "ps aufx:\n"
+ps aufx
+
+printf "/proc/1/cmdline:\n"
+printf "%s\n\n" "$(tr '\0' ' ' </proc/1/cmdline)"
+
+printf "/proc/1/environ:\n"
+printf "%s\n\n" "$(tr '\0' '\n' </proc/1/environ)"
+
+printf "/proc/1/mountinfo:\n"
+cat /proc/self/mountinfo
+printf "\n"
+
+printf "/proc/1/cgroup:\n"
+printf "%s\n\n" "$(cat /proc/1/cgroup)"
+
+printf "/proc/1/uid_map:\n"
+printf "%s\n\n" "$(cat /proc/1/uid_map)"
+
+printf "/proc/1/setgroups:\n"
+printf "%s\n\n" "$(cat /proc/1/setgroups)"
+
+printf "/proc/1/gid_map:\n"
+printf "%s\n\n" "$(cat /proc/1/gid_map)"
+
+printf "ip link:\n"
+ip link
+EOF
+chmod +x "$root/sbin/init"
diff --git a/test/d.service b/test/d.service
deleted file mode 100644 (file)
index 921fd2e..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-[Unit]
-Description=D:Cyclic
-After=b.service
-Before=a.service
-Requires=a.service
-
-[Service]
-ExecStart=/bin/true
diff --git a/test/daughter.service b/test/daughter.service
deleted file mode 100644 (file)
index c790b9d..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-[Unit]
-Description=Daughter Service
-
-[Service]
-Slice=parent.slice
-Type=oneshot
-ExecStart=/bin/true
-CPUAccounting=true
diff --git a/test/dml-discard-empty.service b/test/dml-discard-empty.service
deleted file mode 100644 (file)
index 75228f6..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-[Unit]
-Description=DML discard empty service
-
-[Service]
-Slice=dml-discard.slice
-Type=oneshot
-ExecStart=/bin/true
diff --git a/test/dml-discard-set-ml.service b/test/dml-discard-set-ml.service
deleted file mode 100644 (file)
index 591c992..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-[Unit]
-Description=DML discard set ml service
-
-[Service]
-Slice=dml-discard.slice
-Type=oneshot
-ExecStart=/bin/true
-MemoryLow=15
diff --git a/test/dml-discard.slice b/test/dml-discard.slice
deleted file mode 100644 (file)
index e26d868..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-[Unit]
-Description=DML discard slice
-
-[Slice]
-DefaultMemoryLow=
diff --git a/test/dml-override-empty.service b/test/dml-override-empty.service
deleted file mode 100644 (file)
index 142c987..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-[Unit]
-Description=DML override empty service
-
-[Service]
-Slice=dml-override.slice
-Type=oneshot
-ExecStart=/bin/true
diff --git a/test/dml-override.slice b/test/dml-override.slice
deleted file mode 100644 (file)
index feb6773..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-[Unit]
-Description=DML override slice
-
-[Slice]
-DefaultMemoryLow=10
diff --git a/test/dml-passthrough-empty.service b/test/dml-passthrough-empty.service
deleted file mode 100644 (file)
index 34832de..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-[Unit]
-Description=DML passthrough empty service
-
-[Service]
-Slice=dml-passthrough.slice
-Type=oneshot
-ExecStart=/bin/true
diff --git a/test/dml-passthrough-set-dml.service b/test/dml-passthrough-set-dml.service
deleted file mode 100644 (file)
index 5bdf4ed..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-[Unit]
-Description=DML passthrough set DML service
-
-[Service]
-Slice=dml-passthrough.slice
-Type=oneshot
-ExecStart=/bin/true
-DefaultMemoryLow=15
diff --git a/test/dml-passthrough-set-ml.service b/test/dml-passthrough-set-ml.service
deleted file mode 100644 (file)
index 2e568b5..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-[Unit]
-Description=DML passthrough set ML service
-
-[Service]
-Slice=dml-passthrough.slice
-Type=oneshot
-ExecStart=/bin/true
-MemoryLow=0
diff --git a/test/dml-passthrough.slice b/test/dml-passthrough.slice
deleted file mode 100644 (file)
index 1b1a848..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-[Unit]
-Description=DML passthrough slice
-
-[Slice]
-MemoryLow=100
diff --git a/test/dml.slice b/test/dml.slice
deleted file mode 100644 (file)
index 84e333e..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-[Unit]
-Description=DML slice
-
-[Slice]
-DefaultMemoryLow=50
diff --git a/test/e.service b/test/e.service
deleted file mode 100644 (file)
index 5ba98c7..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-[Unit]
-Description=E:Cyclic
-After=b.service
-Before=a.service
-Wants=a.service
-
-[Service]
-ExecStart=/bin/true
diff --git a/test/end.service b/test/end.service
deleted file mode 100644 (file)
index 6e1996f..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-[Unit]
-Description=End the test
-After=testsuite.service
-OnFailure=poweroff.target
-OnFailureJobMode=replace-irreversibly
-
-[Service]
-Type=oneshot
-ExecStart=/bin/sh -x -c 'systemctl poweroff --no-block'
-TimeoutStartSec=5m
diff --git a/test/f.service b/test/f.service
deleted file mode 100644 (file)
index 7dde681..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-[Unit]
-Description=F
-
-[Service]
-ExecStart=/bin/true
diff --git a/test/fuzz/fuzz-journal-remote/oss-fuzz-21122 b/test/fuzz/fuzz-journal-remote/oss-fuzz-21122
new file mode 100644 (file)
index 0000000..e0e05e1
Binary files /dev/null and b/test/fuzz/fuzz-journal-remote/oss-fuzz-21122 differ
diff --git a/test/fuzz/fuzz-json/github-15907 b/test/fuzz/fuzz-json/github-15907
new file mode 100644 (file)
index 0000000..b247ccd
--- /dev/null
@@ -0,0 +1 @@
+[7E73]
\ No newline at end of file
index b304ad0ddb141923743e5ec30d01099e641652de..7d23a9ecf8e0bb59d7e4619623e8f967b75c653b 100644 (file)
@@ -40,4 +40,9 @@ OtherChannels=
 CombinedChannels=
 Advertise=
 RxBufferSize=
+RxMiniBufferSize=
+RxJumboBufferSize=
 TxBufferSize=
+RxFlowControl=
+TxFlowControl=
+AutoNegotiationFlowControl=
index e8391dab780cfad2385a3a6f641d09a08f2b43b1..ef1f18fa402430c384c4bd14891ebe19f6d5ceaf 100644 (file)
@@ -6,6 +6,7 @@ Id=
 GVRP=
 [MACVLAN]
 Mode=
+SourceMACAddress=
 [WireGuard]
 ListenPort=
 PrivateKey=
@@ -14,6 +15,7 @@ FwMark=
 FirewallMark=
 [MACVTAP]
 Mode=
+SourceMACAddress=
 [Match]
 Architecture=
 Host=
@@ -45,6 +47,7 @@ AgeingTimeSec=
 Priority=
 GroupForwardMask=
 VLANFiltering=
+VLANProtocol=
 MulticastIGMPVersion=
 [VRF]
 TableId=
diff --git a/test/fuzz/fuzz-netdev-parser/github-15968 b/test/fuzz/fuzz-netdev-parser/github-15968
new file mode 100644 (file)
index 0000000..0527704
--- /dev/null
@@ -0,0 +1,14 @@
+[NetDev]
+Name=t
+[L2TP]
+b
+Remote=1.8.0.2
+
+[L2TPSession]
+SessionId=
+[L2TP]
+PeerTunnelId=2
+[NetDev]
+Kind=l2tp
+[L2TP]
+TunnelId=4
diff --git a/test/fuzz/fuzz-netdev-parser/wireguard-duplicated-endpoint b/test/fuzz/fuzz-netdev-parser/wireguard-duplicated-endpoint
new file mode 100644 (file)
index 0000000..adff4c1
--- /dev/null
@@ -0,0 +1,6 @@
+[NetDev]
+Name=w
+Kind=wireguard
+[WireGuardPeer]
+Endpoint=:0
+Endpoint=:8
\ No newline at end of file
index 2b41239b745817f49d6ce4b6e0aaf20861239d12..5d804707197f48fd0c47f087c57adde27ea90b9d 100644 (file)
@@ -37,6 +37,17 @@ Unmanaged=
 MTUBytes=
 Multicast=
 MACAddress=
+Group=
+[SR-IOV]
+VirtualFunction=
+MACSpoofCheck=
+VLANId=
+VLANProtocol=
+QualityOfService=
+QueryReceiveSideScaling=
+Trust=
+LinkState=
+MACAddress=
 [BridgeFDB]
 VLANId=
 MACAddress=
@@ -73,6 +84,7 @@ UseDNS=
 RoutesToDNS=
 UseDomains=
 UseRoutes=
+UseGateway=
 IAID=
 UserClass=
 UseNTP=
@@ -93,20 +105,37 @@ ClientIdentifier=
 ListenPort=
 UseTimezone=
 RouteTable=
+DenyList=
 BlackList=
+AllowList=
 RequestOptions=
 SendRelease=
 MaxAttempts=
 IPServiceType=
 SendOption=
+SendVendorOption=
 SendDecline=
+MUDURL=
 RouteMTUBytes=
+FallbackLeaseLifetimeSec=
 [DHCPv6]
 UseNTP=
 UseDNS=
 RapidCommit=
 ForceDHCPv6PDOtherInformation=
 PrefixDelegationHint=
+WithoutRA=
+MUDURL=
+SendOption=
+RequestOptions=
+UserClass=
+VendorClass=
+SendVendorOption=
+RouteMetric=
+[DHCPv6PrefixDelegation]
+SubnetId=
+Assign=
+Token=
 [Route]
 Destination=
 Protocol=
@@ -138,6 +167,7 @@ Address=
 IPv6ProxyNDPAddress=
 IPv6AcceptRA=
 IPv6AcceptRouterAdvertisements=
+IPv4AcceptLocal=
 DNSSECNegativeTrustAnchors=
 MACVTAP=
 IPv6PrivacyExtensions=
@@ -169,6 +199,7 @@ VXLAN=
 L2TP=
 MACsec=
 LinkLocalAddressing=
+IPv6LinkLocalAddressGenerationMode=
 ConfigureWithoutCarrier=
 NTP=
 DHCP=
@@ -186,6 +217,7 @@ OnLink=
 PreferredLifetimeSec=
 AddressAutoconfiguration=
 ValidLifetimeSec=
+Assign=
 [IPv6RoutePrefix]
 Route=
 LifetimeSec=
@@ -193,11 +225,19 @@ LifetimeSec=
 EgressUntagged=
 VLAN=
 PVID=
+[LLDP]
+MUDURL=
 [CAN]
 SamplePoint=
 BitRate=
+DataSamplePoint=
+DataBitRate=
+FDMode=
+FDNonISO=
 RestartSec=
 TripleSampling=
+Termination=
+ListenOnly=
 [Address]
 DuplicateAddressDetection=
 AutoJoin=
@@ -249,8 +289,10 @@ Prefix=
 UseDomains=
 RouteTable=
 UseDNS=
+DHCPv6Client=
 UseAutonomousPrefix=
 UseOnLinkPrefix=
+DenyList=
 BlackList=
 [DHCPServer]
 EmitNTP=
@@ -261,12 +303,19 @@ EmitDNS=
 NTP=
 EmitSIP=
 SIP=
+EmitPOP3=
+POP3=
+EmitSMTP=
+SMTP=
+EmitLPR=
+LPR=
 EmitRouter=
 MaxLeaseTimeSec=
 DefaultLeaseTimeSec=
 EmitTimezone=
 DNS=
 SendOption=
+SendVendorOption=
 [NextHop]
 Id=
 Gateway=
@@ -285,7 +334,9 @@ PacketLimit=
 Parent=
 Handle=
 Rate=
+BurstBytes=
 Burst=
+LimitBytes=
 LimitSize=
 MTUBytes=
 MPUBytes=
@@ -299,8 +350,10 @@ PerturbPeriodSec=
 Parent=
 Handle=
 PacketLimit=
+MemoryLimitBytes=
 MemoryLimit=
 Flows=
+QuantumBytes=
 Quantum=
 TargetSec=
 IntervalSec=
@@ -311,7 +364,9 @@ Parent=
 Handle=
 PacketLimit=
 FlowLimit=
+QuantumBytes=
 Quantum=
+InitialQuantumBytes=
 InitialQuantum=
 MaximumRate=
 Buckets=
@@ -326,6 +381,11 @@ TargetSec=
 IntervalSec=
 CEThresholdSec=
 ECN=
+[CAKE]
+Parent=
+Handle=
+Bandwidth=
+OverheadBytes=
 [TrafficControlQueueingDiscipline]
 Parent=
 NetworkEmulatorDelaySec=
@@ -337,3 +397,74 @@ NetworkEmulatorPacketLimit=
 Parent=
 Handle=
 Id=
+[HierarchyTokenBucket]
+Parent=
+Handle=
+DefaultClass=
+RateToQuantum=
+[HierarchyTokenBucketClass]
+Parent=
+ClassId=
+Priority=
+QuantumBytes=
+MTUBytes=
+OverheadBytes=
+Rate=
+CeilRate=
+BufferBytes=
+CeilBufferBytes=
+[BFIFO]
+Parent=
+Handle=
+LimitBytes=
+[PFIFO]
+Parent=
+Handle=
+PacketLimit=
+[PFIFOHeadDrop]
+Parent=
+Handle=
+PacketLimit=
+[PFIFOFast]
+Parent=
+Handle=
+[GenericRandomEarlyDetection]
+Parent=
+Handle=
+VirtualQueues=
+DefaultVirtualQueue=
+GenericRIO=
+[StochasticFairBlue]
+Parent=
+Handle=
+PacketLimit=
+[PIE]
+Parent=
+Handle=
+PacketLimit=
+[QuickFairQueueing]
+Parent=
+Handle=
+[QuickFairQueueingClass]
+Parent=
+ClassId=
+Weight=
+MaxPacketBytes=
+[DeficitRoundRobinScheduler]
+Parent=
+Handle=
+[DeficitRoundRobinSchedulerClass]
+Parent=
+ClassId=
+QuantumBytes=
+[EnhancedTransmissionSelection]
+Parent=
+Handle=
+Bands=
+StrictBands=
+QuantumBytes=
+PriorityMap=
+[HeavyHitterFilter]
+Parent=
+Handle=
+PacketLimit=
diff --git a/test/fuzz/fuzz-network-parser/dns-trust-anchor-duplicate.network b/test/fuzz/fuzz-network-parser/dns-trust-anchor-duplicate.network
new file mode 100644 (file)
index 0000000..ed7bdab
--- /dev/null
@@ -0,0 +1,2 @@
+[Network]
+DNSSECNegativeTrustAnchors=i i
\ No newline at end of file
diff --git a/test/fuzz/fuzz-network-parser/github-15885 b/test/fuzz/fuzz-network-parser/github-15885
new file mode 100644 (file)
index 0000000..9bbdcb2
--- /dev/null
@@ -0,0 +1,9 @@
+[DHCPv4]
+SendOption=1:string:
+SendOption=1:uint8:
+SendOption=1:uint16:
+SendOption=1:uint32:
+SendOption=1:ipv4address:
+SendOption=1:ipv4address:127.0.0.1
+SendOption=1:ipv6address:
+SendOption=1:ipv6address:52:54:00:b9:b5:61
diff --git a/test/fuzz/fuzz-network-parser/github-15951 b/test/fuzz/fuzz-network-parser/github-15951
new file mode 100644 (file)
index 0000000..7785f35
--- /dev/null
@@ -0,0 +1,2 @@
+[DHCPServer]
+POP3Servers=1.8.5.0
index 2a37d5f45597a1a3a7a164d6ad1ddc267ef5a17f..fb7cfcfb3c33f231779d46237fe9d909261b678f 100644 (file)
@@ -1,2 +1,2 @@
 [IPv6AcceptRA]
-BlackList=70:: 70::
\ No newline at end of file
+DenyList=70:: 70::
\ No newline at end of file
diff --git a/test/fuzz/fuzz-network-parser/oss-fuzz-23895 b/test/fuzz/fuzz-network-parser/oss-fuzz-23895
new file mode 100644 (file)
index 0000000..a86361d
Binary files /dev/null and b/test/fuzz/fuzz-network-parser/oss-fuzz-23895 differ
diff --git a/test/fuzz/fuzz-network-parser/oss-fuzz-23950 b/test/fuzz/fuzz-network-parser/oss-fuzz-23950
new file mode 100644 (file)
index 0000000..5bfb17b
Binary files /dev/null and b/test/fuzz/fuzz-network-parser/oss-fuzz-23950 differ
diff --git a/test/fuzz/fuzz-udev-rules/line-too-long b/test/fuzz/fuzz-udev-rules/line-too-long
new file mode 100644 (file)
index 0000000..c0b908d
--- /dev/null
@@ -0,0 +1 @@
+O\x4294967296d%d;ycalc\x0a]r\n%s$(xcalc)$1'xcalc!xcalc%s"strjng_escaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaSYMLIaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\85aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
\ No newline at end of file
index 45f8751971b9b199ced97758b187936a4284b5b7..dbff9ab2cc7d6740ad625b99aaa709c77bdfe901 100644 (file)
@@ -44,6 +44,7 @@ BlockIOWeight=
 BlockIOWriteBandwidth=
 Broadcast=
 BusName=
+CoredumpFilter=
 CPUAccounting=
 CPUQuota=
 CPUShares=
@@ -163,6 +164,7 @@ PIDFile=
 PartOf=
 PassCredentials=
 PassSecurity=
+PassPacketInfo=
 PathChanged=
 PathExists=
 PathExistsGlob=
@@ -194,6 +196,9 @@ ReusePort=
 RootDirectory=
 RootDirectoryStartOnly=
 RootImage=
+RootHash=
+RootHashSignature=
+RootVerity=
 RuntimeMaxSec=
 SELinuxContextFromNet=
 SecureBits=
@@ -855,6 +860,7 @@ RateLimitIntervalSec=
 ReadKMsg=
 ReadOnly=
 ReadOnlyPaths=
+ReadWriteOnly=
 ReadWritePaths=
 RemoveIPC=
 ReserveVT=
@@ -863,6 +869,7 @@ RestrictNamespaces=
 RestrictRealtime=
 RestrictSUIDSGID=
 RuntimeDirectory=
+RuntimeDirectoryInodesMax=
 RuntimeDirectoryMode=
 RuntimeDirectoryPreserve=
 RuntimeDirectorySize=
diff --git a/test/fuzz/fuzz-xdg-desktop/full.desktop b/test/fuzz/fuzz-xdg-desktop/full.desktop
new file mode 100644 (file)
index 0000000..e5da36b
--- /dev/null
@@ -0,0 +1,10 @@
+[Desktop Entry]
+Type=Application
+Name=GNOME Settings Daemon's power plugin
+Exec=/usr/bin/sleep %i %f "%F" "--test" ";\\\\!?"
+OnlyShowIn=GNOME;
+NoDisplay=true
+X-GNOME-Autostart-Phase=Initialization
+X-GNOME-Autostart-Notify=true
+X-GNOME-AutoRestart=true
+X-GNOME-HiddenUnderSystemd=true
diff --git a/test/fuzz/fuzz-xdg-desktop/org.gnome.SettingsDaemon.Power.desktop b/test/fuzz/fuzz-xdg-desktop/org.gnome.SettingsDaemon.Power.desktop
new file mode 100644 (file)
index 0000000..9d3e0c5
--- /dev/null
@@ -0,0 +1,10 @@
+[Desktop Entry]
+Type=Application
+Name=GNOME Settings Daemon's power plugin
+Exec=/usr/libexec/gsd-power
+OnlyShowIn=GNOME;
+NoDisplay=true
+X-GNOME-Autostart-Phase=Initialization
+X-GNOME-Autostart-Notify=true
+X-GNOME-AutoRestart=true
+X-GNOME-HiddenUnderSystemd=true
diff --git a/test/fuzz/fuzz-xdg-desktop/oss-fuzz-22812 b/test/fuzz/fuzz-xdg-desktop/oss-fuzz-22812
new file mode 100644 (file)
index 0000000..4b4cadf
Binary files /dev/null and b/test/fuzz/fuzz-xdg-desktop/oss-fuzz-22812 differ
diff --git a/test/fuzz/fuzz-xdg-desktop/valid.desktop b/test/fuzz/fuzz-xdg-desktop/valid.desktop
new file mode 100644 (file)
index 0000000..20fb5fe
--- /dev/null
@@ -0,0 +1,12 @@
+Desktop Entry
+Name=
+Exec=
+TryExec=
+Type=
+OnlyShowIn=
+NotShowIn=
+Hidden=
+AutostartCondition=
+X-KDE-autostart-condition=
+X-GNOME-Autostart-Phase=
+X-GNOME-HiddenUnderSystemd=
index c514f57fe3d1618667bd65122f0efb01983239a5..99584c144b153f610badc41242579e14bb6e68c3 100644 (file)
@@ -1,17 +1,25 @@
 # SPDX-License-Identifier: LGPL-2.1+
 
-sanitize_address = custom_target(
-        'sanitize-address-fuzzers',
-        output : 'sanitize-address-fuzzers',
+# The 'optimization' option was introduced in meson 0.48.0, so let's keep
+# the code compatible with older versions as well
+if meson.version().version_compare('>=0.48.0')
+        optimization = '--optimization=@0@'.format(get_option('optimization'))
+else
+        optimization = ''
+endif
+
+sanitize_address_undefined = custom_target(
+        'sanitize-address-undefined-fuzzers',
+        output : 'sanitize-address-undefined-fuzzers',
         command : [meson_build_sh,
                    project_source_root,
                    '@OUTPUT@',
                    'fuzzers',
-                   '-Db_lundef=false -Db_sanitize=address',
+                   '-Db_lundef=false -Db_sanitize=address,undefined @0@'.format(optimization),
                    ' '.join(cc.cmd_array()),
                    cxx_cmd])
 
-sanitizers = [['address', sanitize_address]]
+sanitizers = [['address,undefined', sanitize_address_undefined]]
 
 if git.found()
         out = run_command(
diff --git a/test/g.service b/test/g.service
deleted file mode 100644 (file)
index cbfa82a..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-[Unit]
-Description=G
-Conflicts=e.service
-
-[Service]
-ExecStart=/bin/true
diff --git a/test/grandchild.service b/test/grandchild.service
deleted file mode 100644 (file)
index ab64130..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-[Unit]
-Description=Grandchild Service
-
-[Service]
-Slice=parent-deep.slice
-Type=oneshot
-ExecStart=/bin/true
diff --git a/test/h.service b/test/h.service
deleted file mode 100644 (file)
index 74a7751..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-[Unit]
-Description=H
-Wants=g.service
-
-[Service]
-ExecStart=/bin/true
diff --git a/test/hello-after-sleep.target b/test/hello-after-sleep.target
deleted file mode 100644 (file)
index 526fbd2..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-[Unit]
-Description=Sleep for a minute, then say hello.
-Wants=sleep.service hello.service
-After=sleep.service
-Before=hello.service
diff --git a/test/hello.service b/test/hello.service
deleted file mode 100644 (file)
index 82907b6..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-[Unit]
-Description=Hello World
-
-[Service]
-ExecStart=/bin/echo "Hello World"
diff --git a/test/i.service b/test/i.service
deleted file mode 100644 (file)
index 938ea77..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-[Unit]
-Description=I
-Conflicts=a.service d.service
-Wants=b.service
-After=b.service
-
-[Service]
-ExecStart=/bin/true
diff --git a/test/loopy.service b/test/loopy.service
deleted file mode 100644 (file)
index 9eb6457..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-[Service]
-ExecStart=/bin/true
diff --git a/test/loopy.service.d/compat.conf b/test/loopy.service.d/compat.conf
deleted file mode 100644 (file)
index 51b84b8..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-[Unit]
-BindsTo=loopy2.service
-
-[Install]
-Also=loopy2.service
diff --git a/test/loopy2.service b/test/loopy2.service
deleted file mode 120000 (symlink)
index 961b1fe..0000000
+++ /dev/null
@@ -1 +0,0 @@
-loopy.service
\ No newline at end of file
diff --git a/test/loopy3.service b/test/loopy3.service
deleted file mode 100644 (file)
index 606e26b..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-[Service]
-ExecStart=/bin/true
-
-[Unit]
-Conflicts=loopy4.service
diff --git a/test/loopy4.service b/test/loopy4.service
deleted file mode 120000 (symlink)
index 43e5658..0000000
+++ /dev/null
@@ -1 +0,0 @@
-loopy3.service
\ No newline at end of file
index 2fbea31ccd9c76e406bebe772c6c713b04ab112a..5d9fb5ab50925757e09af03f0f066dfd84033ded 100644 (file)
 # SPDX-License-Identifier: LGPL-2.1+
 
-test_data_files = '''
-        a.service
-        a-conj.service
-        b.service
-        basic.target
-        c.service
-        d.service
-        daughter.service
-        dml.slice
-        dml-passthrough.slice
-        dml-passthrough-empty.service
-        dml-passthrough-set-dml.service
-        dml-passthrough-set-ml.service
-        dml-override.slice
-        dml-override-empty.service
-        dml-discard.slice
-        dml-discard-empty.service
-        dml-discard-set-ml.service
-        e.service
-        end.service
-        f.service
-        g.service
-        grandchild.service
-        h.service
-        hello-after-sleep.target
-        hello.service
-        hwdb.d/10-bad.hwdb
-        i.service
-        journal-data/journal-1.txt
-        journal-data/journal-2.txt
-        nomem.slice
-        nomemleaf.service
-        parent-deep.slice
-        parent.slice
-        sched_idle_bad.service
-        sched_idle_ok.service
-        sched_rr_bad.service
-        sched_rr_change.service
-        sched_rr_ok.service
-        shutdown.target
-        sleep.service
-        sockets.target
-        son.service
-        sysinit.target
-        test-execute/exec-basic.service
-        test-execute/exec-ambientcapabilities-merge-nfsnobody.service
-        test-execute/exec-ambientcapabilities-merge-nobody.service
-        test-execute/exec-ambientcapabilities-merge.service
-        test-execute/exec-ambientcapabilities-nfsnobody.service
-        test-execute/exec-ambientcapabilities-nobody.service
-        test-execute/exec-ambientcapabilities.service
-        test-execute/exec-bindpaths.service
-        test-execute/exec-capabilityboundingset-invert.service
-        test-execute/exec-capabilityboundingset-merge.service
-        test-execute/exec-capabilityboundingset-reset.service
-        test-execute/exec-capabilityboundingset-simple.service
-        test-execute/exec-condition-failed.service
-        test-execute/exec-condition-skip.service
-        test-execute/exec-cpuaffinity1.service
-        test-execute/exec-cpuaffinity2.service
-        test-execute/exec-cpuaffinity3.service
-        test-execute/exec-dynamicuser-fixeduser-adm.service
-        test-execute/exec-dynamicuser-fixeduser-games.service
-        test-execute/exec-dynamicuser-fixeduser-one-supplementarygroup.service
-        test-execute/exec-dynamicuser-fixeduser.service
-        test-execute/exec-dynamicuser-statedir-migrate-step1.service
-        test-execute/exec-dynamicuser-statedir-migrate-step2.service
-        test-execute/exec-dynamicuser-statedir.service
-        test-execute/exec-dynamicuser-supplementarygroups.service
-        test-execute/exec-environment-no-substitute.service
-        test-execute/exec-environment-empty.service
-        test-execute/exec-environment-multiple.service
-        test-execute/exec-environment.service
-        test-execute/exec-environmentfile.service
-        test-execute/exec-group-nfsnobody.service
-        test-execute/exec-group-nobody.service
-        test-execute/exec-group-nogroup.service
-        test-execute/exec-group.service
-        test-execute/exec-ignoresigpipe-no.service
-        test-execute/exec-ignoresigpipe-yes.service
-        test-execute/exec-inaccessiblepaths-mount-propagation.service
-        test-execute/exec-inaccessiblepaths-sys.service
-        test-execute/exec-ioschedulingclass-best-effort.service
-        test-execute/exec-ioschedulingclass-idle.service
-        test-execute/exec-ioschedulingclass-none.service
-        test-execute/exec-ioschedulingclass-realtime.service
-        test-execute/exec-oomscoreadjust-negative.service
-        test-execute/exec-oomscoreadjust-positive.service
-        test-execute/exec-passenvironment-absent.service
-        test-execute/exec-passenvironment-empty.service
-        test-execute/exec-passenvironment-repeated.service
-        test-execute/exec-passenvironment.service
-        test-execute/exec-personality-aarch64.service
-        test-execute/exec-personality-ppc64.service
-        test-execute/exec-personality-ppc64le.service
-        test-execute/exec-personality-s390.service
-        test-execute/exec-personality-x86-64.service
-        test-execute/exec-personality-x86.service
-        test-execute/exec-privatedevices-disabled-by-prefix.service
-        test-execute/exec-privatedevices-no-capability-mknod.service
-        test-execute/exec-privatedevices-no-capability-sys-rawio.service
-        test-execute/exec-privatedevices-no.service
-        test-execute/exec-privatedevices-yes-with-group.service
-        test-execute/exec-privatedevices-yes-capability-mknod.service
-        test-execute/exec-privatedevices-yes-capability-sys-rawio.service
-        test-execute/exec-privatedevices-yes.service
-        test-execute/exec-privatenetwork-yes.service
-        test-execute/exec-privatetmp-no.service
-        test-execute/exec-privatetmp-yes.service
-        test-execute/exec-privatetmp-disabled-by-prefix.service
-        test-execute/exec-protecthome-tmpfs-vs-protectsystem-strict.service
-        test-execute/exec-protectkernellogs-yes-capabilities.service
-        test-execute/exec-protectkernellogs-no-capabilities.service
-        test-execute/exec-protectkernelmodules-no-capabilities.service
-        test-execute/exec-protectkernelmodules-yes-capabilities.service
-        test-execute/exec-protectkernelmodules-yes-mount-propagation.service
-        test-execute/exec-readonlypaths-mount-propagation.service
-        test-execute/exec-readonlypaths-simple.service
-        test-execute/exec-readonlypaths-with-bindpaths.service
-        test-execute/exec-readonlypaths.service
-        test-execute/exec-readwritepaths-mount-propagation.service
-        test-execute/exec-restrictnamespaces-merge-all.service
-        test-execute/exec-restrictnamespaces-merge-and.service
-        test-execute/exec-restrictnamespaces-merge-or.service
-        test-execute/exec-restrictnamespaces-mnt-blacklist.service
-        test-execute/exec-restrictnamespaces-mnt.service
-        test-execute/exec-restrictnamespaces-no.service
-        test-execute/exec-restrictnamespaces-yes.service
-        test-execute/exec-runtimedirectory-mode.service
-        test-execute/exec-runtimedirectory-owner-nfsnobody.service
-        test-execute/exec-runtimedirectory-owner-nobody.service
-        test-execute/exec-runtimedirectory-owner-nogroup.service
-        test-execute/exec-runtimedirectory-owner.service
-        test-execute/exec-runtimedirectory.service
-        test-execute/exec-specifier-interpolation.service
-        test-execute/exec-specifier.service
-        test-execute/exec-specifier@.service
-        test-execute/exec-standardinput-data.service
-        test-execute/exec-standardinput-file.service
-        test-execute/exec-standardinput-file-cat.service
-        test-execute/exec-standardoutput-file.service
-        test-execute/exec-standardoutput-append.service
-        test-execute/exec-supplementarygroups-multiple-groups-default-group-user.service
-        test-execute/exec-supplementarygroups-multiple-groups-withgid.service
-        test-execute/exec-supplementarygroups-multiple-groups-withuid.service
-        test-execute/exec-supplementarygroups-single-group-user.service
-        test-execute/exec-supplementarygroups-single-group.service
-        test-execute/exec-supplementarygroups.service
-        test-execute/exec-systemcallerrornumber-name.service
-        test-execute/exec-systemcallerrornumber-number.service
-        test-execute/exec-systemcallfilter-failing.service
-        test-execute/exec-systemcallfilter-failing2.service
-        test-execute/exec-systemcallfilter-not-failing.service
-        test-execute/exec-systemcallfilter-not-failing2.service
-        test-execute/exec-systemcallfilter-system-user-nfsnobody.service
-        test-execute/exec-systemcallfilter-system-user-nobody.service
-        test-execute/exec-systemcallfilter-system-user.service
-        test-execute/exec-systemcallfilter-with-errno-multi.service
-        test-execute/exec-systemcallfilter-with-errno-name.service
-        test-execute/exec-systemcallfilter-with-errno-number.service
-        test-execute/exec-temporaryfilesystem-options.service
-        test-execute/exec-temporaryfilesystem-ro.service
-        test-execute/exec-temporaryfilesystem-rw.service
-        test-execute/exec-temporaryfilesystem-usr.service
-        test-execute/exec-umask-0177.service
-        test-execute/exec-umask-default.service
-        test-execute/exec-unsetenvironment.service
-        test-execute/exec-user-nfsnobody.service
-        test-execute/exec-user-nobody.service
-        test-execute/exec-user.service
-        test-execute/exec-workingdirectory.service
-        test-execute/exec-workingdirectory-trailing-dot.service
-        test-path/basic.target
-        test-path/path-changed.path
-        test-path/path-changed.service
-        test-path/path-directorynotempty.path
-        test-path/path-directorynotempty.service
-        test-path/path-exists.path
-        test-path/path-exists.service
-        test-path/path-existsglob.path
-        test-path/path-existsglob.service
-        test-path/path-makedirectory.path
-        test-path/path-makedirectory.service
-        test-path/path-modified.path
-        test-path/path-modified.service
-        test-path/path-mycustomunit.service
-        test-path/path-service.service
-        test-path/path-unit.path
-        test-path/paths.target
-        test-path/sysinit.target
-        test-umount/empty.mountinfo
-        test-umount/example.swaps
-        test-umount/garbled.mountinfo
-        test-umount/rhbug-1554943.mountinfo
-        testsuite.target
-        timers.target
-        unit-with-.service.d/20-override.conf
-        unit-with-multiple-.service.d/20-override.conf
-        unit-with-multiple-.service.d/30-override.conf
-        unit-with-multiple-dashes.service
-        unit-with-multiple-dashes.service.d/10-override.conf
-        unstoppable.service
-'''.split()
+if install_tests
+        testdata_dir = testsdir + '/testdata/'
 
-if conf.get('ENABLE_RESOLVE') == 1
-        test_data_files += '''
-                test-resolve/_openpgpkey.fedoraproject.org.pkts
-                test-resolve/fedoraproject.org.pkts
-                test-resolve/gandi.net.pkts
-                test-resolve/google.com.pkts
-                test-resolve/root.pkts
-                test-resolve/sw1a1aa-sw1a2aa-sw1a2ab-sw1a2ac.find.me.uk.pkts
-                test-resolve/teamits.com.pkts
-                test-resolve/zbyszek@fedoraproject.org.pkts
-                test-resolve/_443._tcp.fedoraproject.org.pkts
-                test-resolve/kyhwana.org.pkts
-                test-resolve/fake-caa.pkts
-        '''.split()
-endif
+        install_subdir('journal-data',
+                       install_dir : testdata_dir)
+        install_subdir('units',
+                       install_dir : testdata_dir)
+        install_subdir('test-execute',
+                       install_dir : testdata_dir)
+        install_subdir('test-path',
+                       install_dir : testdata_dir)
+        install_subdir('test-umount',
+                       install_dir : testdata_dir)
+        install_subdir('test-network-generator-conversion',
+                       install_dir : testdata_dir)
+        install_subdir('testsuite-04.units',
+                       install_dir : testdata_dir)
+        install_subdir('testsuite-06.units',
+                       install_dir : testdata_dir)
+        install_subdir('testsuite-10.units',
+                       install_dir : testdata_dir)
+        install_subdir('testsuite-11.units',
+                       install_dir : testdata_dir)
+        install_subdir('testsuite-16.units',
+                       install_dir : testdata_dir)
+        install_subdir('testsuite-28.units',
+                       install_dir : testdata_dir)
+        install_subdir('testsuite-30.units',
+                       install_dir : testdata_dir)
+        install_subdir('testsuite-52.units',
+                       install_dir : testdata_dir)
 
-if install_tests
-        foreach file : test_data_files
-                subdir = file.split('/')[0]
-                if subdir == file
-                        subdir = ''
-                endif
+        testsuite08_dir = testdata_dir + '/testsuite-08.units'
+        install_data('testsuite-08.units/-.mount',
+                     install_dir : testsuite08_dir)
+        install_data('testsuite-08.units/systemd-remount-fs.service',
+                     install_dir : testsuite08_dir)
+        meson.add_install_script(meson_make_symlink,
+                                 './-.mount',
+                                 testsuite08_dir + '/root.mount')
+        meson.add_install_script(meson_make_symlink,
+                                 '../-.mount',
+                                 testsuite08_dir + '/local-fs.target.wants/-.mount')
+
+        if conf.get('ENABLE_RESOLVE') == 1
+                install_subdir('test-resolve',
+                               install_dir : testdata_dir)
+        endif
 
-                install_data(file,
-                             install_dir : testsdir + '/testdata/' + subdir)
-        endforeach
+        install_data('create-busybox-container',
+                     install_mode : 'rwxr-xr-x',
+                     install_dir : testdata_dir)
 endif
 
+test_network_generator_conversion_sh = find_program('test-network-generator-conversion.sh')
+
 ############################################################
 
 rule_syntax_check_py = find_program('rule-syntax-check.py')
@@ -257,6 +81,9 @@ if install_tests
         install_data('run-unit-tests.py',
                      install_mode : 'rwxr-xr-x',
                      install_dir : testsdir)
+        install_data('test-network-generator-conversion.sh',
+                     install_mode : 'rwxr-xr-x',
+                     install_dir : testsdir)
 endif
 
 ############################################################
index 618237161a3f18b6648b1fcb36e9b248a1163fc8..b225694819447cc06023d7ba7422275ab98a04f1 100755 (executable)
@@ -376,7 +376,7 @@ DHCP={}
                 # IPv6, but we want to wait for both
                 for _ in range(10):
                     out = subprocess.check_output(['ip', 'a', 'show', 'dev', self.iface])
-                    if b'state UP' in out and b'inet6 2600' in out and b'inet 192.168' in out:
+                    if b'state UP' in out and b'inet6 2600' in out and b'inet 192.168' in out and b'tentative' not in out:
                         break
                     time.sleep(1)
                 else:
diff --git a/test/nomem.slice b/test/nomem.slice
deleted file mode 100644 (file)
index 9c5d208..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-[Unit]
-Description=Nomem Parent Slice
-
-[Slice]
-DisableControllers=memory
diff --git a/test/nomemleaf.service b/test/nomemleaf.service
deleted file mode 100644 (file)
index 3cbaccb..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-[Unit]
-Description=Nomem Leaf Service
-
-[Service]
-Slice=nomem.slice
-Type=oneshot
-ExecStart=/bin/true
-IOWeight=200
-MemoryAccounting=true
diff --git a/test/parent-deep.slice b/test/parent-deep.slice
deleted file mode 100644 (file)
index 79b302f..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-[Unit]
-Description=Deeper Parent Slice
-
-[Slice]
-MemoryLimit=3G
diff --git a/test/parent.slice b/test/parent.slice
deleted file mode 100644 (file)
index a95f903..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-[Unit]
-Description=Parent Slice
-
-[Slice]
-IOWeight=200
index c0a8448a88a6f95175075ad27b9b1a45741b0968..ac7a28cf2cd32f62e1be6ced1d577ee999e64eea 100755 (executable)
@@ -5,41 +5,71 @@ BUILD_DIR="$($(dirname "$0")/../tools/find-build-dir.sh)"
 if [ $# -gt 0 ]; then
     args="$@"
 else
-    args="clean setup run clean-again"
+    args="setup run clean-again"
 fi
+args_no_clean=$(sed -r 's/(^| )clean($| )/ /g' <<<$args)
+do_clean=$( [ "$args" = "$args_no_clean" ]; echo $? )
 
 ninja -C "$BUILD_DIR"
 
 declare -A results
+declare -A times
 
 COUNT=0
 FAILURES=0
 
 cd "$(dirname "$0")"
+
+# Let's always do the cleaning operation first, because it destroys the image
+# cache.
+if [ $do_clean = 1 ]; then
+    for TEST in TEST-??-* ; do
+        ( set -x ; make -C "$TEST" "BUILD_DIR=$BUILD_DIR" clean )
+    done
+fi
+
+pass_blacklist() {
+    for marker in $BLACKLIST_MARKERS; do
+        if [ -f "$1/$marker" ]; then
+            echo "========== BLACKLISTED: $1 ($marker) =========="
+            return 1
+        fi
+    done
+    return 0
+}
+
 for TEST in TEST-??-* ; do
     COUNT=$(($COUNT+1))
 
+    pass_blacklist $TEST || continue
+    start=$(date +%s)
+
     echo -e "\n--x-- Running $TEST --x--"
     set +e
-    ( set -x ; make -C "$TEST" "BUILD_DIR=$BUILD_DIR" $args )
+    ( set -x ; make -C "$TEST" "BUILD_DIR=$BUILD_DIR" $args_no_clean )
     RESULT=$?
     set -e
     echo "--x-- Result of $TEST: $RESULT --x--"
 
     results["$TEST"]="$RESULT"
+    times["$TEST"]=$(( $(date +%s) - $start ))
 
     [ "$RESULT" -ne "0" ] && FAILURES=$(($FAILURES+1))
 done
 
+if [ $FAILURES -eq 0 -a $do_clean = 1 ]; then
+    for TEST in ${!results[@]}; do
+        ( set -x ; make -C "$TEST" "BUILD_DIR=$BUILD_DIR" clean-again )
+    done
+fi
+
 echo ""
 
 for TEST in ${!results[@]}; do
     RESULT="${results[$TEST]}"
-    if [ "$RESULT" -eq "0" ] ; then
-        echo "$TEST: SUCCESS"
-    else
-        echo "$TEST: FAIL"
-    fi
+    time="${times[$TEST]}"
+    string=$([ "$RESULT" = "0" ] && echo "SUCCESS" || echo "FAIL")
+    printf "%-35s %-8s (%3s s)\n" "${TEST}:" "${string}" "$time"
 done | sort
 
 if [ "$FAILURES" -eq 0 ] ; then
diff --git a/test/sched_idle_bad.service b/test/sched_idle_bad.service
deleted file mode 100644 (file)
index 589a87c..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-[Unit]
-Description=Bad sched priority for Idle
-
-[Service]
-ExecStart=/bin/true
-CPUSchedulingPriority=1
diff --git a/test/sched_idle_ok.service b/test/sched_idle_ok.service
deleted file mode 100644 (file)
index 262ef3e..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-[Unit]
-Description=Sched idle with prio 0
-
-[Service]
-ExecStart=/bin/true
-CPUSchedulingPriority=0
diff --git a/test/sched_rr_bad.service b/test/sched_rr_bad.service
deleted file mode 100644 (file)
index 0be534a..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-[Unit]
-Description=Bad sched priority for RR
-
-[Service]
-ExecStart=/bin/true
-CPUSchedulingPolicy=rr
-CPUSchedulingPriority=0
-CPUSchedulingPriority=100
diff --git a/test/sched_rr_change.service b/test/sched_rr_change.service
deleted file mode 100644 (file)
index b3e3a00..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-[Unit]
-Description=Change prio
-
-[Service]
-ExecStart=/bin/true
-CPUSchedulingPolicy=rr
-CPUSchedulingPriority=1
-CPUSchedulingPriority=2
-CPUSchedulingPriority=99
diff --git a/test/sched_rr_ok.service b/test/sched_rr_ok.service
deleted file mode 100644 (file)
index b88adc5..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-[Unit]
-Description=Default prio for RR
-
-[Service]
-ExecStart=/bin/true
-CPUSchedulingPolicy=rr
diff --git a/test/shutdown.target b/test/shutdown.target
deleted file mode 120000 (symlink)
index 1a3c2ee..0000000
+++ /dev/null
@@ -1 +0,0 @@
-../units/shutdown.target
\ No newline at end of file
diff --git a/test/sleep.service b/test/sleep.service
deleted file mode 100644 (file)
index 946c44b..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-[Unit]
-Description=Sleep for 1 minute
-
-[Service]
-Type=oneshot
-ExecStart=/bin/sleep 60
diff --git a/test/sockets.target b/test/sockets.target
deleted file mode 120000 (symlink)
index 8ff86a0..0000000
+++ /dev/null
@@ -1 +0,0 @@
-../units/sockets.target
\ No newline at end of file
diff --git a/test/son.service b/test/son.service
deleted file mode 100644 (file)
index 50bb96a..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-[Unit]
-Description=Son Service
-
-[Service]
-Slice=parent.slice
-Type=oneshot
-ExecStart=/bin/true
-CPUShares=100
diff --git a/test/sysinit.target b/test/sysinit.target
deleted file mode 120000 (symlink)
index 3301338..0000000
+++ /dev/null
@@ -1 +0,0 @@
-../units/sysinit.target
\ No newline at end of file
diff --git a/test/test-execute/exec-restrictnamespaces-mnt-blacklist.service b/test/test-execute/exec-restrictnamespaces-mnt-blacklist.service
deleted file mode 100644 (file)
index 7756a25..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-[Unit]
-Description=Test RestrictNamespaces=~mnt
-
-[Service]
-RestrictNamespaces=~mnt
-ExecStart=unshare -m
-Type=oneshot
diff --git a/test/test-execute/exec-restrictnamespaces-mnt-deny-list.service b/test/test-execute/exec-restrictnamespaces-mnt-deny-list.service
new file mode 100644 (file)
index 0000000..7756a25
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=Test RestrictNamespaces=~mnt
+
+[Service]
+RestrictNamespaces=~mnt
+ExecStart=unshare -m
+Type=oneshot
index 7c3f81f2b53fec4bc2294c86708e98c10846260f..a58abc68dd6057e355eb25f63b3648194de7c781 100644 (file)
@@ -27,5 +27,5 @@ ExecStart=test %h = /root
 ExecStart=sh -c 'test %s = /bin/sh'
 ExecStart=sh -c 'test %m = $$(cat /etc/machine-id)'
 ExecStart=sh -c 'test %b = $$(cat /proc/sys/kernel/random/boot_id | sed -e 's/-//g')'
-ExecStart=sh -c 'test %H = $$(hostname)'
+ExecStart=sh -c 'test %H = $$(uname -n)'
 ExecStart=sh -c 'test %v = $$(uname -r)'
index a388926846a18b8a4cc5f1209d02757c5066ab93..faecbf37bc5965a89caabe47fce13acfb634ec0a 100644 (file)
@@ -25,5 +25,5 @@ ExecStart=test %h = /root
 ExecStart=sh -c 'test %s = /bin/sh'
 ExecStart=sh -c 'test %m = $$(cat /etc/machine-id)'
 ExecStart=sh -c 'test %b = $$(cat /proc/sys/kernel/random/boot_id | sed -e 's/-//g')'
-ExecStart=sh -c 'test %H = $$(hostname)'
+ExecStart=sh -c 'test %H = $$(uname -n)'
 ExecStart=sh -c 'test %v = $$(uname -r)'
index 0876ed3be862e4aeb47efe914e33851c4d65a0ed..863cc18aeff0d29cbe9e801b7f72c70f5604768e 100644 (file)
@@ -14,8 +14,12 @@ NSPAWN_TIMEOUT="${NSPAWN_TIMEOUT:-infinity}"
 TIMED_OUT=  # will be 1 after run_* if *_TIMEOUT is set and test timed out
 [[ "$LOOKS_LIKE_SUSE" ]] && FSTYPE="${FSTYPE:-btrfs}" || FSTYPE="${FSTYPE:-ext4}"
 UNIFIED_CGROUP_HIERARCHY="${UNIFIED_CGROUP_HIERARCHY:-default}"
-EFI_MOUNT="$(bootctl -x 2>/dev/null || echo /boot)"
+EFI_MOUNT="${EFI_MOUNT:-$(bootctl -x 2>/dev/null || echo /boot)}"
 QEMU_MEM="${QEMU_MEM:-512M}"
+IMAGE_NAME=${IMAGE_NAME:-default}
+TEST_REQUIRE_INSTALL_TESTS="${TEST_REQUIRE_INSTALL_TESTS:-1}"
+TEST_PARALLELIZE="${TEST_PARALLELIZE:-0}"
+LOOPDEV=
 
 # Decide if we can (and want to) run QEMU with KVM acceleration.
 # Check if nested KVM is explicitly enabled (TEST_NESTED_KVM). If not,
@@ -35,15 +39,111 @@ fi
 
 PATH_TO_INIT=$ROOTLIBDIR/systemd
 [ "$SYSTEMD_JOURNALD" ] || SYSTEMD_JOURNALD=$(which -a $BUILD_DIR/systemd-journald $ROOTLIBDIR/systemd-journald 2>/dev/null | grep '^/' -m1)
+[ "$SYSTEMD_JOURNAL_REMOTE" ] || SYSTEMD_JOURNAL_REMOTE=$(which -a $BUILD_DIR/systemd-journal-remote $ROOTLIBDIR/systemd-journal-remote 2>/dev/null | grep '^/' -m1)
 [ "$SYSTEMD" ] || SYSTEMD=$(which -a $BUILD_DIR/systemd $ROOTLIBDIR/systemd 2>/dev/null | grep '^/' -m1)
 [ "$SYSTEMD_NSPAWN" ] || SYSTEMD_NSPAWN=$(which -a $BUILD_DIR/systemd-nspawn systemd-nspawn 2>/dev/null | grep '^/' -m1)
 [ "$JOURNALCTL" ] || JOURNALCTL=$(which -a $BUILD_DIR/journalctl journalctl 2>/dev/null | grep '^/' -m1)
 
-BASICTOOLS="test env sh bash setsid loadkeys setfont login sulogin gzip sleep echo head tail cat mount umount cryptsetup date dmsetup modprobe sed cmp tee rm true false chmod chown ln xargs"
-DEBUGTOOLS="df free ls stty ps ln ip route dmesg dhclient mkdir cp ping dhclient strace less grep id tty touch du sort hostname find vi mv"
+BASICTOOLS=(
+    awk
+    basename
+    bash
+    busybox
+    capsh
+    cat
+    chmod
+    chown
+    cmp
+    cryptsetup
+    cut
+    date
+    dd
+    diff
+    dirname
+    dmsetup
+    echo
+    env
+    false
+    getent
+    getfacl
+    grep
+    gunzip
+    gzip
+    head
+    ionice
+    ip
+    ln
+    loadkeys
+    login
+    lz4cat
+    mkfifo
+    mktemp
+    modprobe
+    mount
+    mountpoint
+    mv
+    nc
+    nproc
+    readlink
+    rev
+    rm
+    rmdir
+    sed
+    seq
+    setfont
+    setsid
+    sfdisk
+    sh
+    sleep
+    socat
+    stat
+    su
+    sulogin
+    sysctl
+    tail
+    tar
+    tee
+    test
+    timeout
+    touch
+    tr
+    true
+    truncate
+    umount
+    uname
+    unshare
+    xargs
+    xzcat
+)
+
+DEBUGTOOLS=(
+    cp
+    df
+    dhclient
+    dmesg
+    du
+    find
+    free
+    grep
+    hostname
+    id
+    less
+    ln
+    ls
+    mkdir
+    ping
+    ps
+    route
+    sort
+    strace
+    stty
+    tty
+    vi
+)
 
 STATEDIR="${BUILD_DIR:-.}/test/$(basename $(dirname $(realpath $0)))"
 STATEFILE="$STATEDIR/.testdir"
+IMAGESTATEDIR="$STATEDIR/.."
 TESTLOG="$STATEDIR/test.log"
 
 is_built_with_asan() {
@@ -138,6 +238,11 @@ run_qemu() {
 
     CONSOLE=ttyS0
 
+    rm -f "$initdir"/{testok,failed,skipped}
+    # make sure the initdir is not mounted to avoid concurrent access
+    cleanup_initdir
+    umount_loopback
+
     if [[ ! "$KERNEL_BIN" ]]; then
         if [[ "$LOOKS_LIKE_ARCH" ]]; then
             KERNEL_BIN=/boot/vmlinuz-linux
@@ -184,6 +289,9 @@ run_qemu() {
 
     find_qemu_bin || return 1
 
+    # Umount initdir to avoid concurrent access to the filesystem
+    _umount_dir $initdir
+
     local _cgroup_args
     if [[ "$UNIFIED_CGROUP_HIERARCHY" = "yes" ]]; then
         _cgroup_args="systemd.unified_cgroup_hierarchy=yes"
@@ -198,14 +306,18 @@ run_qemu() {
 
     if [[ "$LOOKS_LIKE_SUSE" ]]; then
         PARAMS+="rd.hostonly=0"
-    elif [[ "$LOOKS_LIKE_ARCH" ]]; then
-        PARAMS+="rw"
+    fi
+
+    local _end
+    if [[ ! "$INTERACTIVE_DEBUG" ]]; then
+        _end="systemd.wants=end.service"
     else
-        PARAMS+="ro"
+        _end=""
     fi
 
     KERNEL_APPEND="$PARAMS \
 root=/dev/sda1 \
+rw \
 raid=noautodetect \
 rd.luks=0 \
 loglevel=2 \
@@ -213,15 +325,19 @@ init=$PATH_TO_INIT \
 console=$CONSOLE \
 selinux=0 \
 $_cgroup_args \
+SYSTEMD_UNIT_PATH=/usr/lib/systemd/tests/testdata/testsuite-$1.units:/usr/lib/systemd/tests/testdata/units: \
+systemd.unit=testsuite.target \
+systemd.wants=testsuite-$1.service ${_end} \
 $KERNEL_APPEND \
 "
 
+    [ -e "$IMAGE_PRIVATE" ] && image="$IMAGE_PRIVATE" || image="$IMAGE_PUBLIC"
     QEMU_OPTIONS="-smp $QEMU_SMP \
 -net none \
 -m $QEMU_MEM \
 -nographic \
 -kernel $KERNEL_BIN \
--drive format=raw,cache=unsafe,file=${TESTDIR}/rootdisk.img \
+-drive format=raw,cache=unsafe,file=$image \
 $QEMU_OPTIONS \
 "
 
@@ -252,25 +368,43 @@ $QEMU_OPTIONS \
 # success), or 1 if nspawn is not available.
 run_nspawn() {
     [[ -d /run/systemd/system ]] || return 1
+    rm -f "$initdir"/{testok,failed,skipped}
+
+    local _nspawn_cmd=(
+        --register=no
+        --kill-signal=SIGKILL
+        --directory=$1
+        --setenv=SYSTEMD_UNIT_PATH=/usr/lib/systemd/tests/testdata/testsuite-$2.units:/usr/lib/systemd/tests/testdata/units:
+        $PATH_TO_INIT
+        $KERNEL_APPEND
+        systemd.unit=testsuite.target
+        systemd.wants=testsuite-$2.service
+    )
+
+    if [[ ! "$INTERACTIVE_DEBUG" ]]; then
+        _nspawn_cmd+=( systemd.wants=end.service )
+    fi
 
-    local _nspawn_cmd="$SYSTEMD_NSPAWN $NSPAWN_ARGUMENTS --register=no --kill-signal=SIGKILL --directory=$TESTDIR/$1 $PATH_TO_INIT $KERNEL_APPEND"
+    local _nspawn_pre
     if [[ "$NSPAWN_TIMEOUT" != "infinity" ]]; then
-        _nspawn_cmd="timeout --foreground $NSPAWN_TIMEOUT $_nspawn_cmd"
+        _nspawn_pre=(timeout --foreground $NSPAWN_TIMEOUT)
+    else
+        _nspawn_pre=()
     fi
 
     if [[ "$UNIFIED_CGROUP_HIERARCHY" = "hybrid" ]]; then
         dwarn "nspawn doesn't support SYSTEMD_NSPAWN_UNIFIED_HIERARCHY=hybrid, skipping"
         exit
     elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "yes" || "$UNIFIED_CGROUP_HIERARCHY" = "no" ]]; then
-        _nspawn_cmd="env SYSTEMD_NSPAWN_UNIFIED_HIERARCHY=$UNIFIED_CGROUP_HIERARCHY $_nspawn_cmd"
+        _nspawn_pre=("${_nspawn_pre[@]}" env SYSTEMD_NSPAWN_UNIFIED_HIERARCHY=$UNIFIED_CGROUP_HIERARCHY)
     elif [[ "$UNIFIED_CGROUP_HIERARCHY" = "default" ]]; then
-        _nspawn_cmd="env --unset=UNIFIED_CGROUP_HIERARCHY --unset=SYSTEMD_NSPAWN_UNIFIED_HIERARCHY $_nspawn_cmd"
+        _nspawn_pre=("${_nspawn_pre[@]}" env --unset=UNIFIED_CGROUP_HIERARCHY --unset=SYSTEMD_NSPAWN_UNIFIED_HIERARCHY)
     else
         dfatal "Unknown UNIFIED_CGROUP_HIERARCHY. Got $UNIFIED_CGROUP_HIERARCHY, expected [yes|no|hybrid|default]"
         exit 1
     fi
 
-    (set -x; $_nspawn_cmd)
+    (set -x; "${_nspawn_pre[@]}" "$SYSTEMD_NSPAWN" $NSPAWN_ARGUMENTS "${_nspawn_cmd[@]}")
     rc=$?
     if [ "$rc" = 124 ] && [ "$NSPAWN_TIMEOUT" != "infinity" ]; then
         derror "test timed out after $NSPAWN_TIMEOUT s"
@@ -288,6 +422,7 @@ setup_basic_environment() {
     install_systemd
     install_missing_libraries
     install_config_files
+    install_zoneinfo
     create_rc_local
     install_basic_tools
     install_libnss
@@ -301,6 +436,8 @@ setup_basic_environment() {
     install_plymouth
     install_debug_tools
     install_ld_so_conf
+    install_testuser
+    has_user_dbus_socket && install_user_dbus
     setup_selinux
     strip_binaries
     install_depmod_files
@@ -326,27 +463,9 @@ setup_selinux() {
         exit 1
     fi
 
-    cat <<EOF >$initdir/etc/systemd/system/autorelabel.service
-[Unit]
-Description=Relabel all filesystems
-DefaultDependencies=no
-Requires=local-fs.target
-Conflicts=shutdown.target
-After=local-fs.target
-Before=sysinit.target shutdown.target
-ConditionSecurity=selinux
-ConditionPathExists=|/.autorelabel
-
-[Service]
-ExecStart=/bin/sh -x -c 'echo 0 >/sys/fs/selinux/enforce && fixfiles -f -F relabel && rm /.autorelabel && systemctl --force reboot'
-Type=oneshot
-TimeoutSec=0
-RemainAfterExit=yes
-EOF
-
     touch $initdir/.autorelabel
-    mkdir -p $initdir/etc/systemd/system/basic.target.wants
-    ln -fs autorelabel.service $initdir/etc/systemd/system/basic.target.wants/autorelabel.service
+    mkdir -p $initdir/usr/lib/systemd/tests/testdata/units/basic.target.wants
+    ln -sf ../autorelabel.service $initdir/usr/lib/systemd/tests/testdata/units/basic.target.wants/
 
     dracut_install $_fixfiles_tools
     dracut_install fixfiles
@@ -454,7 +573,7 @@ printf "[Service]\nStandardOutput=file:/systemd-journald.out\n" >"\$JOURNALD_CON
 # under ASan+UBSan in containers, which, in turn, are run in VMs.
 # Let's limit which environments such services should be executed in.
 mkdir -p /etc/systemd/system/systemd-hwdb-update.service.d
-printf "[Unit]\nConditionVirtualization=container\n\n[Service]\nTimeoutSec=180s\n" >/etc/systemd/system/systemd-hwdb-update.service.d/env-override.conf
+printf "[Unit]\nConditionVirtualization=container\n\n[Service]\nTimeoutSec=240s\n" >/etc/systemd/system/systemd-hwdb-update.service.d/env-override.conf
 
 # Let's override another hard-coded timeout that kicks in too early
 mkdir -p /etc/systemd/system/systemd-journal-flush.service.d
@@ -475,7 +594,7 @@ unset_ld_preload() {
 }
 
 unset_ld_preload systemd-remount-fs
-unset_ld_preload testsuite
+unset_ld_preload testsuite-
 
 export ASAN_OPTIONS=\$DEFAULT_ASAN_OPTIONS:log_path=/systemd.asan.log UBSAN_OPTIONS=\$DEFAULT_UBSAN_OPTIONS
 exec  $ROOTLIBDIR/systemd "\$@"
@@ -514,6 +633,9 @@ install_dmevent() {
     else
         inst_rules 10-dm.rules 13-dm-disk.rules 95-dm-notify.rules
     fi
+    if [[ "$LOOKS_LIKE_SUSE" ]]; then
+        inst_rules 60-persistent-storage.rules 61-persistent-storage-compat.rules 99-systemd.rules
+    fi
 }
 
 install_systemd() {
@@ -530,7 +652,7 @@ install_systemd() {
     # and it could fill the available space
     strip_binaries
 
-   [[ "$LOOKS_LIKE_SUSE" ]] && setup_suse
+    [[ "$LOOKS_LIKE_SUSE" ]] && setup_suse
 
     # enable debug logging in PID1
     echo LogLevel=debug >> $initdir/etc/systemd/system.conf
@@ -556,17 +678,35 @@ install_missing_libraries() {
     done
 }
 
+cleanup_loopdev() {
+    if [ -n "${LOOPDEV}" ]; then
+        ddebug "losetup -d $LOOPDEV"
+        losetup -d "${LOOPDEV}"
+        unset LOOPDEV
+    fi
+}
+
+trap cleanup_loopdev EXIT INT QUIT PIPE
+
 create_empty_image() {
+    if [ -z "$IMAGE_NAME" ]; then
+        echo "create_empty_image: \$IMAGE_NAME not set"
+        exit 1
+    fi
+
     local _size=500
     if [[ "$STRIP_BINARIES" = "no" ]]; then
         _size=$((4*_size))
     fi
-    rm -f "$TESTDIR/rootdisk.img"
+
+    echo "Setting up $IMAGE_PUBLIC (${_size} MB)"
+    rm -f "$IMAGE_PRIVATE" "$IMAGE_PUBLIC"
+
     # Create the blank file to use as a root filesystem
-    truncate -s "${_size}M" "$TESTDIR/rootdisk.img"
-    LOOPDEV=$(losetup --show -P -f $TESTDIR/rootdisk.img)
+    truncate -s "${_size}M" "$IMAGE_PUBLIC"
+
+    LOOPDEV=$(losetup --show -P -f "$IMAGE_PUBLIC")
     [ -b "$LOOPDEV" ] || return 1
-    echo "LOOPDEV=$LOOPDEV" >> $STATEFILE
     sfdisk "$LOOPDEV" <<EOF
 ,$((_size-50))M
 ,
@@ -574,20 +714,49 @@ EOF
 
     udevadm settle
 
-    local _label="-L systemd"
+    local _label="-L systemd.${name}"
     # mkfs.reiserfs doesn't know -L. so, use --label instead
-    [[ "$FSTYPE" == "reiserfs" ]] && _label="--label systemd"
-    if ! mkfs -t "${FSTYPE}" ${_label} "${LOOPDEV}p1" -q; then
+    [[ "$FSTYPE" == "reiserfs" ]] && _label="--label systemd.${name}"
+    mkfs -t "${FSTYPE}" ${_label} "${LOOPDEV}p1" -q; ret=$?
+    if [ $ret -ne 0 ] ; then
         dfatal "Failed to mkfs -t ${FSTYPE}"
         exit 1
     fi
 }
 
+mount_initdir() {
+    if [ -z "${LOOPDEV}" ]; then
+        [ -e "$IMAGE_PRIVATE" ] && image="$IMAGE_PRIVATE" || image="$IMAGE_PUBLIC"
+        LOOPDEV=$(losetup --show -P -f "$image")
+        [ -b "$LOOPDEV" ] || return 1
+
+        udevadm settle
+    fi
+
+    if ! mountpoint -q $initdir; then
+        mkdir -p $initdir
+        mount ${LOOPDEV}p1 $initdir
+        TEST_SETUP_CLEANUP_ROOTDIR=1
+    fi
+}
+
+cleanup_initdir() {
+    # only umount if create_empty_image_rootdir() was called to mount it
+    [[ -z $TEST_SETUP_CLEANUP_ROOTDIR ]] || _umount_dir $initdir
+}
+
+umount_loopback() {
+    # unmount the loopback device from all places. Otherwise we risk file
+    # system corruption.
+    for device in $(losetup -l | awk '$6=="'"$IMAGE_PUBLIC"'" {print $1}'); do
+        ddebug "Unmounting all uses of $device"
+        mount | awk '/^'"${device}"'p/{print $1}' | xargs --no-run-if-empty umount -v
+    done
+}
+
 create_empty_image_rootdir() {
     create_empty_image
-    mkdir -p $initdir
-    mount ${LOOPDEV}p1 $initdir
-    TEST_SETUP_CLEANUP_ROOTDIR=1
+    mount_initdir
 }
 
 check_asan_reports() {
@@ -628,33 +797,57 @@ check_asan_reports() {
     return $ret
 }
 
+save_journal() {
+    if [ -n "${ARTIFACT_DIRECTORY}" ]; then
+        dest="${ARTIFACT_DIRECTORY}/${testname}.journal"
+    else
+        dest="$TESTDIR/system.journal"
+    fi
+
+    for j in $1/*; do
+        $SYSTEMD_JOURNAL_REMOTE \
+            -o $dest \
+            --getter="$JOURNALCTL -o export -D $j"
+
+        if [ -n "${TEST_SHOW_JOURNAL}" ]; then
+            echo "---- $j ----"
+            $JOURNALCTL --no-pager -o short-monotonic --no-hostname --priority=${TEST_SHOW_JOURNAL} -D $j
+        fi
+
+        rm -r $j
+    done
+
+    # we want to print this sometime later, so save this in a variable
+    JOURNAL_LIST="$(ls -l $dest*)"
+}
+
 check_result_nspawn() {
     local ret=1
     local journald_report=""
     local pids=""
-    [[ -e $TESTDIR/$1/testok ]] && ret=0
-    [[ -f $TESTDIR/$1/failed ]] && cp -a $TESTDIR/$1/failed $TESTDIR
-    cp -a $TESTDIR/$1/var/log/journal $TESTDIR
+    [[ -e $1/testok ]] && ret=0
+    [[ -f $1/failed ]] && cp -a $1/failed $TESTDIR
+    save_journal $1/var/log/journal
     [[ -f $TESTDIR/failed ]] && cat $TESTDIR/failed
-    ls -l $TESTDIR/journal/*/*.journal
+    echo $JOURNAL_LIST
     test -s $TESTDIR/failed && ret=$(($ret+1))
     [ -n "$TIMED_OUT" ] && ret=$(($ret+1))
-    check_asan_reports "$TESTDIR/$1" || ret=$(($ret+1))
+    check_asan_reports "$1" || ret=$(($ret+1))
+    _umount_dir $initdir
     return $ret
 }
 
 # can be overridden in specific test
 check_result_qemu() {
     local ret=1
-    mkdir -p $initdir
-    mount ${LOOPDEV}p1 $initdir
+    mount_initdir
     [[ -e $initdir/testok ]] && ret=0
     [[ -f $initdir/failed ]] && cp -a $initdir/failed $TESTDIR
-    cp -a $initdir/var/log/journal $TESTDIR
+    save_journal $initdir/var/log/journal
     check_asan_reports "$initdir" || ret=$(($ret+1))
-    umount $initdir
+    _umount_dir $initdir
     [[ -f $TESTDIR/failed ]] && cat $TESTDIR/failed
-    ls -l $TESTDIR/journal/*/*.journal
+    echo $JOURNAL_LIST
     test -s $TESTDIR/failed && ret=$(($ret+1))
     [ -n "$TIMED_OUT" ] && ret=$(($ret+1))
     return $ret
@@ -693,7 +886,7 @@ install_execs() {
          # also, plymouth is pulled in by rescue.service, but even there the exit code
          # is ignored; as it's not present on some distros, don't fail if it doesn't exist
          dinfo "Attempting to install $i"
-         inst $i || [ "${i%.local}" != "$i" ] || [ "${i%systemd-update-done}" != "$i" ] || [ "/bin/plymouth" == "$i" ]
+         inst $i || [ "${i%.local}" != "$i" ] || [ "${i%systemd-update-done}" != "$i" ] || [ "${i##*/}" == "plymouth" ]
      done
     )
 }
@@ -718,7 +911,7 @@ install_plymouth() {
     #         /usr/libexec/plymouth/plymouth-populate-initrd -t $initdir
     #         dracut_install plymouth plymouthd
     # else
-        rm -f $initdir/{usr/lib,etc}/systemd/system/plymouth* $initdir/{usr/lib,etc}/systemd/system/*/plymouth*
+        rm -f $initdir/{usr/lib,lib,etc}/systemd/system/plymouth* $initdir/{usr/lib,lib,etc}/systemd/system/*/plymouth*
     # fi
 }
 
@@ -727,11 +920,22 @@ install_ld_so_conf() {
     ldconfig -r "$initdir"
 }
 
+install_testuser() {
+    # create unprivileged user for user manager tests
+    mkdir -p $initdir/etc/sysusers.d
+    cat >$initdir/etc/sysusers.d/testuser.conf <<EOF
+u testuser    4711     "Test User" /home/testuser
+EOF
+
+    mkdir -p $initdir/home/testuser -m 0700
+    chown 4711:4711 $initdir/home/testuser
+}
+
 install_config_files() {
     inst /etc/sysconfig/init || :
     inst /etc/passwd
     inst /etc/shadow
-    inst /etc/login.defs
+    inst_any /etc/login.defs /usr/etc/login.defs
     inst /etc/group
     inst /etc/shells
     inst_any /etc/nsswitch.conf /usr/etc/nsswitch.conf
@@ -741,29 +945,26 @@ install_config_files() {
     # we want an empty environment
     > $initdir/etc/environment
     > $initdir/etc/machine-id
+
     # set the hostname
     echo systemd-testsuite > $initdir/etc/hostname
-    # fstab
-    if [[ "$LOOKS_LIKE_SUSE" ]]; then
-       ROOTMOUNT="/dev/sda1           /       ${FSTYPE}    rw 0 1"
-    else
-       ROOTMOUNT="LABEL=systemd           /       ${FSTYPE}    rw 0 1"
-    fi
 
-    cat >$initdir/etc/fstab <<EOF
-$ROOTMOUNT
-EOF
+    # let's set up just one image with the traditional verbose output
+    if [ ${IMAGE_NAME} != "basic" ]; then
+        mkdir -p $initdir/etc/systemd/system.conf.d
+        echo -e '[Manager]\nStatusUnitFormat=name' >$initdir/etc/systemd/system.conf.d/status.conf
+    fi
 }
 
 install_basic_tools() {
-    [[ $BASICTOOLS ]] && dracut_install $BASICTOOLS
+    dracut_install "${BASICTOOLS[@]}"
     dracut_install -o sushell
     # in Debian ldconfig is just a shell script wrapper around ldconfig.real
     dracut_install -o ldconfig.real
 }
 
 install_debug_tools() {
-    [[ $DEBUGTOOLS ]] && dracut_install $DEBUGTOOLS
+    dracut_install "${DEBUGTOOLS[@]}"
 
     if [[ $INTERACTIVE_DEBUG ]]; then
         # Set default TERM from vt220 to linux, so at least basic key shortcuts work
@@ -810,6 +1011,19 @@ install_dbus() {
         | while read file; do
         inst $file
     done
+
+    # setup policy for Type=dbus test
+    mkdir -p $initdir/etc/dbus-1/system.d
+    cat > $initdir/etc/dbus-1/system.d/systemd.test.ExecStopPost.conf <<EOF
+<?xml version="1.0"?>
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+        "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+    <policy user="root">
+        <allow own="systemd.test.ExecStopPost"/>
+    </policy>
+</busconfig>
+EOF
 }
 
 install_user_dbus() {
@@ -885,10 +1099,16 @@ install_keymaps() {
 }
 
 install_zoneinfo() {
-    for i in /usr/share/zoneinfo/{,*/,*/*/}*; do
-        [[ -f $i ]] || continue
-        inst $i
-    done
+    inst_any /usr/share/zoneinfo/Asia/Seoul
+    inst_any /usr/share/zoneinfo/Asia/Vladivostok
+    inst_any /usr/share/zoneinfo/Australia/Sydney
+    inst_any /usr/share/zoneinfo/Europe/Berlin
+    inst_any /usr/share/zoneinfo/Europe/Kiev
+    inst_any /usr/share/zoneinfo/Pacific/Auckland
+    inst_any /usr/share/zoneinfo/Pacific/Honolulu
+    inst_any /usr/share/zoneinfo/CET
+    inst_any /usr/share/zoneinfo/EET
+    inst_any /usr/share/zoneinfo/UTC
 }
 
 install_fonts() {
@@ -916,40 +1136,17 @@ has_user_dbus_socket() {
     fi
 }
 
-enable_user_manager() {
-    has_user_dbus_socket || return 0
-
-    local _userid
-    [[ $# -gt 0 ]] || set -- nobody
-    mkdir -p "$initdir/var/lib/systemd/linger"
-    for _userid; do
-        touch "$initdir/var/lib/systemd/linger/$_userid"
-    done
-    dracut_install su
-    install_user_dbus
-}
-
-setup_testsuite() {
-    cp $TEST_BASE_DIR/testsuite.target $initdir/etc/systemd/system/
-    cp $TEST_BASE_DIR/end.service $initdir/etc/systemd/system/
+setup_nspawn_root() {
+    if [ -z "${initdir}" ]; then
+        dfatal "\$initdir not defined"
+        exit 1
+    fi
 
-    mkdir -p $initdir/etc/systemd/system/testsuite.target.wants
-    ln -fs $TEST_BASE_DIR/testsuite.service $initdir/etc/systemd/system/testsuite.target.wants/testsuite.service
-    # Don't shutdown the machine after running the test when INTERACTIVE_DEBUG is set
-    [[ -z $INTERACTIVE_DEBUG ]] && ln -fs $TEST_BASE_DIR/end.service $initdir/etc/systemd/system/testsuite.target.wants/end.service
+    rm -rf "$TESTDIR/unprivileged-nspawn-root"
 
-    # make the testsuite the default target
-    ln -fs testsuite.target $initdir/etc/systemd/system/default.target
-}
-
-setup_nspawn_root() {
-    rm -fr $TESTDIR/nspawn-root
-    ddebug "cp -ar $initdir $TESTDIR/nspawn-root"
-    cp -ar $initdir $TESTDIR/nspawn-root
-    # we don't mount in the nspawn root
-    rm -f $TESTDIR/nspawn-root/etc/fstab
     if [[ "$RUN_IN_UNPRIVILEGED_CONTAINER" = "yes" ]]; then
-        cp -ar $TESTDIR/nspawn-root $TESTDIR/unprivileged-nspawn-root
+        ddebug "cp -ar $initdir $TESTDIR/unprivileged-nspawn-root"
+        cp -ar $initdir $TESTDIR/unprivileged-nspawn-root
     fi
 }
 
@@ -1005,7 +1202,10 @@ inst_libs() {
 }
 
 import_testdir() {
+    # make sure we don't get a stale LOOPDEV value from old times
+    __LOOPDEV=$LOOPDEV
     [[ -e $STATEFILE ]] && . $STATEFILE
+    LOOPDEV=$__LOOPDEV
     if [[ ! -d "$TESTDIR" ]]; then
         if [[ -z "$TESTDIR" ]]; then
             TESTDIR=$(mktemp --tmpdir=/var/tmp -d -t systemd-test.XXXXXX)
@@ -1013,9 +1213,14 @@ import_testdir() {
             mkdir -p "$TESTDIR"
         fi
 
-        echo "TESTDIR=\"$TESTDIR\"" > $STATEFILE
+        cat >$STATEFILE<<EOF
+TESTDIR="$TESTDIR"
+EOF
         export TESTDIR
     fi
+
+    IMAGE_PRIVATE="${TESTDIR}/${IMAGE_NAME}.img"
+    IMAGE_PUBLIC="${IMAGESTATEDIR}/${IMAGE_NAME}.img"
 }
 
 import_initdir() {
@@ -1746,6 +1951,7 @@ setup_suse() {
     ln -fs ../usr/bin/systemctl $initdir/bin/
     ln -fs ../usr/lib/systemd $initdir/lib/
     inst_simple "/usr/lib/systemd/system/haveged.service"
+    instmods ext4
 }
 
 _umount_dir() {
@@ -1755,14 +1961,9 @@ _umount_dir() {
     fi
 }
 
-_test_setup_cleanup() {
-    # only umount if create_empty_image_rootdir() was called to mount it
-    [[ -z $TEST_SETUP_CLEANUP_ROOTDIR ]] || _umount_dir $initdir
-}
-
 # can be overridden in specific test
 test_setup_cleanup() {
-    _test_setup_cleanup
+    cleanup_initdir
 }
 
 _test_cleanup() {
@@ -1770,12 +1971,9 @@ _test_cleanup() {
     (
         set +e
         _umount_dir $initdir
-        if [[ $LOOPDEV && -b $LOOPDEV ]]; then
-            ddebug "losetup -d $LOOPDEV"
-            losetup -d $LOOPDEV
-        fi
-        rm -fr "$TESTDIR"
-        rm -f "$STATEFILE"
+        rm -vf "$IMAGE_PUBLIC"
+        rm -vfr "$TESTDIR"
+        rm -vf "$STATEFILE"
     ) || :
 }
 
@@ -1784,24 +1982,81 @@ test_cleanup() {
     _test_cleanup
 }
 
+test_cleanup_again() {
+    [ -n "$TESTDIR" ] || return
+    rm -rf "$TESTDIR/unprivileged-nspawn-root"
+    _umount_dir $initdir
+}
+
+test_create_image() {
+    create_empty_image_rootdir
+
+    # Create what will eventually be our root filesystem onto an overlay
+    (
+        LOG_LEVEL=5
+        setup_basic_environment
+        mask_supporting_services
+    )
+}
+
+test_setup() {
+    if [ ${TEST_REQUIRE_INSTALL_TESTS} -ne 0 ] && \
+            type -P meson >/dev/null && \
+            [[ "$(meson configure $BUILD_DIR | grep install-tests | awk '{ print $2 }')" != "true" ]]; then
+        dfatal "$BUILD_DIR needs to be built with -Dinstall-tests=true"
+        exit 1
+    fi
+
+    if [ -e "$IMAGE_PRIVATE" ]; then
+        echo "Reusing existing image $IMAGE_PRIVATE → $(realpath $IMAGE_PRIVATE)"
+        mount_initdir
+    else
+        if [ ! -e "$IMAGE_PUBLIC" ]; then
+            # Create the backing public image, but then completely unmount
+            # it and drop the loopback device responsible for it, since we're
+            # going to symlink/copy the image and mount it again from
+            # elsewhere.
+            test_create_image
+            test_setup_cleanup
+            umount_loopback
+            cleanup_loopdev
+        fi
+
+        echo "Reusing existing cached image $IMAGE_PUBLIC → $(realpath $IMAGE_PUBLIC)"
+        if [ ${TEST_PARALLELIZE} -ne 0 ]; then
+            cp -v "$(realpath $IMAGE_PUBLIC)" "$IMAGE_PRIVATE"
+        else
+            ln -sv "$(realpath $IMAGE_PUBLIC)" "$IMAGE_PRIVATE"
+        fi
+
+        mount_initdir
+    fi
+
+    setup_nspawn_root
+}
+
 test_run() {
+    mount_initdir
+
     if [ -z "$TEST_NO_QEMU" ]; then
-        if run_qemu; then
-            check_result_qemu || return 1
+        if run_qemu "$1"; then
+            check_result_qemu || { echo "QEMU test failed"; return 1; }
         else
             dwarn "can't run QEMU, skipping"
         fi
     fi
     if [ -z "$TEST_NO_NSPAWN" ]; then
-        if run_nspawn "nspawn-root"; then
-            check_result_nspawn "nspawn-root" || return 1
+        mount_initdir
+        if run_nspawn "$initdir" "$1"; then
+            check_result_nspawn "$initdir" || { echo "nspawn-root test failed"; return 1; }
         else
             dwarn "can't run systemd-nspawn, skipping"
         fi
 
         if [[ "$RUN_IN_UNPRIVILEGED_CONTAINER" = "yes" ]]; then
-            if NSPAWN_ARGUMENTS="-U --private-network $NSPAWN_ARGUMENTS" run_nspawn "unprivileged-nspawn-root"; then
-                check_result_nspawn "unprivileged-nspawn-root" || return 1
+            dir="$TESTDIR/unprivileged-nspawn-root"
+            if NSPAWN_ARGUMENTS="-U --private-network $NSPAWN_ARGUMENTS" run_nspawn "$dir" "$1"; then
+                check_result_nspawn "$dir" || { echo "unprivileged-nspawn-root test failed"; return 1; }
             else
                 dwarn "can't run systemd-nspawn, skipping"
             fi
@@ -1830,34 +2085,40 @@ do_test() {
     import_testdir
     import_initdir
 
+    testname="$(basename $PWD)"
+
     while (($# > 0)); do
         case $1 in
             --run)
-                echo "TEST RUN: $TEST_DESCRIPTION"
-                test_run
+                echo "${testname} RUN: $TEST_DESCRIPTION"
+                test_run "$2"
                 ret=$?
                 if (( $ret == 0 )); then
-                    echo "TEST RUN: $TEST_DESCRIPTION [OK]"
+                    echo "${testname} RUN: $TEST_DESCRIPTION [OK]"
                 else
-                    echo "TEST RUN: $TEST_DESCRIPTION [FAILED]"
+                    echo "${testname} RUN: $TEST_DESCRIPTION [FAILED]"
                 fi
                 exit $ret;;
             --setup)
-                echo "TEST SETUP: $TEST_DESCRIPTION"
+                echo "${testname} SETUP: $TEST_DESCRIPTION"
                 test_setup
                 test_setup_cleanup
                 ;;
             --clean)
-                echo "TEST CLEANUP: $TEST_DESCRIPTION"
+                echo "${testname} CLEANUP: $TEST_DESCRIPTION"
                 test_cleanup
                 ;;
+            --clean-again)
+                echo "${testname} CLEANUP AGAIN: $TEST_DESCRIPTION"
+                test_cleanup_again
+                ;;
             --all)
                 ret=0
-                echo -n "TEST: $TEST_DESCRIPTION "
+                echo -n "${testname}: $TEST_DESCRIPTION "
                 (
                     test_setup
                     test_setup_cleanup
-                    test_run
+                    test_run "$2"
                 ) </dev/null >"$TESTLOG" 2>&1 || ret=$?
                 test_cleanup
                 if [ $ret -eq 0 ]; then
diff --git a/test/test-network-generator-conversion.sh b/test/test-network-generator-conversion.sh
new file mode 100755 (executable)
index 0000000..d0d0834
--- /dev/null
@@ -0,0 +1,30 @@
+#!/usr/bin/env bash
+set -ex
+
+if [[ -n "$1" ]]; then
+    generator=$1
+elif [[ -x /usr/lib/systemd/systemd-network-generator ]]; then
+    generator=/usr/lib/systemd/systemd-network-generator
+elif [[ -x /lib/systemd/systemd-network-generator ]]; then
+    generator=/lib/systemd/systemd-network-generator
+else
+    exit 1
+fi
+
+src="$(dirname "$0")/testdata/test-network-generator-conversion"
+
+for f in "$src"/test-*.input; do
+    echo "*** Running $f"
+
+    (
+        out=$(mktemp --directory)
+        trap "rm -rf '$out'" EXIT INT QUIT PIPE
+
+        $generator --root "$out" -- $(cat $f)
+
+        if ! diff -u "$out"/run/systemd/network ${f%.input}.expected; then
+            echo "**** Unexpected output for $f"
+            exit 1
+        fi
+    ) || exit 1
+done
diff --git a/test/test-network-generator-conversion/test-01-dhcp.expected/91-default.network b/test/test-network-generator-conversion/test-01-dhcp.expected/91-default.network
new file mode 100644 (file)
index 0000000..e42ce1e
--- /dev/null
@@ -0,0 +1,11 @@
+# Automatically generated by systemd-network-generator
+
+[Match]
+Name=*
+
+[Link]
+
+[Network]
+DHCP=ipv4
+
+[DHCP]
diff --git a/test/test-network-generator-conversion/test-01-dhcp.input b/test/test-network-generator-conversion/test-01-dhcp.input
new file mode 100644 (file)
index 0000000..e55893e
--- /dev/null
@@ -0,0 +1 @@
+ip=dhcp
diff --git a/test/test-network-generator-conversion/test-02-bridge.expected/90-bridge99.netdev b/test/test-network-generator-conversion/test-02-bridge.expected/90-bridge99.netdev
new file mode 100644 (file)
index 0000000..97c2248
--- /dev/null
@@ -0,0 +1,5 @@
+# Automatically generated by systemd-network-generator
+
+[NetDev]
+Kind=bridge
+Name=bridge99
diff --git a/test/test-network-generator-conversion/test-02-bridge.expected/90-bridge99.network b/test/test-network-generator-conversion/test-02-bridge.expected/90-bridge99.network
new file mode 100644 (file)
index 0000000..f8d19ba
--- /dev/null
@@ -0,0 +1,13 @@
+# Automatically generated by systemd-network-generator
+
+[Match]
+Name=bridge99
+
+[Link]
+MACAddress=00:11:22:33:44:55
+MTUBytes=1530
+
+[Network]
+DHCP=ipv4
+
+[DHCP]
diff --git a/test/test-network-generator-conversion/test-02-bridge.expected/90-eth0.network b/test/test-network-generator-conversion/test-02-bridge.expected/90-eth0.network
new file mode 100644 (file)
index 0000000..8842b57
--- /dev/null
@@ -0,0 +1,21 @@
+# Automatically generated by systemd-network-generator
+
+[Match]
+Name=eth0
+
+[Link]
+
+[Network]
+DHCP=no
+DNS=10.10.10.10
+DNS=10.10.10.11
+Bridge=bridge99
+
+[DHCP]
+Hostname=hogehoge
+
+[Address]
+Address=192.168.0.10/24
+
+[Route]
+Gateway=192.168.0.1
diff --git a/test/test-network-generator-conversion/test-02-bridge.expected/90-eth1.network b/test/test-network-generator-conversion/test-02-bridge.expected/90-eth1.network
new file mode 100644 (file)
index 0000000..feff4f5
--- /dev/null
@@ -0,0 +1,21 @@
+# Automatically generated by systemd-network-generator
+
+[Match]
+Name=eth1
+
+[Link]
+
+[Network]
+DHCP=no
+DNS=10.10.10.10
+DNS=10.10.10.11
+Bridge=bridge99
+
+[DHCP]
+Hostname=hogehoge
+
+[Address]
+Address=192.168.0.11/24
+
+[Route]
+Gateway=192.168.0.1
diff --git a/test/test-network-generator-conversion/test-02-bridge.input b/test/test-network-generator-conversion/test-02-bridge.input
new file mode 100644 (file)
index 0000000..0c863fc
--- /dev/null
@@ -0,0 +1,4 @@
+ip=192.168.0.10::192.168.0.1:255.255.255.0:hogehoge:eth0:off:10.10.10.10:10.10.10.11
+ip=192.168.0.11::192.168.0.1:255.255.255.0:hogehoge:eth1:off:10.10.10.10:10.10.10.11
+ip=bridge99:dhcp:1530:00:11:22:33:44:55
+bridge=bridge99:eth0,eth1
diff --git a/test/test-network-generator-conversion/test-03-issue-14319.expected/90-enp3s0.network b/test/test-network-generator-conversion/test-03-issue-14319.expected/90-enp3s0.network
new file mode 100644 (file)
index 0000000..28ccfdd
--- /dev/null
@@ -0,0 +1,17 @@
+# Automatically generated by systemd-network-generator
+
+[Match]
+Name=enp3s0
+
+[Link]
+
+[Network]
+DHCP=no
+
+[DHCP]
+
+[Address]
+Address=10.99.37.44/16
+
+[Route]
+Gateway=10.99.10.1
diff --git a/test/test-network-generator-conversion/test-03-issue-14319.input b/test/test-network-generator-conversion/test-03-issue-14319.input
new file mode 100644 (file)
index 0000000..3be7520
--- /dev/null
@@ -0,0 +1 @@
+root=/dev/nfs nfsroot=10.99.37.240:/srv/netroot,v3,tcp ip=10.99.37.44::10.99.10.1:255.255.0.0::enp3s0:off
index 31e5d12f7508e60789cf53afcacc3e8912b6e806..cad651156646f76cf9325384b12f7e08adcbf1f6 100644 (file)
@@ -1,2 +1,5 @@
 [Match]
 Name=bond199
+
+[Network]
+IPv6AcceptRA=no
index de35045388c2d930cf21aa169f3766320a1578a4..9edaf871b4cc1a12c8a3317371b154c105cdec58 100644 (file)
@@ -2,4 +2,5 @@
 Name=veth-peer
 
 [Network]
+IPv6AcceptRA=no
 EmitLLDP=yes
index 0c65a1d9e2e43a8db378e58f37a0c3961fe63c23..7602927d81878db61cfa04b3fa7100adc0d46e41 100644 (file)
@@ -2,4 +2,5 @@
 Name=dummy98
 
 [Network]
+IPv6AcceptRA=no
 KeepConfiguration=static
index fbdfb1b6728ec4c6dc3022a45f3001b80604100b..84723138f2186a9c8db8f9c4fcfbae18a8318948 100644 (file)
@@ -2,4 +2,5 @@
 Name=veth99
 
 [Network]
+IPv6AcceptRA=no
 LLDP=yes
index 124af438a3dd9747de84db37d8c52abdbe5f01b4..5c37d2f61f51833075591ba9f5d84953c0f18de0 100644 (file)
@@ -2,6 +2,7 @@
 Name=dummy98
 
 [Network]
+IPv6AcceptRA=no
 Address=192.168.42.100/24
 DNS=192.168.42.1
 Domains= one two three four five six seven eight nine ten
index 759e83c3256264444ae6952b6847ca62598b4bc4..d6ab3402714b9c969794091ba467ee613dff3003 100644 (file)
@@ -3,3 +3,6 @@ Name=dummy98
 
 [Link]
 MACAddress=00:01:02:aa:bb:cc
+
+[Network]
+IPv6AcceptRA=no
index a1e760510319727aaca821089c286d4fd0f8c8a5..d3d02d282fb1c5eb58ece9b8e91c4038afd2445d 100644 (file)
@@ -2,6 +2,8 @@
 Name=dummy98
 
 [Network]
+IPv6AcceptRA=no
+
 # these lines are ignored
 Address=hogehoge
 Address=foofoo
index bcca0c27abebc615b0eff5bc164e00db5d4ce023..b8b368fe5aa168ec82ed0cad6ff810f367c33aea 100644 (file)
@@ -1,6 +1,9 @@
 [Match]
 Name=test1
 
+[Network]
+IPv6AcceptRA=no
+
 [RoutingPolicyRule]
 TypeOfService=0x08
 Table=7
index 36646ec0ff04686cdf2f9f38186a7e924efa74f1..77874b34405c1ca8da2ae4fd71ebb165d57ef402 100644 (file)
@@ -1,6 +1,9 @@
 [Match]
 Name=test1
 
+[Network]
+IPv6AcceptRA=no
+
 [RoutingPolicyRule]
 TypeOfService=0x08
 Table=7
index f42dfee3256caf722739eaa6a8b41ce051e1f759..44716e396a0ea9fbaca4832bec33e2c78cafa7f5 100644 (file)
@@ -1,6 +1,9 @@
 [Match]
 Name=test1
 
+[Network]
+IPv6AcceptRA=no
+
 [RoutingPolicyRule]
 TypeOfService=0x08
 Table=7
index dfac8f48cdbb9277edd38dcbbff491a3a5a4aa98..908e58881efb23b72d75767d1156cd6ad2347b63 100644 (file)
@@ -2,5 +2,6 @@
 Name=dummy98
 
 [Network]
+IPv6AcceptRA=no
 Address=149.10.124.58/28
 Gateway=149.10.124.60
index 448a21f2b9bf634e7c2f0d1bd1bdb224dcc21e92..1ea184fa2ff864231819d963ba06d28d98be5a41 100644 (file)
@@ -2,5 +2,6 @@
 Name=dummy98
 
 [Network]
+IPv6AcceptRA=no
 Address=149.10.124.58/28
 Gateway=149.10.124.59
index 945b7dcc45360ce21158b25a7d7d9161b6167f27..0742ad58494f13d5e5c38e818f674c73be9562ba 100644 (file)
@@ -1,6 +1,9 @@
 [Match]
 Name=dummy98
 
+[Network]
+IPv6AcceptRA=no
+
 [IPv6AddressLabel]
 Label=4444
 Prefix=2004:da8:1:0::/64
diff --git a/test/test-network/conf/25-netdevsim.netdev b/test/test-network/conf/25-netdevsim.netdev
deleted file mode 100644 (file)
index 899f2d9..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-[NetDev]
-Kind=netdevsim
-Name=netdevsim99
diff --git a/test/test-network/conf/25-prefix-route-with-vrf.network b/test/test-network/conf/25-prefix-route-with-vrf.network
new file mode 100644 (file)
index 0000000..fdc1e11
--- /dev/null
@@ -0,0 +1,26 @@
+[Match]
+Name=dummy98
+
+[Network]
+IPv6AcceptRA=no
+VRF=vrf99
+Address=fdde:11:22::1/128
+Address=fdde:11:33::1/64
+Address=10.20.22.1/32
+Address=10.20.33.1/24
+
+[Address]
+Address=fdde:11:44::1/128
+AddPrefixRoute=no
+
+[Address]
+Address=fdde:11:55::1/64
+AddPrefixRoute=no
+
+[Address]
+Address=10.20.44.1/32
+AddPrefixRoute=no
+
+[Address]
+Address=10.20.55.1/24
+AddPrefixRoute=no
diff --git a/test/test-network/conf/25-prefix-route-without-vrf.network b/test/test-network/conf/25-prefix-route-without-vrf.network
new file mode 100644 (file)
index 0000000..9354b55
--- /dev/null
@@ -0,0 +1,25 @@
+[Match]
+Name=test1
+
+[Network]
+IPv6AcceptRA=no
+Address=fdde:12:22::1/128
+Address=fdde:12:33::1/64
+Address=10.21.22.1/32
+Address=10.21.33.1/24
+
+[Address]
+Address=fdde:12:44::1/128
+AddPrefixRoute=no
+
+[Address]
+Address=fdde:12:55::1/64
+AddPrefixRoute=no
+
+[Address]
+Address=10.21.44.1/32
+AddPrefixRoute=no
+
+[Address]
+Address=10.21.55.1/24
+AddPrefixRoute=no
diff --git a/test/test-network/conf/25-qdisc-cake.network b/test/test-network/conf/25-qdisc-cake.network
new file mode 100644 (file)
index 0000000..a1b00f2
--- /dev/null
@@ -0,0 +1,12 @@
+[Match]
+Name=dummy98
+
+[Network]
+IPv6AcceptRA=no
+Address=10.1.2.3/16
+
+[CAKE]
+Parent=root
+Handle=3a
+OverheadBytes=128
+Bandwidth=500M
diff --git a/test/test-network/conf/25-qdisc-clsact-and-htb.network b/test/test-network/conf/25-qdisc-clsact-and-htb.network
new file mode 100644 (file)
index 0000000..fd2520d
--- /dev/null
@@ -0,0 +1,203 @@
+[Match]
+Name=dummy98
+
+[Network]
+IPv6AcceptRA=no
+Address=10.1.2.3/16
+
+[QDisc]
+Parent=clsact
+
+[HierarchyTokenBucket]
+Parent=root
+Handle=0002
+DefaultClass=30
+RateToQuantum=20
+
+[HierarchyTokenBucketClass]
+Parent=root
+ClassId=0002:0030
+Priority=1
+QuantumBytes=4000
+MTUBytes=1700
+OverheadBytes=100
+Rate=1M
+BufferBytes=123456
+CeilRate=0.5M
+CeilBufferBytes=123457
+
+[NetworkEmulator]
+Parent=2:30
+Handle=0030
+DelaySec=50ms
+DelayJitterSec=10ms
+LossRate=20%
+PacketLimit=100
+
+[HierarchyTokenBucketClass]
+Parent=root
+ClassId=0002:0031
+Priority=1
+Rate=1M
+CeilRate=0.5M
+
+[TrivialLinkEqualizer]
+Parent=2:31
+Handle=0031
+Id=1
+
+[HierarchyTokenBucketClass]
+Parent=root
+ClassId=0002:0032
+Priority=1
+Rate=1M
+CeilRate=0.5M
+
+[FairQueueing]
+Parent=2:32
+Handle=0032
+PacketLimit=1000
+FlowLimit=200
+QuantumBytes=1500
+InitialQuantumBytes=13000
+MaximumRate=1M
+Buckets=512
+OrphanMask=511
+Pacing=yes
+CEThresholdSec=100ms
+
+[HierarchyTokenBucketClass]
+Parent=root
+ClassId=0002:0033
+Priority=1
+Rate=1M
+CeilRate=0.5M
+
+[ControlledDelay]
+Parent=2:33
+Handle=0033
+PacketLimit=2000
+TargetSec=10ms
+IntervalSec=50ms
+ECN=yes
+CEThresholdSec=100ms
+
+[HierarchyTokenBucketClass]
+Parent=root
+ClassId=0002:0034
+Priority=1
+Rate=1M
+CeilRate=0.5M
+
+[FairQueueingControlledDelay]
+Parent=2:34
+Handle=0034
+PacketLimit=20480
+MemoryLimitBytes=64M
+Flows=2048
+TargetSec=10ms
+IntervalSec=200ms
+QuantumBytes=1400
+ECN=yes
+CEThresholdSec=100ms
+
+[HierarchyTokenBucketClass]
+Parent=root
+ClassId=0002:0035
+Priority=1
+Rate=1M
+CeilRate=0.5M
+
+[TokenBucketFilter]
+Parent=2:35
+Handle=0035
+Rate=1G
+BurstBytes=5000
+LatencySec=70msec
+PeakRate=100G
+MTUBytes=1000000
+
+[HierarchyTokenBucketClass]
+Parent=root
+ClassId=0002:0036
+Priority=1
+Rate=1M
+CeilRate=0.5M
+
+[StochasticFairnessQueueing]
+Parent=2:36
+Handle=0036
+PerturbPeriodSec=5sec
+
+[HierarchyTokenBucketClass]
+Parent=root
+ClassId=0002:0037
+Priority=1
+Rate=1M
+CeilRate=0.5M
+
+[PFIFO]
+Parent=2:37
+Handle=0037
+PacketLimit=100000
+
+[HierarchyTokenBucketClass]
+Parent=root
+ClassId=0002:0038
+Priority=1
+Rate=1M
+CeilRate=0.5M
+
+[GenericRandomEarlyDetection]
+Parent=2:38
+Handle=0038
+VirtualQueues=12
+DefaultVirtualQueue=10
+GenericRIO=yes
+
+[HierarchyTokenBucketClass]
+Parent=root
+ClassId=0002:0039
+Priority=1
+Rate=1M
+CeilRate=0.5M
+
+[StochasticFairBlue]
+Parent=2:39
+Handle=0039
+PacketLimit=200000
+
+[HierarchyTokenBucketClass]
+Parent=root
+ClassId=0002:003a
+Priority=1
+Rate=1M
+CeilRate=0.5M
+
+[BFIFO]
+Parent=2:3a
+Handle=003a
+LimitBytes=1000000
+
+[HierarchyTokenBucketClass]
+Parent=root
+ClassId=0002:003b
+Priority=1
+Rate=1M
+CeilRate=0.5M
+
+[PFIFOHeadDrop]
+Parent=2:3b
+Handle=003b
+PacketLimit=1023
+
+[HierarchyTokenBucketClass]
+Parent=root
+ClassId=0002:003c
+Priority=1
+Rate=1M
+CeilRate=0.5M
+
+[PFIFOFast]
+Parent=2:3c
+Handle=003c
diff --git a/test/test-network/conf/25-qdisc-clsact-root-compat.network b/test/test-network/conf/25-qdisc-clsact-root-compat.network
deleted file mode 100644 (file)
index 1f0dea4..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-[Match]
-Name=dummy98
-
-[Network]
-IPv6AcceptRA=no
-Address=10.1.2.3/16
-
-#[TrafficControlQueueingDiscipline]
-#Parent=root
-
-[TrafficControlQueueingDiscipline]
-Parent=clsact
diff --git a/test/test-network/conf/25-qdisc-drr.network b/test/test-network/conf/25-qdisc-drr.network
new file mode 100644 (file)
index 0000000..dff8b09
--- /dev/null
@@ -0,0 +1,15 @@
+[Match]
+Name=dummy98
+
+[Network]
+IPv6AcceptRA=no
+Address=10.1.2.3/16
+
+[DeficitRoundRobinScheduler]
+Parent=root
+Handle=0002
+
+[DeficitRoundRobinSchedulerClass]
+Parent=root
+ClassId=0002:0030
+QuantumBytes=2000
diff --git a/test/test-network/conf/25-qdisc-ets.network b/test/test-network/conf/25-qdisc-ets.network
new file mode 100644 (file)
index 0000000..a8c6b68
--- /dev/null
@@ -0,0 +1,20 @@
+[Match]
+Name=dummy98
+
+[Network]
+IPv6AcceptRA=no
+Address=10.1.2.3/16
+
+[EnhancedTransmissionSelection]
+Parent=root
+Handle=3a
+Bands=10
+StrictBands=3
+QuantumBytes=2 4 6
+QuantumBytes=
+QuantumBytes=1 2 3
+QuantumBytes=4 5
+PriorityMap=8 7 6 5
+PriorityMap=
+PriorityMap=3 4 5
+PriorityMap=6 7
diff --git a/test/test-network/conf/25-qdisc-fq-codel.network b/test/test-network/conf/25-qdisc-fq-codel.network
deleted file mode 100644 (file)
index 42c8dfa..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-[Match]
-Name=dummy98
-
-[Network]
-IPv6AcceptRA=no
-Address=10.1.2.3/16
-
-[FairQueueing]
-Parent=root
-Handle=0003
-PacketLimit=1000
-FlowLimit=200
-Quantum=1500
-InitialQuantum=13000
-MaximumRate=1M
-Buckets=512
-OrphanMask=511
-Pacing=yes
-CEThresholdSec=100ms
-
-[ControlledDelay]
-Parent=clsact
-PacketLimit=2000
-TargetSec=10ms
-IntervalSec=50ms
-ECN=yes
-CEThresholdSec=100ms
diff --git a/test/test-network/conf/25-qdisc-hhf.network b/test/test-network/conf/25-qdisc-hhf.network
new file mode 100644 (file)
index 0000000..9505362
--- /dev/null
@@ -0,0 +1,11 @@
+[Match]
+Name=dummy98
+
+[Network]
+IPv6AcceptRA=no
+Address=10.1.2.3/16
+
+[HeavyHitterFilter]
+Parent=root
+Handle=3a
+PacketLimit=1022
diff --git a/test/test-network/conf/25-qdisc-ingress-root.network b/test/test-network/conf/25-qdisc-ingress-root.network
deleted file mode 100644 (file)
index f72b701..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-[Match]
-Name=test1
-
-[Network]
-IPv6AcceptRA=no
-Address=10.1.2.4/16
-
-#[QDisc]
-#Parent=root
-
-[QDisc]
-Parent=ingress
diff --git a/test/test-network/conf/25-qdisc-netem-and-fqcodel.network b/test/test-network/conf/25-qdisc-netem-and-fqcodel.network
deleted file mode 100644 (file)
index de03d0d..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-[Match]
-Name=dummy98
-
-[Network]
-IPv6AcceptRA=no
-Address=10.1.2.3/16
-
-[NetworkEmulator]
-Parent=root
-Handle=001f
-DelaySec=50ms
-DelayJitterSec=10ms
-LossRate=20%
-PacketLimit=100
-
-[FairQueueingControlledDelay]
-Parent=ingress
-PacketLimit=20480
-MemoryLimit=64M
-Flows=2048
-TargetSec=10ms
-IntervalSec=200ms
-Quantum=1400
-ECN=yes
-CEThresholdSec=100ms
diff --git a/test/test-network/conf/25-qdisc-pie.network b/test/test-network/conf/25-qdisc-pie.network
new file mode 100644 (file)
index 0000000..d41ceba
--- /dev/null
@@ -0,0 +1,11 @@
+[Match]
+Name=dummy98
+
+[Network]
+IPv6AcceptRA=no
+Address=10.1.2.3/16
+
+[PIE]
+Parent=root
+Handle=3a
+PacketLimit=200000
diff --git a/test/test-network/conf/25-qdisc-qfq.network b/test/test-network/conf/25-qdisc-qfq.network
new file mode 100644 (file)
index 0000000..3a24415
--- /dev/null
@@ -0,0 +1,22 @@
+[Match]
+Name=test1
+
+[Network]
+IPv6AcceptRA=no
+Address=10.1.2.4/16
+
+[QuickFairQueueing]
+Parent=root
+Handle=0002
+
+[QuickFairQueueingClass]
+Parent=root
+ClassId=0002:0030
+Weight=2
+MaxPacketBytes=16000
+
+[QuickFairQueueingClass]
+Parent=root
+ClassId=0002:0031
+Weight=10
+MaxPacketBytes=8000
diff --git a/test/test-network/conf/25-qdisc-tbf-and-sfq.network b/test/test-network/conf/25-qdisc-tbf-and-sfq.network
deleted file mode 100644 (file)
index c960886..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-[Match]
-Name=test1
-
-[Network]
-IPv6AcceptRA=no
-Address=10.1.2.4/16
-
-[TokenBucketFilter]
-Parent=root
-Handle=003f
-Rate=1G
-Burst=5K
-LatencySec=70msec
-PeakRate=100G
-MTUBytes=1M
-
-[StochasticFairnessQueueing]
-Parent=clsact
-PerturbPeriodSec=5sec
diff --git a/test/test-network/conf/25-qdisc-teql.network b/test/test-network/conf/25-qdisc-teql.network
deleted file mode 100644 (file)
index c8bb903..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-[Match]
-Name=dummy98
-
-[Network]
-IPv6AcceptRA=no
-Address=10.1.2.3/16
-
-[TrivialLinkEqualizer]
-Parent=root
-Handle=0002
-Id=1
index e786066d3ffb5644fcef21b6821e36f873296366..038dff21aa486d79cbe7fe822dbd028862b294e7 100644 (file)
@@ -2,6 +2,7 @@
 Name=dummy98
 
 [Network]
+IPv6AcceptRA=no
 VRF=vrf99
 Address=192.168.100.2/24
 Gateway=192.168.100.1
diff --git a/test/test-network/conf/25-sriov.network b/test/test-network/conf/25-sriov.network
new file mode 100644 (file)
index 0000000..c962c3d
--- /dev/null
@@ -0,0 +1,37 @@
+[Match]
+Name=eni99np1
+
+[Network]
+Address=192.168.100.100/24
+
+[SR-IOV]
+VirtualFunction=0
+VLANId=5
+VLANProtocol=802.1ad
+QualityOfService=1
+MACSpoofCheck=yes
+QueryReceiveSideScaling=yes
+Trust=yes
+LinkState=yes
+MACAddress=00:11:22:33:44:55
+
+[SR-IOV]
+VirtualFunction=1
+VLANId=6
+VLANProtocol=802.1Q
+QualityOfService=2
+MACSpoofCheck=no
+QueryReceiveSideScaling=no
+Trust=no
+LinkState=no
+MACAddress=00:11:22:33:44:56
+
+[SR-IOV]
+VirtualFunction=2
+VLANId=7
+QualityOfService=3
+MACSpoofCheck=no
+QueryReceiveSideScaling=no
+Trust=no
+LinkState=auto
+MACAddress=00:11:22:33:44:57
index 68be305477049341edeccf108d66320396ffca8b..dc1d6542c0e2c5b099b42c279b20cd8bfce862a4 100644 (file)
@@ -9,3 +9,4 @@ IPv6HopLimit=5
 IPv4ProxyARP=true
 IPv6ProxyNDP=true
 IPv6AcceptRA=no
+IPv4AcceptLocal=yes
diff --git a/test/test-network/conf/25-test1.network b/test/test-network/conf/25-test1.network
new file mode 100644 (file)
index 0000000..965013f
--- /dev/null
@@ -0,0 +1,6 @@
+[Match]
+Name=test1
+
+[Network]
+Address=192.168.0.15/24
+Gateway=192.168.0.1
diff --git a/test/test-network/conf/25-test1.network.d/configure-without-carrier.conf b/test/test-network/conf/25-test1.network.d/configure-without-carrier.conf
new file mode 100644 (file)
index 0000000..fed3b0e
--- /dev/null
@@ -0,0 +1,2 @@
+[Network]
+ConfigureWithoutCarrier=true
diff --git a/test/test-network/conf/25-test1.network.d/ignore-carrier-loss-no.conf b/test/test-network/conf/25-test1.network.d/ignore-carrier-loss-no.conf
new file mode 100644 (file)
index 0000000..b091e98
--- /dev/null
@@ -0,0 +1,2 @@
+[Network]
+IgnoreCarrierLoss=false
index 42ce5b192529dbbd38d599b586c6dff163df2214..d47ecf078968e56322b480d2d5dc07fd6d8c6668 100644 (file)
@@ -1,2 +1,5 @@
 [Match]
 Name=vrf99
+
+[Network]
+IPv6AcceptRA=no
diff --git a/test/test-network/conf/25-wireguard-no-peer.netdev b/test/test-network/conf/25-wireguard-no-peer.netdev
new file mode 100644 (file)
index 0000000..13fd55d
--- /dev/null
@@ -0,0 +1,8 @@
+[NetDev]
+Name=wg97
+Kind=wireguard
+
+[WireGuard]
+PrivateKey=EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=
+ListenPort=51821
+FwMark=1235
diff --git a/test/test-network/conf/25-wireguard-no-peer.network b/test/test-network/conf/25-wireguard-no-peer.network
new file mode 100644 (file)
index 0000000..5c29d64
--- /dev/null
@@ -0,0 +1,2 @@
+[Match]
+Name=wg97
diff --git a/test/test-network/conf/26-bridge-configure-without-carrier.network b/test/test-network/conf/26-bridge-configure-without-carrier.network
new file mode 100644 (file)
index 0000000..e1196b8
--- /dev/null
@@ -0,0 +1,9 @@
+[Match]
+Name=bridge99
+
+[Network]
+LinkLocalAddressing=yes
+IPv6AcceptRA=no
+ConfigureWithoutCarrier=yes
+Address=10.1.2.3/24
+Gateway=10.1.2.1
index 84e5af0ff03ab7e218146e8ed2eaca466d83fcc1..96bd561ff90f7455cca04f3667b43d57caf1ed32 100644 (file)
@@ -2,4 +2,5 @@
 Name=dummy98
 
 [Network]
+IPv6AcceptRA=no
 Tunnel=sittun99
diff --git a/test/test-network/conf/configure-without-carrier.network b/test/test-network/conf/configure-without-carrier.network
deleted file mode 100644 (file)
index 5bd9d7e..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-[Match]
-Name=test1
-
-[Network]
-Address=192.168.0.15/24
-Gateway=192.168.0.1
-ConfigureWithoutCarrier=true
diff --git a/test/test-network/conf/dhcp-client-ipv4-use-routes-no.network b/test/test-network/conf/dhcp-client-ipv4-use-routes-no.network
deleted file mode 100644 (file)
index 21e21fa..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-[Match]
-Name=veth99
-
-[Network]
-DHCP=ipv4
-IPv6AcceptRA=false
-
-[DHCPv4]
-UseRoutes=no
diff --git a/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network b/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network
new file mode 100644 (file)
index 0000000..c980bf9
--- /dev/null
@@ -0,0 +1,6 @@
+[Match]
+Name=veth99
+
+[Network]
+DHCP=ipv4
+IPv6AcceptRA=false
diff --git a/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-dns-routes-False.conf b/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-dns-routes-False.conf
new file mode 100644 (file)
index 0000000..9c561fb
--- /dev/null
@@ -0,0 +1,2 @@
+[DHCPv4]
+RoutesToDNS=no
diff --git a/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-dns-routes-True.conf b/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-dns-routes-True.conf
new file mode 100644 (file)
index 0000000..2504c20
--- /dev/null
@@ -0,0 +1,2 @@
+[DHCPv4]
+RoutesToDNS=yes
diff --git a/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-gateway-False.conf b/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-gateway-False.conf
new file mode 100644 (file)
index 0000000..78d0493
--- /dev/null
@@ -0,0 +1,2 @@
+[DHCPv4]
+UseGateway=no
diff --git a/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-gateway-True.conf b/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-gateway-True.conf
new file mode 100644 (file)
index 0000000..f6f597b
--- /dev/null
@@ -0,0 +1,2 @@
+[DHCPv4]
+UseGateway=yes
diff --git a/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-routes-False.conf b/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-routes-False.conf
new file mode 100644 (file)
index 0000000..38fa983
--- /dev/null
@@ -0,0 +1,2 @@
+[DHCPv4]
+UseRoutes=no
diff --git a/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-routes-True.conf b/test/test-network/conf/dhcp-client-ipv4-use-routes-use-gateway.network.d/use-routes-True.conf
new file mode 100644 (file)
index 0000000..3b2d0ab
--- /dev/null
@@ -0,0 +1,2 @@
+[DHCPv4]
+UseRoutes=yes
index 49364c506c7b06d036345041ac9e99383c9ef45e..d1a88cdaaf25bea770c6279886bb535d6f4fc5a1 100644 (file)
@@ -2,5 +2,6 @@
 Name=dummy98
 
 [Network]
+IPv6AcceptRA=no
 Tunnel=erspan99
 Tunnel=erspan98
index 1493fcf2b01104066d940ce2cceb3d883d187dfa..1deaab45d6a7414f0d7d0157ff762fddff2a4f96 100644 (file)
@@ -2,5 +2,6 @@
 Name=dummy98
 
 [Network]
+IPv6AcceptRA=no
 Tunnel=gretap99
 Tunnel=gretap98
index 5510b1c9b6544ebe4110255889fa58d94f8aec9c..68d95b18080c1199cbf1e0511461ebdffb425966 100644 (file)
@@ -2,6 +2,7 @@
 Name=dummy98
 
 [Network]
+IPv6AcceptRA=no
 Tunnel=gretun99
 Tunnel=gretun98
 Tunnel=gretun97
index 7ae4e3aea73572d2cbcc423afa5bb8ae68abf1b7..8434c62efbe15fd26842146e0157be0583ddbeb5 100644 (file)
@@ -2,5 +2,6 @@
 Name=dummy98
 
 [Network]
+IPv6AcceptRA=no
 Tunnel=ip6gretap99
 Tunnel=ip6gretap98
index 8fbee98851ec185d1001d7d0194bbc0d2dbf9fd2..ce2bbd8792e9ab9008b7ca577d8d1420505b47be 100644 (file)
@@ -2,6 +2,7 @@
 Name=dummy98
 
 [Network]
+IPv6AcceptRA=no
 Tunnel=ip6gretun99
 Tunnel=ip6gretun98
 Tunnel=ip6gretun97
index 15c6d15d453df50794233d3edf0a3a9fc2990bc9..7ad1e1b5b1fc79ec7744a22e02064cf210ffc536 100644 (file)
@@ -2,6 +2,7 @@
 Name=dummy98
 
 [Network]
+IPv6AcceptRA=no
 Tunnel=ip6tnl99
 Tunnel=ip6tnl98
 Tunnel=ip6tnl97
index ea4b3a13533604c84ec67067f565d41a84d20b00..c1ef30519c71b29f189dc83f6d5ce287fe62313c 100644 (file)
@@ -2,6 +2,7 @@
 Name=dummy98
 
 [Network]
+IPv6AcceptRA=no
 Tunnel=ipiptun99
 Tunnel=ipiptun98
 Tunnel=ipiptun97
index 49c6f31e77e66af5ef96ddd65384a2fbbebae613..18fecb880280c3bf402731478388605e12a79e9d 100644 (file)
@@ -4,4 +4,6 @@ Name=veth99
 [Network]
 IPv6AcceptRA=true
 IPv6Token=::1a:2b:3c:4d
+IPv6Token=::1a:2b:3c:4d
+IPv6Token=::1a:2b:3c:4d
 IPv6Token=::fa:de:ca:fe
index 7bb6661362463624978166d0d8a899ef5dcf7d59..9dc32cb4da016c544104d3b262d210ce60f59306 100644 (file)
@@ -4,10 +4,13 @@ Name=veth99
 [Network]
 DHCP=no
 IPv6PrefixDelegation=yes
-Address=2001:db8:0:1::1/64
 
 [IPv6Prefix]
-Prefix=2001:db8:0:1::4/64
+Prefix=2001:db8:0:1::/64
+
+[IPv6Prefix]
+Prefix=2001:db8:0:2::/64
+Assign=yes
 
 [IPv6RoutePrefix]
 Route=2001:db0:fff::/64
index d0532205507fa4afdab8cbeafdb620533ce350d1..b3fccb146f338c6acf3fa64ce983ec2061f3027b 100644 (file)
@@ -2,4 +2,5 @@
 Name=test1
 
 [Network]
+IPv6AcceptRA=no
 IPVLAN=ipvlan99
index c81ba52e2d365863ed564acdb2be448e724310f0..4549164323163f8a5d498bb9e6824d4b8d463483 100644 (file)
@@ -2,4 +2,5 @@
 Name=test1
 
 [Network]
+IPv6AcceptRA=no
 IPVTAP=ipvtap99
index e8d03ed6bd20ef1bdb761951274837c0137dccdc..9458330859ec4553a675a21848f0b45fa78d9d94 100644 (file)
@@ -2,4 +2,5 @@
 Name=dummy98
 
 [Network]
+IPv6AcceptRA=no
 Tunnel=isataptun99
index a41c1f916cf46b88f25a92587a92d6d528e60212..e18842ca23df3588d05a218f1074f1231a77cac1 100644 (file)
@@ -2,4 +2,5 @@
 Name=test1
 
 [Network]
+IPv6AcceptRA=no
 MACVLAN=macvlan99
index 6ee99ab9ce347317c3abadd82f51006ca9bbca3b..956d13fc3acc80409de757756f7249bd5fddb8b5 100644 (file)
@@ -2,4 +2,5 @@
 Name=test1
 
 [Network]
+IPv6AcceptRA=no
 MACVTAP=macvtap99
index 8136c20ae42a7ed7a17199f4269d022574d7baf7..804597cec45de218dc1bdd23cfa1224227a6f11d 100644 (file)
@@ -1,6 +1,9 @@
 [Match]
 Name=dummy98
 
+[Network]
+IPv6AcceptRA=no
+
 [RoutingPolicyRule]
 TypeOfService=0x08
 Table=8
index ffcedc99a2b71e774036d132f5ed55e73c70bb0d..3594602cbb3cd04592fe94453ddbd7031e5dfe1d 100644 (file)
@@ -1,6 +1,9 @@
 [Match]
 Name=test1
 
+[Network]
+IPv6AcceptRA=no
+
 [RoutingPolicyRule]
 TypeOfService=0x08
 Table=7
index 79909fcd6befd2c1a1d4ff77e96cd0dea06e1d78..571c5c93d920f32e87785a0422ef24fe3efeb2ee 100644 (file)
@@ -2,6 +2,7 @@
 Name=dummy98
 
 [Network]
+IPv6AcceptRA=no
 Tunnel=sittun99
 Tunnel=sittun98
 Tunnel=sittun97
index d0233841fd5958435a4a80e23c6d968d9161dd35..1f7e7d16f01556a95116efe06245b17c257a9544 100644 (file)
@@ -5,7 +5,8 @@ Name=dummy98
 RequiredForOnline=routable
 
 [Network]
-DNS=10.10.10.10 10.10.10.11
+IPv6AcceptRA=no
+DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com [1111:2222::3333]:1234#ccc.com
 NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org
 Domains=hogehoge ~foofoo
 LLMNR=no
index 761362e48221daeb5493a518e9d0d2049d8ffa6b..888e79a79db9c7a2e2a46cc7f484ded071597c46 100644 (file)
@@ -2,6 +2,7 @@
 Name=dummy98
 
 [Network]
+IPv6AcceptRA=no
 Tunnel=vtitun99
 Tunnel=vtitun98
 Tunnel=vtitun97
index 60ccb77f56bfa92d149042c0e1cb372cc4ab7098..0916de86dd1f6f24b980c96539b4051dfcd86f52 100644 (file)
@@ -2,6 +2,7 @@
 Name=dummy98
 
 [Network]
+IPv6AcceptRA=no
 Tunnel=vti6tun99
 Tunnel=vti6tun98
 Tunnel=vti6tun97
index 97488243ffc6bb533bde562ba3cbff20c842fd6d..0ca1fb3bf93e303d541c1946ac2f7931cf03891d 100755 (executable)
@@ -3,6 +3,7 @@
 # systemd-networkd tests
 
 import argparse
+import itertools
 import os
 import re
 import shutil
@@ -157,6 +158,81 @@ def expectedFailureIfAlternativeNameIsNotAvailable():
 
     return f
 
+def expectedFailureIfNetdevsimWithSRIOVIsNotAvailable():
+    def f(func):
+        call('rmmod netdevsim', stderr=subprocess.DEVNULL)
+        rc = call('modprobe netdevsim', stderr=subprocess.DEVNULL)
+        if rc != 0:
+            return unittest.expectedFailure(func)
+
+        try:
+            with open('/sys/bus/netdevsim/new_device', mode='w') as f:
+                f.write('99 1')
+        except Exception as error:
+            return unittest.expectedFailure(func)
+
+        call('udevadm settle')
+        call('udevadm info -w10s /sys/devices/netdevsim99/net/eni99np1', stderr=subprocess.DEVNULL)
+        try:
+            with open('/sys/class/net/eni99np1/device/sriov_numvfs', mode='w') as f:
+                f.write('3')
+        except Exception as error:
+            call('rmmod netdevsim', stderr=subprocess.DEVNULL)
+            return unittest.expectedFailure(func)
+
+        call('rmmod netdevsim', stderr=subprocess.DEVNULL)
+        return func
+
+    return f
+
+def expectedFailureIfCAKEIsNotAvailable():
+    def f(func):
+        call('ip link add dummy98 type dummy', stderr=subprocess.DEVNULL)
+        rc = call('tc qdisc add dev dummy98 parent root cake', stderr=subprocess.DEVNULL)
+        call('ip link del dummy98', stderr=subprocess.DEVNULL)
+        if rc == 0:
+            return func
+        else:
+            return unittest.expectedFailure(func)
+
+    return f
+
+def expectedFailureIfPIEIsNotAvailable():
+    def f(func):
+        call('ip link add dummy98 type dummy', stderr=subprocess.DEVNULL)
+        rc = call('tc qdisc add dev dummy98 parent root pie', stderr=subprocess.DEVNULL)
+        call('ip link del dummy98', stderr=subprocess.DEVNULL)
+        if rc == 0:
+            return func
+        else:
+            return unittest.expectedFailure(func)
+
+    return f
+
+def expectedFailureIfHHFIsNotAvailable():
+    def f(func):
+        call('ip link add dummy98 type dummy', stderr=subprocess.DEVNULL)
+        rc = call('tc qdisc add dev dummy98 parent root hhf', stderr=subprocess.DEVNULL)
+        call('ip link del dummy98', stderr=subprocess.DEVNULL)
+        if rc == 0:
+            return func
+        else:
+            return unittest.expectedFailure(func)
+
+    return f
+
+def expectedFailureIfETSIsNotAvailable():
+    def f(func):
+        call('ip link add dummy98 type dummy', stderr=subprocess.DEVNULL)
+        rc = call('tc qdisc add dev dummy98 parent root ets bands 10', stderr=subprocess.DEVNULL)
+        call('ip link del dummy98', stderr=subprocess.DEVNULL)
+        if rc == 0:
+            return func
+        else:
+            return unittest.expectedFailure(func)
+
+    return f
+
 def setUpModule():
     global running_units
 
@@ -408,6 +484,9 @@ class Utilities():
     def check_link_exists(self, link):
         self.assertTrue(link_exists(link))
 
+    def check_link_attr(self, *args):
+        self.assertEqual(read_link_attr(*args[:-1]), args[-1]);
+
     def wait_operstate(self, link, operstate='degraded', setup_state='configured', setup_timeout=5, fail_assert=True):
         """Wait for the link to reach the specified operstate and/or setup state.
 
@@ -478,7 +557,7 @@ class Utilities():
             if i > 0:
                 time.sleep(1)
             output = check_output(f'ip {ipv} address show dev {link} scope {scope}')
-            if re.search(address_regex, output):
+            if re.search(address_regex, output) and 'tentative' not in output:
                 break
         else:
             self.assertRegex(output, address_regex)
@@ -706,6 +785,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         'vtitun99',
         'vxcan99',
         'vxlan99',
+        'wg97',
         'wg98',
         'wg99',
     ]
@@ -792,6 +872,8 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         '25-vxlan.netdev',
         '25-wireguard-23-peers.netdev',
         '25-wireguard-23-peers.network',
+        '25-wireguard-no-peer.netdev',
+        '25-wireguard-no-peer.network',
         '25-wireguard-preshared-key.txt',
         '25-wireguard-private-key.txt',
         '25-wireguard.netdev',
@@ -1075,9 +1157,10 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
     def test_wireguard(self):
         copy_unit_to_networkd_unit_path('25-wireguard.netdev', '25-wireguard.network',
                                         '25-wireguard-23-peers.netdev', '25-wireguard-23-peers.network',
-                                        '25-wireguard-preshared-key.txt', '25-wireguard-private-key.txt')
+                                        '25-wireguard-preshared-key.txt', '25-wireguard-private-key.txt',
+                                        '25-wireguard-no-peer.netdev', '25-wireguard-no-peer.network')
         start_networkd()
-        self.wait_online(['wg99:carrier', 'wg98:routable'])
+        self.wait_online(['wg99:carrier', 'wg98:routable', 'wg97:carrier'])
 
         if shutil.which('wg'):
             call('wg')
@@ -1102,6 +1185,11 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
             output = check_output('wg show wg98 private-key')
             self.assertRegex(output, r'CJQUtcS9emY2fLYqDlpSZiE/QJyHkPWr\+WHtZLZ90FU=')
 
+            output = check_output('wg show wg97 listen-port')
+            self.assertRegex(output, '51821')
+            output = check_output('wg show wg97 fwmark')
+            self.assertRegex(output, '0x4d3')
+
     def test_geneve(self):
         copy_unit_to_networkd_unit_path('25-geneve.netdev', 'netdev-link-local-addressing-yes.network')
         start_networkd()
@@ -1621,25 +1709,30 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         '25-neighbor-ip-dummy.network',
         '25-neighbor-ip.network',
         '25-nexthop.network',
-        '25-qdisc-clsact-root-compat.network',
-        '25-qdisc-fq-codel.network',
+        '25-qdisc-cake.network',
+        '25-qdisc-clsact-and-htb.network',
+        '25-qdisc-drr.network',
+        '25-qdisc-ets.network',
+        '25-qdisc-hhf.network',
         '25-qdisc-ingress-netem-compat.network',
-        '25-qdisc-ingress-root.network',
-        '25-qdisc-netem-and-fqcodel.network',
-        '25-qdisc-tbf-and-sfq.network',
-        '25-qdisc-teql.network',
+        '25-qdisc-pie.network',
+        '25-qdisc-qfq.network',
+        '25-prefix-route-with-vrf.network',
+        '25-prefix-route-without-vrf.network',
         '25-route-ipv6-src.network',
         '25-route-static.network',
         '25-route-vrf.network',
         '25-gateway-static.network',
         '25-gateway-next-static.network',
+        '25-sriov.network',
         '25-sysctl-disable-ipv6.network',
         '25-sysctl.network',
+        '25-test1.network',
         '25-veth-peer.network',
         '25-veth.netdev',
         '25-vrf.netdev',
+        '25-vrf.network',
         '26-link-local-addressing-ipv6.network',
-        'configure-without-carrier.network',
         'routing-policy-rule-dummy98.network',
         'routing-policy-rule-test1.network']
 
@@ -1725,16 +1818,128 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         print(output)
         self.assertNotRegex(output, '192.168.100.10/24')
 
+    @expectedFailureIfModuleIsNotAvailable('vrf')
+    def test_prefix_route(self):
+        copy_unit_to_networkd_unit_path('25-prefix-route-with-vrf.network', '12-dummy.netdev',
+                                        '25-prefix-route-without-vrf.network', '11-dummy.netdev',
+                                        '25-vrf.netdev', '25-vrf.network')
+        for trial in range(2):
+            if trial == 0:
+                start_networkd()
+            else:
+                restart_networkd(3)
+
+            self.wait_online(['dummy98:routable', 'test1:routable', 'vrf99:carrier'])
+
+            output = check_output('ip route show table 42 dev dummy98')
+            print('### ip route show table 42 dev dummy98')
+            print(output)
+            self.assertRegex(output, 'local 10.20.22.1 proto kernel scope host src 10.20.22.1')
+            self.assertRegex(output, 'broadcast 10.20.33.0 proto kernel scope link src 10.20.33.1')
+            self.assertRegex(output, '10.20.33.0/24 proto kernel scope link src 10.20.33.1')
+            self.assertRegex(output, 'local 10.20.33.1 proto kernel scope host src 10.20.33.1')
+            self.assertRegex(output, 'broadcast 10.20.33.255 proto kernel scope link src 10.20.33.1')
+            self.assertRegex(output, 'local 10.20.44.1 proto kernel scope host src 10.20.44.1')
+            self.assertRegex(output, 'broadcast 10.20.55.0 proto kernel scope link src 10.20.55.1')
+            self.assertRegex(output, 'local 10.20.55.1 proto kernel scope host src 10.20.55.1')
+            self.assertRegex(output, 'broadcast 10.20.55.255 proto kernel scope link src 10.20.55.1')
+            output = check_output('ip -6 route show table 42 dev dummy98')
+            print('### ip -6 route show table 42 dev dummy98')
+            print(output)
+            if trial == 0:
+                # Kernel's bug?
+                self.assertRegex(output, 'local fdde:11:22::1 proto kernel metric 0 pref medium')
+            #self.assertRegex(output, 'fdde:11:22::1 proto kernel metric 256 pref medium')
+            self.assertRegex(output, 'local fdde:11:33::1 proto kernel metric 0 pref medium')
+            self.assertRegex(output, 'fdde:11:33::/64 proto kernel metric 256 pref medium')
+            self.assertRegex(output, 'local fdde:11:44::1 proto kernel metric 0 pref medium')
+            self.assertRegex(output, 'local fdde:11:55::1 proto kernel metric 0 pref medium')
+            self.assertRegex(output, 'fe80::/64 proto kernel metric 256 pref medium')
+            self.assertRegex(output, 'ff00::/8 metric 256 pref medium')
+
+            print()
+
+            output = check_output('ip route show dev test1')
+            print('### ip route show dev test1')
+            print(output)
+            self.assertRegex(output, '10.21.33.0/24 proto kernel scope link src 10.21.33.1')
+            output = check_output('ip route show table local dev test1')
+            print('### ip route show table local dev test1')
+            print(output)
+            self.assertRegex(output, 'local 10.21.22.1 proto kernel scope host src 10.21.22.1')
+            self.assertRegex(output, 'broadcast 10.21.33.0 proto kernel scope link src 10.21.33.1')
+            self.assertRegex(output, 'local 10.21.33.1 proto kernel scope host src 10.21.33.1')
+            self.assertRegex(output, 'broadcast 10.21.33.255 proto kernel scope link src 10.21.33.1')
+            self.assertRegex(output, 'local 10.21.44.1 proto kernel scope host src 10.21.44.1')
+            self.assertRegex(output, 'broadcast 10.21.55.0 proto kernel scope link src 10.21.55.1')
+            self.assertRegex(output, 'local 10.21.55.1 proto kernel scope host src 10.21.55.1')
+            self.assertRegex(output, 'broadcast 10.21.55.255 proto kernel scope link src 10.21.55.1')
+            output = check_output('ip -6 route show dev test1')
+            print('### ip -6 route show dev test1')
+            print(output)
+            self.assertRegex(output, 'fdde:12:22::1 proto kernel metric 256 pref medium')
+            self.assertRegex(output, 'fdde:12:33::/64 proto kernel metric 256 pref medium')
+            self.assertRegex(output, 'fe80::/64 proto kernel metric 256 pref medium')
+            output = check_output('ip -6 route show table local dev test1')
+            print('### ip -6 route show table local dev test1')
+            print(output)
+            self.assertRegex(output, 'local fdde:12:22::1 proto kernel metric 0 pref medium')
+            self.assertRegex(output, 'local fdde:12:33::1 proto kernel metric 0 pref medium')
+            self.assertRegex(output, 'local fdde:12:44::1 proto kernel metric 0 pref medium')
+            self.assertRegex(output, 'local fdde:12:55::1 proto kernel metric 0 pref medium')
+            self.assertRegex(output, 'ff00::/8 metric 256 pref medium')
+
     def test_configure_without_carrier(self):
-        copy_unit_to_networkd_unit_path('configure-without-carrier.network', '11-dummy.netdev')
+        copy_unit_to_networkd_unit_path('11-dummy.netdev')
         start_networkd()
-        self.wait_online(['test1:routable'])
+        self.wait_operstate('test1', 'off', '')
+        check_output('ip link set dev test1 up carrier off')
 
-        output = check_output(*networkctl_cmd, '-n', '0', 'status', 'test1', env=env)
-        print(output)
-        self.assertRegex(output, '192.168.0.15')
-        self.assertRegex(output, '192.168.0.1')
-        self.assertRegex(output, 'routable')
+        copy_unit_to_networkd_unit_path('25-test1.network.d/configure-without-carrier.conf', dropins=False)
+        restart_networkd()
+        self.wait_online(['test1:no-carrier'])
+
+        carrier_map = {'on': '1', 'off': '0'}
+        routable_map = {'on': 'routable', 'off': 'no-carrier'}
+        for carrier in ['off', 'on', 'off']:
+            with self.subTest(carrier=carrier):
+                if carrier_map[carrier] != read_link_attr('test1', 'carrier'):
+                    check_output(f'ip link set dev test1 carrier {carrier}')
+                self.wait_online([f'test1:{routable_map[carrier]}'])
+
+                output = check_output(*networkctl_cmd, '-n', '0', 'status', 'test1', env=env)
+                print(output)
+                self.assertRegex(output, '192.168.0.15')
+                self.assertRegex(output, '192.168.0.1')
+                self.assertRegex(output, routable_map[carrier])
+
+    def test_configure_without_carrier_yes_ignore_carrier_loss_no(self):
+        copy_unit_to_networkd_unit_path('11-dummy.netdev')
+        start_networkd()
+        self.wait_operstate('test1', 'off', '')
+        check_output('ip link set dev test1 up carrier off')
+
+        copy_unit_to_networkd_unit_path('25-test1.network')
+        restart_networkd()
+        self.wait_online(['test1:no-carrier'])
+
+        carrier_map = {'on': '1', 'off': '0'}
+        routable_map = {'on': 'routable', 'off': 'no-carrier'}
+        for (carrier, have_config) in [('off', True), ('on', True), ('off', False)]:
+            with self.subTest(carrier=carrier, have_config=have_config):
+                if carrier_map[carrier] != read_link_attr('test1', 'carrier'):
+                    check_output(f'ip link set dev test1 carrier {carrier}')
+                self.wait_online([f'test1:{routable_map[carrier]}'])
+
+                output = check_output(*networkctl_cmd, '-n', '0', 'status', 'test1', env=env)
+                print(output)
+                if have_config:
+                    self.assertRegex(output, '192.168.0.15')
+                    self.assertRegex(output, '192.168.0.1')
+                else:
+                    self.assertNotRegex(output, '192.168.0.15')
+                    self.assertNotRegex(output, '192.168.0.1')
+                self.assertRegex(output, routable_map[carrier])
 
     def test_routing_policy_rule(self):
         copy_unit_to_networkd_unit_path('routing-policy-rule-test1.network', '11-dummy.netdev')
@@ -2113,6 +2318,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         self.assertEqual(read_ipv6_sysctl_attr('dummy98', 'proxy_ndp'), '1')
         self.assertEqual(read_ipv4_sysctl_attr('dummy98', 'forwarding'),'1')
         self.assertEqual(read_ipv4_sysctl_attr('dummy98', 'proxy_arp'), '1')
+        self.assertEqual(read_ipv4_sysctl_attr('dummy98', 'accept_local'), '1')
 
     def test_sysctl_disable_ipv6(self):
         copy_unit_to_networkd_unit_path('25-sysctl-disable-ipv6.network', '12-dummy.netdev')
@@ -2133,7 +2339,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         self.assertRegex(output, 'inet6 .* scope link')
         output = check_output('ip -4 route show dev dummy98')
         print(output)
-        self.assertEqual(output, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
+        self.assertRegex(output, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
         output = check_output('ip -6 route show dev dummy98')
         print(output)
         self.assertRegex(output, 'default via 2607:5300:203:39ff:ff:ff:ff:ff proto static')
@@ -2156,7 +2362,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         self.assertRegex(output, 'inet6 .* scope link')
         output = check_output('ip -4 route show dev dummy98')
         print(output)
-        self.assertEqual(output, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
+        self.assertRegex(output, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
         output = check_output('ip -6 route show dev dummy98')
         print(output)
         self.assertRegex(output, 'default via 2607:5300:203:39ff:ff:ff:ff:ff proto static')
@@ -2254,71 +2460,181 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         self.assertRegex(output, '192.168.5.1')
 
     def test_qdisc(self):
-        copy_unit_to_networkd_unit_path('25-qdisc-netem-and-fqcodel.network', '12-dummy.netdev',
-                                        '25-qdisc-tbf-and-sfq.network', '11-dummy.netdev')
+        copy_unit_to_networkd_unit_path('25-qdisc-clsact-and-htb.network', '12-dummy.netdev',
+                                        '25-qdisc-ingress-netem-compat.network', '11-dummy.netdev')
+        check_output('modprobe sch_teql max_equalizers=2')
         start_networkd()
 
         self.wait_online(['dummy98:routable', 'test1:routable'])
 
+        output = check_output('tc qdisc show dev test1')
+        print(output)
+        self.assertRegex(output, 'qdisc netem')
+        self.assertRegex(output, 'limit 100 delay 50(.0)?ms  10(.0)?ms loss 20%')
+        self.assertRegex(output, 'qdisc ingress')
+
         output = check_output('tc qdisc show dev dummy98')
         print(output)
-        self.assertRegex(output, 'qdisc netem 1f:')
-        self.assertRegex(output, 'limit 100 delay 50.0ms  10.0ms loss 20%')
+        self.assertRegex(output, 'qdisc clsact')
+
+        self.assertRegex(output, 'qdisc htb 2: root')
+        self.assertRegex(output, r'default (0x30|30)')
+
+        self.assertRegex(output, 'qdisc netem 30: parent 2:30')
+        self.assertRegex(output, 'limit 100 delay 50(.0)?ms  10(.0)?ms loss 20%')
         self.assertRegex(output, 'qdisc fq_codel')
-        self.assertRegex(output, 'limit 20480p flows 2048 quantum 1400 target 10.0ms ce_threshold 100.0ms interval 200.0ms memory_limit 64Mb ecn')
-        output = check_output('tc qdisc show dev test1')
-        print(output)
-        self.assertRegex(output, 'qdisc tbf 3f:')
-        self.assertRegex(output, 'rate 1Gbit burst 5000b peakrate 100Gbit minburst 987500b lat 70.0ms')
-        self.assertRegex(output, 'qdisc sfq')
+        self.assertRegex(output, 'limit 20480p flows 2048 quantum 1400 target 10(.0)?ms ce_threshold 100(.0)?ms interval 200(.0)?ms memory_limit 64Mb ecn')
+
+        self.assertRegex(output, 'qdisc teql1 31: parent 2:31')
+
+        self.assertRegex(output, 'qdisc fq 32: parent 2:32')
+        self.assertRegex(output, 'limit 1000p flow_limit 200p buckets 512 orphan_mask 511')
+        self.assertRegex(output, 'quantum 1500')
+        self.assertRegex(output, 'initial_quantum 13000')
+        self.assertRegex(output, 'maxrate 1Mbit')
+
+        self.assertRegex(output, 'qdisc codel 33: parent 2:33')
+        self.assertRegex(output, 'limit 2000p target 10(.0)?ms ce_threshold 100(.0)?ms interval 50(.0)?ms ecn')
+
+        self.assertRegex(output, 'qdisc fq_codel 34: parent 2:34')
+        self.assertRegex(output, 'limit 20480p flows 2048 quantum 1400 target 10(.0)?ms ce_threshold 100(.0)?ms interval 200(.0)?ms memory_limit 64Mb ecn')
+
+        self.assertRegex(output, 'qdisc tbf 35: parent 2:35')
+        self.assertRegex(output, 'rate 1Gbit burst 5000b peakrate 100Gbit minburst 987500b lat 70(.0)?ms')
+
+        self.assertRegex(output, 'qdisc sfq 36: parent 2:36')
         self.assertRegex(output, 'perturb 5sec')
 
+        self.assertRegex(output, 'qdisc pfifo 37: parent 2:37')
+        self.assertRegex(output, 'limit 100000p')
+
+        self.assertRegex(output, 'qdisc gred 38: parent 2:38')
+        self.assertRegex(output, 'vqs 12 default 10 grio')
+
+        self.assertRegex(output, 'qdisc sfb 39: parent 2:39')
+        self.assertRegex(output, 'limit 200000')
+
+        self.assertRegex(output, 'qdisc bfifo 3a: parent 2:3a')
+        self.assertRegex(output, 'limit 1000000')
+
+        self.assertRegex(output, 'qdisc pfifo_head_drop 3b: parent 2:3b')
+        self.assertRegex(output, 'limit 1023p')
+
+        self.assertRegex(output, 'qdisc pfifo_fast 3c: parent 2:3c')
+
+        output = check_output('tc -d class show dev dummy98')
+        print(output)
+        self.assertRegex(output, 'class htb 2:30 root leaf 30:')
+        self.assertRegex(output, 'class htb 2:31 root leaf 31:')
+        self.assertRegex(output, 'class htb 2:32 root leaf 32:')
+        self.assertRegex(output, 'class htb 2:33 root leaf 33:')
+        self.assertRegex(output, 'class htb 2:34 root leaf 34:')
+        self.assertRegex(output, 'class htb 2:35 root leaf 35:')
+        self.assertRegex(output, 'class htb 2:36 root leaf 36:')
+        self.assertRegex(output, 'class htb 2:37 root leaf 37:')
+        self.assertRegex(output, 'class htb 2:38 root leaf 38:')
+        self.assertRegex(output, 'class htb 2:39 root leaf 39:')
+        self.assertRegex(output, 'class htb 2:3a root leaf 3a:')
+        self.assertRegex(output, 'class htb 2:3b root leaf 3b:')
+        self.assertRegex(output, 'class htb 2:3c root leaf 3c:')
+        self.assertRegex(output, 'prio 1 quantum 4000 rate 1Mbit overhead 100 ceil 500Kbit')
+        self.assertRegex(output, 'burst 123456')
+        self.assertRegex(output, 'cburst 123457')
+
     def test_qdisc2(self):
-        copy_unit_to_networkd_unit_path('25-qdisc-fq-codel.network', '12-dummy.netdev',
-                                        '25-qdisc-ingress-root.network', '11-dummy.netdev')
+        copy_unit_to_networkd_unit_path('25-qdisc-drr.network', '12-dummy.netdev',
+                                        '25-qdisc-qfq.network', '11-dummy.netdev')
         start_networkd()
 
         self.wait_online(['dummy98:routable', 'test1:routable'])
 
         output = check_output('tc qdisc show dev dummy98')
         print(output)
-        self.assertRegex(output, 'qdisc fq 3:')
-        self.assertRegex(output, 'limit 1000p flow_limit 200p buckets 512 orphan_mask 511')
-        self.assertRegex(output, 'quantum 1500')
-        self.assertRegex(output, 'initial_quantum 13000')
-        self.assertRegex(output, 'maxrate 1Mbit')
-        self.assertRegex(output, 'qdisc codel')
-        self.assertRegex(output, 'limit 2000p target 10.0ms ce_threshold 100.0ms interval 50.0ms ecn')
+        self.assertRegex(output, 'qdisc drr 2: root')
+        output = check_output('tc class show dev dummy98')
+        print(output)
+        self.assertRegex(output, 'class drr 2:30 root quantum 2000b')
+
         output = check_output('tc qdisc show dev test1')
         print(output)
-        self.assertRegex(output, 'qdisc ingress')
+        self.assertRegex(output, 'qdisc qfq 2: root')
+        output = check_output('tc class show dev test1')
+        print(output)
+        self.assertRegex(output, 'class qfq 2:30 root weight 2 maxpkt 16000')
+        self.assertRegex(output, 'class qfq 2:31 root weight 10 maxpkt 8000')
 
-    def test_qdisc3(self):
-        copy_unit_to_networkd_unit_path('25-qdisc-clsact-root-compat.network', '12-dummy.netdev',
-                                        '25-qdisc-ingress-netem-compat.network', '11-dummy.netdev')
+    @expectedFailureIfCAKEIsNotAvailable()
+    def test_qdisc_cake(self):
+        copy_unit_to_networkd_unit_path('25-qdisc-cake.network', '12-dummy.netdev')
         start_networkd()
-
-        self.wait_online(['dummy98:routable', 'test1:routable'])
+        self.wait_online(['dummy98:routable'])
 
         output = check_output('tc qdisc show dev dummy98')
         print(output)
-        self.assertRegex(output, 'qdisc clsact')
-        output = check_output('tc qdisc show dev test1')
+        self.assertRegex(output, 'qdisc cake 3a: root')
+        self.assertRegex(output, 'bandwidth 500Mbit')
+        self.assertRegex(output, 'overhead 128')
+
+    @expectedFailureIfPIEIsNotAvailable()
+    def test_qdisc_pie(self):
+        copy_unit_to_networkd_unit_path('25-qdisc-pie.network', '12-dummy.netdev')
+        start_networkd()
+        self.wait_online(['dummy98:routable'])
+
+        output = check_output('tc qdisc show dev dummy98')
         print(output)
-        self.assertRegex(output, 'qdisc netem')
-        self.assertRegex(output, 'limit 100 delay 50.0ms  10.0ms loss 20%')
-        self.assertRegex(output, 'qdisc ingress')
+        self.assertRegex(output, 'qdisc pie 3a: root')
+        self.assertRegex(output, 'limit 200000')
 
-    def test_qdisc4(self):
-        copy_unit_to_networkd_unit_path('25-qdisc-teql.network', '12-dummy.netdev')
-        check_output('modprobe sch_teql max_equalizers=2')
+    @expectedFailureIfHHFIsNotAvailable()
+    def test_qdisc_hhf(self):
+        copy_unit_to_networkd_unit_path('25-qdisc-hhf.network', '12-dummy.netdev')
         start_networkd()
+        self.wait_online(['dummy98:routable'])
+
+        output = check_output('tc qdisc show dev dummy98')
+        print(output)
+        self.assertRegex(output, 'qdisc hhf 3a: root')
+        self.assertRegex(output, 'limit 1022p')
 
+    @expectedFailureIfETSIsNotAvailable()
+    def test_qdisc_ets(self):
+        copy_unit_to_networkd_unit_path('25-qdisc-ets.network', '12-dummy.netdev')
+        start_networkd()
         self.wait_online(['dummy98:routable'])
 
         output = check_output('tc qdisc show dev dummy98')
         print(output)
-        self.assertRegex(output, 'qdisc teql1 2:')
+        self.assertRegex(output, 'qdisc ets 3a: root')
+        self.assertRegex(output, 'bands 10 strict 3')
+        self.assertRegex(output, 'quanta 1 2 3 4 5')
+        self.assertRegex(output, 'priomap 3 4 5 6 7')
+
+    @expectedFailureIfNetdevsimWithSRIOVIsNotAvailable()
+    def test_sriov(self):
+        call('rmmod netdevsim', stderr=subprocess.DEVNULL)
+        call('modprobe netdevsim', stderr=subprocess.DEVNULL)
+        with open('/sys/bus/netdevsim/new_device', mode='w') as f:
+            f.write('99 1')
+
+        call('udevadm settle')
+        call('udevadm info -w10s /sys/devices/netdevsim99/net/eni99np1', stderr=subprocess.DEVNULL)
+        with open('/sys/class/net/eni99np1/device/sriov_numvfs', mode='w') as f:
+            f.write('3')
+
+        copy_unit_to_networkd_unit_path('25-sriov.network')
+        start_networkd()
+        self.wait_online(['eni99np1:routable'])
+
+        output = check_output('ip link show dev eni99np1')
+        print(output)
+        self.assertRegex(output,
+                         'vf 0 .*00:11:22:33:44:55.*vlan 5, qos 1, vlan protocol 802.1ad, spoof checking on, link-state enable, trust on, query_rss on\n *'
+                         'vf 1 .*00:11:22:33:44:56.*vlan 6, qos 2, spoof checking off, link-state disable, trust off, query_rss off\n *'
+                         'vf 2 .*00:11:22:33:44:57.*vlan 7, qos 3, spoof checking off, link-state auto, trust off, query_rss off'
+        )
+
+        call('rmmod netdevsim', stderr=subprocess.DEVNULL)
 
 class NetworkdStateFileTests(unittest.TestCase, Utilities):
     links = [
@@ -2350,7 +2666,9 @@ class NetworkdStateFileTests(unittest.TestCase, Utilities):
 
         path = os.path.join('/run/systemd/netif/links/', ifindex)
         self.assertTrue(os.path.exists(path))
-        time.sleep(2)
+
+        # make link state file updated
+        check_output(*resolvectl_cmd, 'revert', 'dummy98', env=env)
 
         with open(path) as f:
             data = f.read()
@@ -2359,7 +2677,7 @@ class NetworkdStateFileTests(unittest.TestCase, Utilities):
             self.assertRegex(data, r'REQUIRED_FOR_ONLINE=yes')
             self.assertRegex(data, r'REQUIRED_OPER_STATE_FOR_ONLINE=routable')
             self.assertRegex(data, r'NETWORK_FILE=/run/systemd/network/state-file-tests.network')
-            self.assertRegex(data, r'DNS=10.10.10.10 10.10.10.11')
+            self.assertRegex(data, r'DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com \[1111:2222::3333\]:1234#ccc.com')
             self.assertRegex(data, r'NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org')
             self.assertRegex(data, r'DOMAINS=hogehoge')
             self.assertRegex(data, r'ROUTE_DOMAINS=foofoo')
@@ -2368,17 +2686,16 @@ class NetworkdStateFileTests(unittest.TestCase, Utilities):
             self.assertRegex(data, r'DNSSEC=no')
             self.assertRegex(data, r'ADDRESSES=192.168.(10.10|12.12)/24 192.168.(12.12|10.10)/24')
 
-        check_output(*resolvectl_cmd, 'dns', 'dummy98', '10.10.10.12', '10.10.10.13', env=env)
+        check_output(*resolvectl_cmd, 'dns', 'dummy98', '10.10.10.12#ccc.com', '10.10.10.13', '1111:2222::3333', env=env)
         check_output(*resolvectl_cmd, 'domain', 'dummy98', 'hogehogehoge', '~foofoofoo', env=env)
         check_output(*resolvectl_cmd, 'llmnr', 'dummy98', 'yes', env=env)
         check_output(*resolvectl_cmd, 'mdns', 'dummy98', 'no', env=env)
         check_output(*resolvectl_cmd, 'dnssec', 'dummy98', 'yes', env=env)
         check_output(*timedatectl_cmd, 'ntp-servers', 'dummy98', '2.fedora.pool.ntp.org', '3.fedora.pool.ntp.org', env=env)
-        time.sleep(2)
 
         with open(path) as f:
             data = f.read()
-            self.assertRegex(data, r'DNS=10.10.10.12 10.10.10.13')
+            self.assertRegex(data, r'DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333')
             self.assertRegex(data, r'NTP=2.fedora.pool.ntp.org 3.fedora.pool.ntp.org')
             self.assertRegex(data, r'DOMAINS=hogehogehoge')
             self.assertRegex(data, r'ROUTE_DOMAINS=foofoofoo')
@@ -2387,11 +2704,10 @@ class NetworkdStateFileTests(unittest.TestCase, Utilities):
             self.assertRegex(data, r'DNSSEC=yes')
 
         check_output(*timedatectl_cmd, 'revert', 'dummy98', env=env)
-        time.sleep(2)
 
         with open(path) as f:
             data = f.read()
-            self.assertRegex(data, r'DNS=10.10.10.12 10.10.10.13')
+            self.assertRegex(data, r'DNS=10.10.10.12#ccc.com 10.10.10.13 1111:2222::3333')
             self.assertRegex(data, r'NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org')
             self.assertRegex(data, r'DOMAINS=hogehogehoge')
             self.assertRegex(data, r'ROUTE_DOMAINS=foofoofoo')
@@ -2400,11 +2716,10 @@ class NetworkdStateFileTests(unittest.TestCase, Utilities):
             self.assertRegex(data, r'DNSSEC=yes')
 
         check_output(*resolvectl_cmd, 'revert', 'dummy98', env=env)
-        time.sleep(2)
 
         with open(path) as f:
             data = f.read()
-            self.assertRegex(data, r'DNS=10.10.10.10 10.10.10.11')
+            self.assertRegex(data, r'DNS=10.10.10.10#aaa.com 10.10.10.11:1111#bbb.com \[1111:2222::3333\]:1234#ccc.com')
             self.assertRegex(data, r'NTP=0.fedora.pool.ntp.org 1.fedora.pool.ntp.org')
             self.assertRegex(data, r'DOMAINS=hogehoge')
             self.assertRegex(data, r'ROUTE_DOMAINS=foofoo')
@@ -2512,6 +2827,7 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities):
         '11-dummy.netdev',
         '12-dummy.netdev',
         '26-bridge.netdev',
+        '26-bridge-configure-without-carrier.network',
         '26-bridge-slave-interface-1.network',
         '26-bridge-slave-interface-2.network',
         '26-bridge-vlan-master.network',
@@ -2626,6 +2942,54 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities):
         print(output)
         self.assertRegex(output, 'ff00::/8 table local metric 256 (linkdown )?pref medium')
 
+    def test_bridge_configure_without_carrier(self):
+        copy_unit_to_networkd_unit_path('26-bridge.netdev', '26-bridge-configure-without-carrier.network',
+                                        '11-dummy.netdev')
+        start_networkd()
+
+        # With ConfigureWithoutCarrier=yes, the bridge should remain configured for all these situations
+        for test in ['no-slave', 'add-slave', 'slave-up', 'slave-no-carrier', 'slave-carrier', 'slave-down']:
+            with self.subTest(test=test):
+                if test == 'no-slave':
+                    # bridge has no slaves; it's up but *might* not have carrier
+                    self.wait_operstate('bridge99', operstate=r'(no-carrier|routable)', setup_state=None, setup_timeout=30)
+                    # due to a bug in the kernel, newly-created bridges are brought up
+                    # *with* carrier, unless they have had any setting changed; e.g.
+                    # their mac set, priority set, etc.  Then, they will lose carrier
+                    # as soon as a (down) slave interface is added, and regain carrier
+                    # again once the slave interface is brought up.
+                    #self.check_link_attr('bridge99', 'carrier', '0')
+                elif test == 'add-slave':
+                    # add slave to bridge, but leave it down; bridge is definitely no-carrier
+                    self.check_link_attr('test1', 'operstate', 'down')
+                    check_output('ip link set dev test1 master bridge99')
+                    self.wait_operstate('bridge99', operstate='no-carrier', setup_state=None)
+                    self.check_link_attr('bridge99', 'carrier', '0')
+                elif test == 'slave-up':
+                    # bring up slave, which will have carrier; bridge gains carrier
+                    check_output('ip link set dev test1 up')
+                    self.wait_online(['bridge99:routable'])
+                    self.check_link_attr('bridge99', 'carrier', '1')
+                elif test == 'slave-no-carrier':
+                    # drop slave carrier; bridge loses carrier
+                    check_output('ip link set dev test1 carrier off')
+                    self.wait_online(['bridge99:no-carrier:no-carrier'])
+                    self.check_link_attr('bridge99', 'carrier', '0')
+                elif test == 'slave-carrier':
+                    # restore slave carrier; bridge gains carrier
+                    check_output('ip link set dev test1 carrier on')
+                    self.wait_online(['bridge99:routable'])
+                    self.check_link_attr('bridge99', 'carrier', '1')
+                elif test == 'slave-down':
+                    # bring down slave; bridge loses carrier
+                    check_output('ip link set dev test1 down')
+                    self.wait_online(['bridge99:no-carrier:no-carrier'])
+                    self.check_link_attr('bridge99', 'carrier', '0')
+
+                output = check_output(*networkctl_cmd, '-n', '0', 'status', 'bridge99', env=env)
+                self.assertRegex(output, '10.1.2.3')
+                self.assertRegex(output, '10.1.2.1')
+
     def test_bridge_ignore_carrier_loss(self):
         copy_unit_to_networkd_unit_path('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
                                         '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
@@ -2826,7 +3190,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
         'dhcp-client-ipv4-dhcp-settings.network',
         'dhcp-client-ipv4-only-ipv6-disabled.network',
         'dhcp-client-ipv4-only.network',
-        'dhcp-client-ipv4-use-routes-no.network',
+        'dhcp-client-ipv4-use-routes-use-gateway.network',
         'dhcp-client-ipv6-only.network',
         'dhcp-client-ipv6-rapid-commit.network',
         'dhcp-client-keep-configuration-dhcp-on-stop.network',
@@ -2841,7 +3205,6 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
         'dhcp-client-use-dns-no.network',
         'dhcp-client-use-dns-yes.network',
         'dhcp-client-use-domains.network',
-        'dhcp-client-use-routes-no.network',
         'dhcp-client-vrf.network',
         'dhcp-client-with-ipv4ll-fallback-with-dhcp-server.network',
         'dhcp-client-with-ipv4ll-fallback-without-dhcp-server.network',
@@ -2850,7 +3213,6 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
         'dhcp-server-decline.network',
         'dhcp-server-veth-peer.network',
         'dhcp-v4-server-veth-peer.network',
-        'dhcp-client-use-domains.network',
         'static.network']
 
     def setUp(self):
@@ -2879,6 +3241,12 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
         self.assertRegex(output, '2600::')
         self.assertNotRegex(output, '192.168.5')
 
+        output = check_output('ip addr show dev veth99')
+        print(output)
+        self.assertRegex(output, '2600::')
+        self.assertNotRegex(output, '192.168.5')
+        self.assertNotRegex(output, 'tentative')
+
         # Confirm that ipv6 token is not set in the kernel
         output = check_output('ip token show dev veth99')
         print(output)
@@ -2931,8 +3299,23 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
         self.assertRegex(output, r'192.168.5.7 proto dhcp scope link src 192.168.5.181 metric 1024')
         self.assertRegex(output, r'192.168.5.8 proto dhcp scope link src 192.168.5.181 metric 1024')
 
-    def test_dhcp_client_ipv4_use_routes_no(self):
-        copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv4-use-routes-no.network')
+    def test_dhcp_client_ipv4_use_routes_gateway(self):
+        for (routes, gateway, dnsroutes) in itertools.product([True, False, None], repeat=3):
+            self.setUp()
+            with self.subTest(routes=routes, gateway=gateway, dnsroutes=dnsroutes):
+                self._test_dhcp_client_ipv4_use_routes_gateway(routes, gateway, dnsroutes)
+            self.tearDown()
+
+    def _test_dhcp_client_ipv4_use_routes_gateway(self, routes, gateway, dnsroutes):
+        testunit = 'dhcp-client-ipv4-use-routes-use-gateway.network'
+        testunits = ['25-veth.netdev', 'dhcp-server-veth-peer.network', testunit]
+        if routes != None:
+            testunits.append(f'{testunit}.d/use-routes-{routes}.conf');
+        if gateway != None:
+            testunits.append(f'{testunit}.d/use-gateway-{gateway}.conf');
+        if dnsroutes != None:
+            testunits.append(f'{testunit}.d/use-dns-routes-{dnsroutes}.conf');
+        copy_unit_to_networkd_unit_path(*testunits, dropins=False)
 
         start_networkd()
         self.wait_online(['veth-peer:carrier'])
@@ -2941,9 +3324,31 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
 
         output = check_output('ip route show dev veth99')
         print(output)
-        self.assertNotRegex(output, r'192.168.5.5')
-        self.assertRegex(output, r'default via 192.168.5.1 proto dhcp src 192.168.5.181 metric 1024')
-        self.assertRegex(output, r'192.168.5.1 proto dhcp scope link src 192.168.5.181 metric 1024')
+
+        # UseRoutes= defaults to true
+        useroutes = routes in [True, None]
+        # UseGateway= defaults to useroutes
+        usegateway = useroutes if gateway == None else gateway
+
+        # Check UseRoutes=
+        if useroutes:
+            self.assertRegex(output, r'192.168.5.0/24 via 192.168.5.5 proto dhcp src 192.168.5.181 metric 1024')
+        else:
+            self.assertNotRegex(output, r'192.168.5.5')
+
+        # Check UseGateway=
+        if usegateway:
+            self.assertRegex(output, r'default via 192.168.5.1 proto dhcp src 192.168.5.181 metric 1024')
+        else:
+            self.assertNotRegex(output, r'default via 192.168.5.1')
+
+        # Check RoutesToDNS=, which defaults to false
+        if dnsroutes:
+            self.assertRegex(output, r'192.168.5.6 proto dhcp scope link src 192.168.5.181 metric 1024')
+            self.assertRegex(output, r'192.168.5.7 proto dhcp scope link src 192.168.5.181 metric 1024')
+        else:
+            self.assertNotRegex(output, r'192.168.5.6')
+            self.assertNotRegex(output, r'192.168.5.7')
 
     def test_dhcp_client_ipv4_ipv6(self):
         copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv6-only.network',
@@ -3591,10 +3996,15 @@ class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities):
         start_networkd()
         self.wait_online(['veth99:routable', 'veth-peer:routable'])
 
-        output = check_output('ip', '-6', 'route', 'show', 'dev', 'veth-peer')
+        output = check_output('ip -6 route show dev veth-peer')
         print(output)
         self.assertRegex(output, '2001:db8:0:1::/64 proto ra')
 
+        output = check_output('ip addr show dev veth99')
+        print(output)
+        self.assertNotRegex(output, '2001:db8:0:1')
+        self.assertRegex(output, '2001:db8:0:2')
+
 class NetworkdMTUTests(unittest.TestCase, Utilities):
     links = ['dummy98']
 
deleted file mode 120000 (symlink)
index a882b72cc9b07fb4fadc644e1af596f3293df005..0000000000000000000000000000000000000000
+++ /dev/null
@@ -1 +0,0 @@
-../../units/basic.target
\ No newline at end of file
new file mode 100644 (file)
index 0000000000000000000000000000000000000000..4f44292249405339fe4568885f4bb84291c1a558
--- /dev/null
@@ -0,0 +1,22 @@
+#  SPDX-License-Identifier: LGPL-2.1+
+#
+#  This file is part of systemd.
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU Lesser General Public License as published by
+#  the Free Software Foundation; either version 2.1 of the License, or
+#  (at your option) any later version.
+
+[Unit]
+Description=Basic System
+Documentation=man:systemd.special(7)
+Requires=sysinit.target
+Wants=sockets.target timers.target paths.target slices.target
+After=sysinit.target sockets.target paths.target slices.target tmp.mount
+
+# We support /var, /tmp, /var/tmp, being on NFS, but we don't pull in
+# remote-fs.target by default, hence pull them in explicitly here. Note that we
+# require /var and /var/tmp, but only add a Wants= type dependency on /tmp, as
+# we support that unit being masked, and this should not be considered an error.
+RequiresMountsFor=/var /var/tmp
+Wants=tmp.mount
deleted file mode 120000 (symlink)
index 8bdf1788301fb281b10602498531816fde5bbe50..0000000000000000000000000000000000000000
+++ /dev/null
@@ -1 +0,0 @@
-path-service.service
\ No newline at end of file
new file mode 100644 (file)
index 0000000000000000000000000000000000000000..fb465d76bbca5eed338988ecc969f57addbb1884
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=Service Test for Path units
+
+[Service]
+ExecStart=/bin/true
+Type=simple
+RemainAfterExit=true
deleted file mode 120000 (symlink)
index 8bdf1788301fb281b10602498531816fde5bbe50..0000000000000000000000000000000000000000
+++ /dev/null
@@ -1 +0,0 @@
-path-service.service
\ No newline at end of file
new file mode 100644 (file)
index 0000000000000000000000000000000000000000..fb465d76bbca5eed338988ecc969f57addbb1884
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=Service Test for Path units
+
+[Service]
+ExecStart=/bin/true
+Type=simple
+RemainAfterExit=true
deleted file mode 120000 (symlink)
index 8bdf1788301fb281b10602498531816fde5bbe50..0000000000000000000000000000000000000000
+++ /dev/null
@@ -1 +0,0 @@
-path-service.service
\ No newline at end of file
new file mode 100644 (file)
index 0000000000000000000000000000000000000000..fb465d76bbca5eed338988ecc969f57addbb1884
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=Service Test for Path units
+
+[Service]
+ExecStart=/bin/true
+Type=simple
+RemainAfterExit=true
deleted file mode 120000 (symlink)
index 8bdf1788301fb281b10602498531816fde5bbe50..0000000000000000000000000000000000000000
+++ /dev/null
@@ -1 +0,0 @@
-path-service.service
\ No newline at end of file
new file mode 100644 (file)
index 0000000000000000000000000000000000000000..fb465d76bbca5eed338988ecc969f57addbb1884
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=Service Test for Path units
+
+[Service]
+ExecStart=/bin/true
+Type=simple
+RemainAfterExit=true
deleted file mode 120000 (symlink)
index 8bdf1788301fb281b10602498531816fde5bbe50..0000000000000000000000000000000000000000
+++ /dev/null
@@ -1 +0,0 @@
-path-service.service
\ No newline at end of file
new file mode 100644 (file)
index 0000000000000000000000000000000000000000..fb465d76bbca5eed338988ecc969f57addbb1884
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=Service Test for Path units
+
+[Service]
+ExecStart=/bin/true
+Type=simple
+RemainAfterExit=true
deleted file mode 120000 (symlink)
index 8bdf1788301fb281b10602498531816fde5bbe50..0000000000000000000000000000000000000000
+++ /dev/null
@@ -1 +0,0 @@
-path-service.service
\ No newline at end of file
new file mode 100644 (file)
index 0000000000000000000000000000000000000000..fb465d76bbca5eed338988ecc969f57addbb1884
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=Service Test for Path units
+
+[Service]
+ExecStart=/bin/true
+Type=simple
+RemainAfterExit=true
index 172ac0d0d5fdf12997581be564ee2c4868b58ddd..bcdafe4f3072967bb0c6d0eee853aef9e3fc3952 100644 (file)
@@ -1,6 +1,7 @@
 [Unit]
-Description=Service Test Path Unit=
+Description=Service Test Path Unit
 
 [Service]
 ExecStart=/bin/true
-Type=oneshot
+Type=simple
+RemainAfterExit=true
deleted file mode 120000 (symlink)
index b402796cb9185fee824e77901338c08b1edddfe5..0000000000000000000000000000000000000000
+++ /dev/null
@@ -1 +0,0 @@
-../../units/paths.target
\ No newline at end of file
new file mode 100644 (file)
index 0000000000000000000000000000000000000000..9b6ed1c13feec452f236b8314fea492b89d62337
--- /dev/null
@@ -0,0 +1,12 @@
+#  SPDX-License-Identifier: LGPL-2.1+
+#
+#  This file is part of systemd.
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU Lesser General Public License as published by
+#  the Free Software Foundation; either version 2.1 of the License, or
+#  (at your option) any later version.
+
+[Unit]
+Description=Paths
+Documentation=man:systemd.special(7)
deleted file mode 120000 (symlink)
index 9d10e5b2e21b853d858b3d0162ca4594bcb2f185..0000000000000000000000000000000000000000
+++ /dev/null
@@ -1 +0,0 @@
-../../units/sysinit.target
\ No newline at end of file
new file mode 100644 (file)
index 0000000000000000000000000000000000000000..b6c16a14120f88f817470a0669a5f32406c06872
--- /dev/null
@@ -0,0 +1,15 @@
+#  SPDX-License-Identifier: LGPL-2.1+
+#
+#  This file is part of systemd.
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU Lesser General Public License as published by
+#  the Free Software Foundation; either version 2.1 of the License, or
+#  (at your option) any later version.
+
+[Unit]
+Description=System Initialization
+Documentation=man:systemd.special(7)
+Conflicts=emergency.service emergency.target
+Wants=local-fs.target swap.target
+After=local-fs.target swap.target emergency.service emergency.target
diff --git a/test/test-resolve/com~20200417.pkts b/test/test-resolve/com~20200417.pkts
new file mode 100644 (file)
index 0000000..7927a79
Binary files /dev/null and b/test/test-resolve/com~20200417.pkts differ
diff --git a/test/test-resolve/google.com.pkts b/test/test-resolve/google.com.pkts
deleted file mode 100644 (file)
index f98c4cd..0000000
Binary files a/test/test-resolve/google.com.pkts and /dev/null differ
diff --git a/test/test-resolve/google.com~20160131.pkts b/test/test-resolve/google.com~20160131.pkts
new file mode 100644 (file)
index 0000000..f98c4cd
Binary files /dev/null and b/test/test-resolve/google.com~20160131.pkts differ
diff --git a/test/test-resolve/google.com~20200417.pkts b/test/test-resolve/google.com~20200417.pkts
new file mode 100644 (file)
index 0000000..be944cd
Binary files /dev/null and b/test/test-resolve/google.com~20200417.pkts differ
diff --git a/test/test-resolve/michigan.gov~20200417.pkts b/test/test-resolve/michigan.gov~20200417.pkts
new file mode 100644 (file)
index 0000000..5db23aa
Binary files /dev/null and b/test/test-resolve/michigan.gov~20200417.pkts differ
diff --git a/test/test-resolve/org~20200417.pkts b/test/test-resolve/org~20200417.pkts
new file mode 100644 (file)
index 0000000..5fa4a16
Binary files /dev/null and b/test/test-resolve/org~20200417.pkts differ
diff --git a/test/test-resolve/vdwaa.nl~20200417.pkts b/test/test-resolve/vdwaa.nl~20200417.pkts
new file mode 100644 (file)
index 0000000..2964101
Binary files /dev/null and b/test/test-resolve/vdwaa.nl~20200417.pkts differ
diff --git a/test/testdata b/test/testdata
new file mode 120000 (symlink)
index 0000000..945c9b4
--- /dev/null
@@ -0,0 +1 @@
+.
\ No newline at end of file
diff --git a/test/testsuite-04.units/forever-print-hola.service b/test/testsuite-04.units/forever-print-hola.service
new file mode 100644 (file)
index 0000000..a0dc095
--- /dev/null
@@ -0,0 +1,6 @@
+[Unit]
+Description=ForeverPrintHola service
+
+[Service]
+Type=simple
+ExecStart=sh -x -c 'while :; do printf "Hola\n" || touch /i-lose-my-logs; sleep 1; done'
diff --git a/test/testsuite-06.units/hola.service b/test/testsuite-06.units/hola.service
new file mode 100644 (file)
index 0000000..5dc6332
--- /dev/null
@@ -0,0 +1,6 @@
+[Service]
+Type=oneshot
+ExecStart=/bin/echo Start Hola
+ExecReload=/bin/echo Reload Hola
+ExecStop=/bin/echo Stop Hola
+RemainAfterExit=yes
diff --git a/test/testsuite-06.units/load-systemd-test-module.service b/test/testsuite-06.units/load-systemd-test-module.service
new file mode 100644 (file)
index 0000000..323a76c
--- /dev/null
@@ -0,0 +1,14 @@
+[Unit]
+Description=Load systemd-test module
+DefaultDependencies=no
+Requires=local-fs.target
+Conflicts=shutdown.target
+After=local-fs.target
+Before=sysinit.target shutdown.target autorelabel.service
+ConditionSecurity=selinux
+
+[Service]
+ExecStart=sh -x -c 'echo 0 >/sys/fs/selinux/enforce && cd /systemd-test-module && make -f /usr/share/selinux/devel/Makefile load'
+Type=oneshot
+TimeoutSec=0
+RemainAfterExit=yes
diff --git a/test/testsuite-08.units/-.mount b/test/testsuite-08.units/-.mount
new file mode 100644 (file)
index 0000000..af4e219
--- /dev/null
@@ -0,0 +1,12 @@
+[Unit]
+Before=local-fs.target
+
+[Mount]
+What=/dev/sda1
+Where=/
+Type=ext4
+Options=errors=remount-ro,noatime
+
+[Install]
+WantedBy=local-fs.target
+Alias=root.mount
diff --git a/test/testsuite-08.units/local-fs.target.wants/-.mount b/test/testsuite-08.units/local-fs.target.wants/-.mount
new file mode 120000 (symlink)
index 0000000..5566fce
--- /dev/null
@@ -0,0 +1 @@
+../-.mount
\ No newline at end of file
diff --git a/test/testsuite-08.units/root.mount b/test/testsuite-08.units/root.mount
new file mode 120000 (symlink)
index 0000000..fd8c47d
--- /dev/null
@@ -0,0 +1 @@
+-.mount
\ No newline at end of file
diff --git a/test/testsuite-08.units/systemd-remount-fs.service b/test/testsuite-08.units/systemd-remount-fs.service
new file mode 100644 (file)
index 0000000..398d612
--- /dev/null
@@ -0,0 +1,11 @@
+[Unit]
+DefaultDependencies=no
+Conflicts=shutdown.target
+After=systemd-fsck-root.service
+Before=local-fs-pre.target local-fs.target shutdown.target
+Wants=local-fs-pre.target
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart=/bin/systemctl reload /
diff --git a/test/testsuite-10.units/test10.service b/test/testsuite-10.units/test10.service
new file mode 100644 (file)
index 0000000..d0be786
--- /dev/null
@@ -0,0 +1,6 @@
+[Unit]
+Requires=test10.socket
+ConditionPathExistsGlob=/tmp/nonexistent
+
+[Service]
+ExecStart=true
diff --git a/test/testsuite-10.units/test10.socket b/test/testsuite-10.units/test10.socket
new file mode 100644 (file)
index 0000000..9cceebb
--- /dev/null
@@ -0,0 +1,2 @@
+[Socket]
+ListenStream=/run/test.ctl
diff --git a/test/testsuite-11.units/fail-on-restart.service b/test/testsuite-11.units/fail-on-restart.service
new file mode 100644 (file)
index 0000000..9264f15
--- /dev/null
@@ -0,0 +1,9 @@
+[Unit]
+Description=Fail on restart
+StartLimitIntervalSec=1m
+StartLimitBurst=3
+
+[Service]
+Type=simple
+ExecStart=false
+Restart=always
diff --git a/test/testsuite-16.units/extend-timeout.sh b/test/testsuite-16.units/extend-timeout.sh
new file mode 100755 (executable)
index 0000000..ed1af8a
--- /dev/null
@@ -0,0 +1,62 @@
+#!/usr/bin/env bash
+set -x
+set -e
+set -o pipefail
+
+# sleep interval (seconds)
+: ${sleep_interval:=1}
+# extend_timeout_interval second(s)
+: ${extend_timeout_interval:=1}
+# number of sleep_intervals before READY=1
+: ${start_intervals:=10}
+# number of sleep_intervals before exiting
+: ${stop_intervals:=10}
+# run intervals, number of sleep_intervals to run
+: ${run_intervals:=7}
+
+# We convert to usec
+extend_timeout_interval=$(( $extend_timeout_interval * 1000000 ))
+
+trap "{ touch /${SERVICE}.terminated; exit 1; }"  SIGTERM SIGABRT
+
+rm -f /${SERVICE}.*
+touch /${SERVICE}.startfail
+
+systemd-notify EXTEND_TIMEOUT_USEC=$extend_timeout_interval
+while [ $start_intervals -gt 0 ]
+do
+    sleep $sleep_interval
+    start_intervals=$(( $start_intervals - 1 ))
+    systemd-notify EXTEND_TIMEOUT_USEC=$extend_timeout_interval
+done
+
+systemd-notify --ready --status="Waiting for your request"
+
+touch /${SERVICE}.runtimefail
+rm /${SERVICE}.startfail
+
+systemd-notify EXTEND_TIMEOUT_USEC=$extend_timeout_interval
+while [ $run_intervals -gt 0 ]
+do
+    sleep $sleep_interval
+    run_intervals=$(( $run_intervals - 1 ))
+    systemd-notify EXTEND_TIMEOUT_USEC=$extend_timeout_interval
+done
+
+systemd-notify STOPPING=1
+
+touch /${SERVICE}.stopfail
+rm /${SERVICE}.runtimefail
+
+systemd-notify EXTEND_TIMEOUT_USEC=$extend_timeout_interval
+while [ $stop_intervals -gt 0 ]
+do
+    sleep $sleep_interval
+    stop_intervals=$(( $stop_intervals - 1 ))
+    systemd-notify EXTEND_TIMEOUT_USEC=$extend_timeout_interval
+done
+
+touch /${SERVICE}.success
+rm /${SERVICE}.stopfail
+
+exit 0
diff --git a/test/testsuite-16.units/fail-runtime.service b/test/testsuite-16.units/fail-runtime.service
new file mode 100644 (file)
index 0000000..baa655f
--- /dev/null
@@ -0,0 +1,12 @@
+[Unit]
+Description=Testsuite: Fail Runtime (EXTEND_TIMEOUT_USEC Didn't occur in sufficient time after RuntimeSecMax.)
+
+[Service]
+# EXTEND_TIMEOUT_USEC on runtime start (0) and 7 seconds after. Systemd will expect one at 7+5 (extend_timeout_interval)
+# seconds this won't happen until 7 + 7 (sleep interval) seconds. Therefore timeout at 12 seconds.
+Type=notify
+TimeoutStartSec=4
+TimeoutStopSec=4
+RuntimeMaxSec=10
+Environment=SERVICE=fail_runtime extend_timeout_interval=5 sleep_interval=7 start_intervals=0 run_intervals=2 stop_intervals=0
+ExecStart=/usr/lib/systemd/tests/testdata/testsuite-16.units/extend-timeout.sh
diff --git a/test/testsuite-16.units/fail-start.service b/test/testsuite-16.units/fail-start.service
new file mode 100644 (file)
index 0000000..8829004
--- /dev/null
@@ -0,0 +1,13 @@
+[Unit]
+Description=Testsuite: Fail Start (EXTEND_TIMEOUT_USEC Didn't occur in sufficient time after TimeoutStartSec.)
+
+[Service]
+
+# EXTEND_TIMEOUT_USEC on startup and 7 seconds from start. Systemd will expect one at 7+5 (extend_timeout_interval)
+# seconds  this won't happen until 7 + 7 (sleep interval) seconds. Therefore timeout at 12 seconds.
+Type=notify
+TimeoutStartSec=10
+TimeoutStopSec=4
+RuntimeMaxSec=4
+Environment=SERVICE=fail_start extend_timeout_interval=5 sleep_interval=7 start_intervals=2 run_intervals=0 stop_intervals=0
+ExecStart=/usr/lib/systemd/tests/testdata/testsuite-16.units/extend-timeout.sh
diff --git a/test/testsuite-16.units/fail-stop.service b/test/testsuite-16.units/fail-stop.service
new file mode 100644 (file)
index 0000000..cdea2a9
--- /dev/null
@@ -0,0 +1,15 @@
+[Unit]
+Description=Testsuite: Fail Stop (EXTEND_TIMEOUT_USEC Didn't occur in sufficient time after TimeoutStopSec.)
+
+[Service]
+# EXTEND_TIMEOUT_USEC on stop (0) and 7 seconds after. Systemd will expect one at 7+5 (extend_timeout_interval)
+# seconds this won't happen until 7 + 7 (sleep interval) seconds. Therefore timeout at 12 seconds.
+Type=notify
+TimeoutStartSec=4
+TimeoutStopSec=10
+RuntimeMaxSec=4
+Environment=SERVICE=fail_stop extend_timeout_interval=5 sleep_interval=7 start_intervals=0 run_intervals=0 stop_intervals=2
+ExecStart=/usr/lib/systemd/tests/testdata/testsuite-16.units/extend-timeout.sh
+# Due to 6041a7ee2c1bbff6301082f192fc1b0882400d42 SIGTERM isn't sent as the service shuts down with STOPPING=1
+# This file makes the test assess.sh quicker by notifing it that this test has finished.
+ExecStopPost=/bin/bash -c '[[ $SERVICE_RESULT == timeout && $EXIT_CODE == killed ]] && touch /fail_runtime.terminated'
diff --git a/test/testsuite-16.units/success-all.service b/test/testsuite-16.units/success-all.service
new file mode 100644 (file)
index 0000000..e2d7e60
--- /dev/null
@@ -0,0 +1,14 @@
+[Unit]
+Description=Testsuite: EXTEND_TIMEOUT_USEC Success - extend timeout on all services
+
+[Service]
+
+# Normal success - startup / runtime / shutdown all take 8 seconds which is within the EXTEND_TIMEOUT_USEC=4 seconds interval
+# runtime is 8+8+8 seconds. so we are relying on the EXTEND_TIMEOUT_USEC to exceed all stages, Start, Runtime and Stop.
+# success occurs after 24 seconds
+Type=notify
+TimeoutStartSec=4
+TimeoutStopSec=4
+RuntimeMaxSec=4
+Environment=SERVICE=success_all extend_timeout_interval=4 sleep_interval=2 start_intervals=3 run_intervals=3 stop_intervals=3
+ExecStart=/usr/lib/systemd/tests/testdata/testsuite-16.units/extend-timeout.sh
diff --git a/test/testsuite-16.units/success-runtime.service b/test/testsuite-16.units/success-runtime.service
new file mode 100644 (file)
index 0000000..15283b7
--- /dev/null
@@ -0,0 +1,13 @@
+[Unit]
+Description=Testsuite: Success Runtime (EXTEND_TIMEOUT_USEC > WATCHDOG_USEC however < RuntimeMaxSec)
+
+[Service]
+
+# EXTEND_TIMEOUT_USEC=4 second once during runtime, but sleep for 6 seconds.
+# Runtime is 6 seconds and < RuntimeMaxSec so still successful.
+Type=notify
+TimeoutStartSec=4
+TimeoutStopSec=4
+RuntimeMaxSec=8
+Environment=SERVICE=success_runtime extend_timeout_interval=4 sleep_interval=6 start_intervals=0 run_intervals=1 stop_intervals=0
+ExecStart=/usr/lib/systemd/tests/testdata/testsuite-16.units/extend-timeout.sh
diff --git a/test/testsuite-16.units/success-start.service b/test/testsuite-16.units/success-start.service
new file mode 100644 (file)
index 0000000..cfdcc33
--- /dev/null
@@ -0,0 +1,12 @@
+[Unit]
+Description=Testsuite: Success Start (EXTEND_TIMEOUT_USEC > WATCHDOG_USEC however < TimeoutStartSec)
+
+[Service]
+# EXTEND_TIMEOUT_USEC=4 second interval once at startup, but sleep 6 seconds.
+# Therefore startup is 6 seconds and < TimeoutStartSec so still successful.
+Type=notify
+TimeoutStartSec=8
+TimeoutStopSec=4
+RuntimeMaxSec=4
+Environment=SERVICE=success_start extend_timeout_interval=4 sleep_interval=6 start_intervals=1 run_intervals=0 stop_intervals=0
+ExecStart=/usr/lib/systemd/tests/testdata/testsuite-16.units/extend-timeout.sh
diff --git a/test/testsuite-16.units/success-stop.service b/test/testsuite-16.units/success-stop.service
new file mode 100644 (file)
index 0000000..c4600ac
--- /dev/null
@@ -0,0 +1,12 @@
+[Unit]
+Description=Testsuite: Success Stop (EXTEND_TIMEOUT_USEC > WATCHDOG_USEC however < TimeoutStopSec)
+
+[Service]
+# EXTEND_TIMEOUT_USEC=4 seconds once during shutdown, but sleep for 6 seconds.
+# Therefore stop time is 6 seconds and < TimeoutStopSec so still successful.
+Type=notify
+TimeoutStartSec=4
+TimeoutStopSec=8
+RuntimeMaxSec=4
+Environment=SERVICE=success_stop extend_timeout_interval=4 sleep_interval=6 start_intervals=0 run_intervals=0 stop_intervals=1
+ExecStart=/usr/lib/systemd/tests/testdata/testsuite-16.units/extend-timeout.sh
diff --git a/test/testsuite-28.units/specifier-j-depends-wants.service b/test/testsuite-28.units/specifier-j-depends-wants.service
new file mode 100644 (file)
index 0000000..f9c6abb
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=Dependent service for percent-j specifier
+After=testsuite-28-pre.service
+
+[Service]
+Type=oneshot
+ExecStart=touch /tmp/test-specifier-j-wants
diff --git a/test/testsuite-28.units/specifier-j-wants.service b/test/testsuite-28.units/specifier-j-wants.service
new file mode 100644 (file)
index 0000000..facf557
--- /dev/null
@@ -0,0 +1,10 @@
+[Unit]
+Description=Wants with percent-j specifier
+Wants=specifier-j-depends-%j.service
+After=specifier-j-depends-%j.service
+After=testsuite-28-pre.service
+
+[Service]
+Type=oneshot
+ExecStart=test -f /tmp/test-specifier-j-%j
+ExecStart=sh -c 'echo OK > /testok'
diff --git a/test/testsuite-28.units/testsuite-28-pre.service b/test/testsuite-28.units/testsuite-28-pre.service
new file mode 100644 (file)
index 0000000..2b8ef98
--- /dev/null
@@ -0,0 +1,3 @@
+[Service]
+ExecStart=rm -f /failed /testok
+Type=oneshot
diff --git a/test/testsuite-30.units/systemd-timedated.service.d/watchdog.conf b/test/testsuite-30.units/systemd-timedated.service.d/watchdog.conf
new file mode 100644 (file)
index 0000000..d5ed27c
--- /dev/null
@@ -0,0 +1,2 @@
+[Service]
+WatchdogSec=10min
diff --git a/test/testsuite-52.units/testsuite-52.service b/test/testsuite-52.units/testsuite-52.service
new file mode 100755 (executable)
index 0000000..93f847f
--- /dev/null
@@ -0,0 +1,6 @@
+[Unit]
+Description=Testsuite service
+
+[Service]
+ExecStart=/usr/lib/systemd/tests/testdata/%N.units/%N.sh
+Type=oneshot
diff --git a/test/testsuite-52.units/testsuite-52.sh b/test/testsuite-52.units/testsuite-52.sh
new file mode 100755 (executable)
index 0000000..9cccf1b
--- /dev/null
@@ -0,0 +1,18 @@
+#!/bin/bash
+set -ex
+set -o pipefail
+
+if ! test -x /usr/lib/systemd/tests/testdata/units/test-honor-first-shutdown.sh ; then
+        echo "honor-first-shutdown script not found - FAIL" > /testok
+        exit 0
+fi
+
+systemd-analyze log-level debug
+systemd-analyze log-target console
+
+systemctl enable test-honor-first-shutdown.service
+systemctl start test-honor-first-shutdown.service
+
+echo OK > /testok
+
+exit 0
diff --git a/test/testsuite.target b/test/testsuite.target
deleted file mode 100644 (file)
index 1a7e5b3..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-[Unit]
-Description=Testsuite target
-Requires=multi-user.target
-After=multi-user.target
-Conflicts=rescue.target
-AllowIsolate=yes
diff --git a/test/timers.target b/test/timers.target
deleted file mode 120000 (symlink)
index 576d47f..0000000
+++ /dev/null
@@ -1 +0,0 @@
-../units/timers.target
\ No newline at end of file
diff --git a/test/unit-.service.d/10-override.conf b/test/unit-.service.d/10-override.conf
deleted file mode 100644 (file)
index 916737d..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-[Unit]
-Description=override0
diff --git a/test/unit-with-.service.d/20-override.conf b/test/unit-with-.service.d/20-override.conf
deleted file mode 100644 (file)
index c6c2438..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-[Unit]
-Documentation=man:override1
diff --git a/test/unit-with-multiple-.service.d/20-override.conf b/test/unit-with-multiple-.service.d/20-override.conf
deleted file mode 100644 (file)
index 62fafd2..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-[Unit]
-Documentation=man:override2
diff --git a/test/unit-with-multiple-.service.d/30-override.conf b/test/unit-with-multiple-.service.d/30-override.conf
deleted file mode 100644 (file)
index b9616da..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-[Unit]
-Documentation=man:override3
diff --git a/test/unit-with-multiple-dashes.service b/test/unit-with-multiple-dashes.service
deleted file mode 100644 (file)
index b38b360..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-[Unit]
-Description=A unit with multiple dashes
-Documentation=man:test
-
-[Service]
-ExecStart=/bin/true
diff --git a/test/unit-with-multiple-dashes.service.d/10-override.conf b/test/unit-with-multiple-dashes.service.d/10-override.conf
deleted file mode 100644 (file)
index 982c362..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-[Unit]
-Description=override4
diff --git a/test/units/a-conj.service b/test/units/a-conj.service
new file mode 100644 (file)
index 0000000..db37ae7
--- /dev/null
@@ -0,0 +1,8 @@
+[Unit]
+Description=A conjugate
+Requires=a.service
+After=a.service
+Before=a.service
+
+[Service]
+ExecStart=/bin/true
diff --git a/test/units/a.service b/test/units/a.service
new file mode 100644 (file)
index 0000000..4168d2d
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=A
+Requires=b.service
+Before=b.service
+
+[Service]
+ExecStart=/bin/true
diff --git a/test/units/autorelabel.service b/test/units/autorelabel.service
new file mode 100644 (file)
index 0000000..cb38849
--- /dev/null
@@ -0,0 +1,18 @@
+[Unit]
+Description=Relabel all filesystems
+DefaultDependencies=no
+Requires=local-fs.target
+Conflicts=shutdown.target
+After=local-fs.target
+Before=sysinit.target shutdown.target
+ConditionSecurity=selinux
+ConditionPathExists=|/.autorelabel
+
+[Service]
+ExecStart=sh -x -c 'echo 0 >/sys/fs/selinux/enforce && fixfiles -f -F relabel && rm /.autorelabel && systemctl --force reboot'
+Type=oneshot
+TimeoutSec=0
+RemainAfterExit=yes
+
+[Install]
+WantedBy=basic.target
diff --git a/test/units/b.service b/test/units/b.service
new file mode 100644 (file)
index 0000000..e03bae3
--- /dev/null
@@ -0,0 +1,6 @@
+[Unit]
+Description=B
+Wants=f.service
+
+[Service]
+ExecStart=/bin/true
diff --git a/test/units/basic.target b/test/units/basic.target
new file mode 100644 (file)
index 0000000..4f44292
--- /dev/null
@@ -0,0 +1,22 @@
+#  SPDX-License-Identifier: LGPL-2.1+
+#
+#  This file is part of systemd.
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU Lesser General Public License as published by
+#  the Free Software Foundation; either version 2.1 of the License, or
+#  (at your option) any later version.
+
+[Unit]
+Description=Basic System
+Documentation=man:systemd.special(7)
+Requires=sysinit.target
+Wants=sockets.target timers.target paths.target slices.target
+After=sysinit.target sockets.target paths.target slices.target tmp.mount
+
+# We support /var, /tmp, /var/tmp, being on NFS, but we don't pull in
+# remote-fs.target by default, hence pull them in explicitly here. Note that we
+# require /var and /var/tmp, but only add a Wants= type dependency on /tmp, as
+# we support that unit being masked, and this should not be considered an error.
+RequiresMountsFor=/var /var/tmp
+Wants=tmp.mount
diff --git a/test/units/c.service b/test/units/c.service
new file mode 100644 (file)
index 0000000..e2f60a8
--- /dev/null
@@ -0,0 +1,6 @@
+[Unit]
+Description=C
+Requires=a.service
+
+[Service]
+ExecStart=/bin/true
diff --git a/test/units/d.service b/test/units/d.service
new file mode 100644 (file)
index 0000000..921fd2e
--- /dev/null
@@ -0,0 +1,8 @@
+[Unit]
+Description=D:Cyclic
+After=b.service
+Before=a.service
+Requires=a.service
+
+[Service]
+ExecStart=/bin/true
diff --git a/test/units/daughter.service b/test/units/daughter.service
new file mode 100644 (file)
index 0000000..c790b9d
--- /dev/null
@@ -0,0 +1,8 @@
+[Unit]
+Description=Daughter Service
+
+[Service]
+Slice=parent.slice
+Type=oneshot
+ExecStart=/bin/true
+CPUAccounting=true
diff --git a/test/units/dml-discard-empty.service b/test/units/dml-discard-empty.service
new file mode 100644 (file)
index 0000000..75228f6
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=DML discard empty service
+
+[Service]
+Slice=dml-discard.slice
+Type=oneshot
+ExecStart=/bin/true
diff --git a/test/units/dml-discard-set-ml.service b/test/units/dml-discard-set-ml.service
new file mode 100644 (file)
index 0000000..591c992
--- /dev/null
@@ -0,0 +1,8 @@
+[Unit]
+Description=DML discard set ml service
+
+[Service]
+Slice=dml-discard.slice
+Type=oneshot
+ExecStart=/bin/true
+MemoryLow=15
diff --git a/test/units/dml-discard.slice b/test/units/dml-discard.slice
new file mode 100644 (file)
index 0000000..e26d868
--- /dev/null
@@ -0,0 +1,5 @@
+[Unit]
+Description=DML discard slice
+
+[Slice]
+DefaultMemoryLow=
diff --git a/test/units/dml-override-empty.service b/test/units/dml-override-empty.service
new file mode 100644 (file)
index 0000000..142c987
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=DML override empty service
+
+[Service]
+Slice=dml-override.slice
+Type=oneshot
+ExecStart=/bin/true
diff --git a/test/units/dml-override.slice b/test/units/dml-override.slice
new file mode 100644 (file)
index 0000000..feb6773
--- /dev/null
@@ -0,0 +1,5 @@
+[Unit]
+Description=DML override slice
+
+[Slice]
+DefaultMemoryLow=10
diff --git a/test/units/dml-passthrough-empty.service b/test/units/dml-passthrough-empty.service
new file mode 100644 (file)
index 0000000..34832de
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=DML passthrough empty service
+
+[Service]
+Slice=dml-passthrough.slice
+Type=oneshot
+ExecStart=/bin/true
diff --git a/test/units/dml-passthrough-set-dml.service b/test/units/dml-passthrough-set-dml.service
new file mode 100644 (file)
index 0000000..5bdf4ed
--- /dev/null
@@ -0,0 +1,8 @@
+[Unit]
+Description=DML passthrough set DML service
+
+[Service]
+Slice=dml-passthrough.slice
+Type=oneshot
+ExecStart=/bin/true
+DefaultMemoryLow=15
diff --git a/test/units/dml-passthrough-set-ml.service b/test/units/dml-passthrough-set-ml.service
new file mode 100644 (file)
index 0000000..2e568b5
--- /dev/null
@@ -0,0 +1,8 @@
+[Unit]
+Description=DML passthrough set ML service
+
+[Service]
+Slice=dml-passthrough.slice
+Type=oneshot
+ExecStart=/bin/true
+MemoryLow=0
diff --git a/test/units/dml-passthrough.slice b/test/units/dml-passthrough.slice
new file mode 100644 (file)
index 0000000..1b1a848
--- /dev/null
@@ -0,0 +1,5 @@
+[Unit]
+Description=DML passthrough slice
+
+[Slice]
+MemoryLow=100
diff --git a/test/units/dml.slice b/test/units/dml.slice
new file mode 100644 (file)
index 0000000..84e333e
--- /dev/null
@@ -0,0 +1,5 @@
+[Unit]
+Description=DML slice
+
+[Slice]
+DefaultMemoryLow=50
diff --git a/test/units/e.service b/test/units/e.service
new file mode 100644 (file)
index 0000000..5ba98c7
--- /dev/null
@@ -0,0 +1,8 @@
+[Unit]
+Description=E:Cyclic
+After=b.service
+Before=a.service
+Wants=a.service
+
+[Service]
+ExecStart=/bin/true
diff --git a/test/units/end.service b/test/units/end.service
new file mode 100644 (file)
index 0000000..e7ed75e
--- /dev/null
@@ -0,0 +1,10 @@
+[Unit]
+Description=End the test
+After=testsuite.target
+OnFailure=poweroff.target
+OnFailureJobMode=replace-irreversibly
+
+[Service]
+Type=oneshot
+ExecStart=/bin/sh -x -c 'systemctl poweroff --no-block'
+TimeoutStartSec=5m
diff --git a/test/units/f.service b/test/units/f.service
new file mode 100644 (file)
index 0000000..7dde681
--- /dev/null
@@ -0,0 +1,5 @@
+[Unit]
+Description=F
+
+[Service]
+ExecStart=/bin/true
diff --git a/test/units/g.service b/test/units/g.service
new file mode 100644 (file)
index 0000000..cbfa82a
--- /dev/null
@@ -0,0 +1,6 @@
+[Unit]
+Description=G
+Conflicts=e.service
+
+[Service]
+ExecStart=/bin/true
diff --git a/test/units/grandchild.service b/test/units/grandchild.service
new file mode 100644 (file)
index 0000000..ab64130
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=Grandchild Service
+
+[Service]
+Slice=parent-deep.slice
+Type=oneshot
+ExecStart=/bin/true
diff --git a/test/units/h.service b/test/units/h.service
new file mode 100644 (file)
index 0000000..74a7751
--- /dev/null
@@ -0,0 +1,6 @@
+[Unit]
+Description=H
+Wants=g.service
+
+[Service]
+ExecStart=/bin/true
diff --git a/test/units/hello-after-sleep.target b/test/units/hello-after-sleep.target
new file mode 100644 (file)
index 0000000..526fbd2
--- /dev/null
@@ -0,0 +1,5 @@
+[Unit]
+Description=Sleep for a minute, then say hello.
+Wants=sleep.service hello.service
+After=sleep.service
+Before=hello.service
diff --git a/test/units/hello.service b/test/units/hello.service
new file mode 100644 (file)
index 0000000..82907b6
--- /dev/null
@@ -0,0 +1,5 @@
+[Unit]
+Description=Hello World
+
+[Service]
+ExecStart=/bin/echo "Hello World"
diff --git a/test/units/i.service b/test/units/i.service
new file mode 100644 (file)
index 0000000..938ea77
--- /dev/null
@@ -0,0 +1,8 @@
+[Unit]
+Description=I
+Conflicts=a.service d.service
+Wants=b.service
+After=b.service
+
+[Service]
+ExecStart=/bin/true
diff --git a/test/units/loopy.service b/test/units/loopy.service
new file mode 100644 (file)
index 0000000..9eb6457
--- /dev/null
@@ -0,0 +1,2 @@
+[Service]
+ExecStart=/bin/true
diff --git a/test/units/loopy.service.d/compat.conf b/test/units/loopy.service.d/compat.conf
new file mode 100644 (file)
index 0000000..51b84b8
--- /dev/null
@@ -0,0 +1,5 @@
+[Unit]
+BindsTo=loopy2.service
+
+[Install]
+Also=loopy2.service
diff --git a/test/units/loopy2.service b/test/units/loopy2.service
new file mode 100644 (file)
index 0000000..9eb6457
--- /dev/null
@@ -0,0 +1,2 @@
+[Service]
+ExecStart=/bin/true
diff --git a/test/units/loopy3.service b/test/units/loopy3.service
new file mode 100644 (file)
index 0000000..606e26b
--- /dev/null
@@ -0,0 +1,5 @@
+[Service]
+ExecStart=/bin/true
+
+[Unit]
+Conflicts=loopy4.service
diff --git a/test/units/loopy4.service b/test/units/loopy4.service
new file mode 100644 (file)
index 0000000..606e26b
--- /dev/null
@@ -0,0 +1,5 @@
+[Service]
+ExecStart=/bin/true
+
+[Unit]
+Conflicts=loopy4.service
diff --git a/test/units/nomem.slice b/test/units/nomem.slice
new file mode 100644 (file)
index 0000000..9c5d208
--- /dev/null
@@ -0,0 +1,5 @@
+[Unit]
+Description=Nomem Parent Slice
+
+[Slice]
+DisableControllers=memory
diff --git a/test/units/nomemleaf.service b/test/units/nomemleaf.service
new file mode 100644 (file)
index 0000000..3cbaccb
--- /dev/null
@@ -0,0 +1,9 @@
+[Unit]
+Description=Nomem Leaf Service
+
+[Service]
+Slice=nomem.slice
+Type=oneshot
+ExecStart=/bin/true
+IOWeight=200
+MemoryAccounting=true
diff --git a/test/units/parent-deep.slice b/test/units/parent-deep.slice
new file mode 100644 (file)
index 0000000..79b302f
--- /dev/null
@@ -0,0 +1,5 @@
+[Unit]
+Description=Deeper Parent Slice
+
+[Slice]
+MemoryLimit=3G
diff --git a/test/units/parent.slice b/test/units/parent.slice
new file mode 100644 (file)
index 0000000..a95f903
--- /dev/null
@@ -0,0 +1,5 @@
+[Unit]
+Description=Parent Slice
+
+[Slice]
+IOWeight=200
diff --git a/test/units/sched_idle_bad.service b/test/units/sched_idle_bad.service
new file mode 100644 (file)
index 0000000..589a87c
--- /dev/null
@@ -0,0 +1,6 @@
+[Unit]
+Description=Bad sched priority for Idle
+
+[Service]
+ExecStart=/bin/true
+CPUSchedulingPriority=1
diff --git a/test/units/sched_idle_ok.service b/test/units/sched_idle_ok.service
new file mode 100644 (file)
index 0000000..262ef3e
--- /dev/null
@@ -0,0 +1,6 @@
+[Unit]
+Description=Sched idle with prio 0
+
+[Service]
+ExecStart=/bin/true
+CPUSchedulingPriority=0
diff --git a/test/units/sched_rr_bad.service b/test/units/sched_rr_bad.service
new file mode 100644 (file)
index 0000000..0be534a
--- /dev/null
@@ -0,0 +1,8 @@
+[Unit]
+Description=Bad sched priority for RR
+
+[Service]
+ExecStart=/bin/true
+CPUSchedulingPolicy=rr
+CPUSchedulingPriority=0
+CPUSchedulingPriority=100
diff --git a/test/units/sched_rr_change.service b/test/units/sched_rr_change.service
new file mode 100644 (file)
index 0000000..b3e3a00
--- /dev/null
@@ -0,0 +1,9 @@
+[Unit]
+Description=Change prio
+
+[Service]
+ExecStart=/bin/true
+CPUSchedulingPolicy=rr
+CPUSchedulingPriority=1
+CPUSchedulingPriority=2
+CPUSchedulingPriority=99
diff --git a/test/units/sched_rr_ok.service b/test/units/sched_rr_ok.service
new file mode 100644 (file)
index 0000000..b88adc5
--- /dev/null
@@ -0,0 +1,6 @@
+[Unit]
+Description=Default prio for RR
+
+[Service]
+ExecStart=/bin/true
+CPUSchedulingPolicy=rr
diff --git a/test/units/shutdown.target b/test/units/shutdown.target
new file mode 100644 (file)
index 0000000..d48e6d6
--- /dev/null
@@ -0,0 +1,14 @@
+#  SPDX-License-Identifier: LGPL-2.1+
+#
+#  This file is part of systemd.
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU Lesser General Public License as published by
+#  the Free Software Foundation; either version 2.1 of the License, or
+#  (at your option) any later version.
+
+[Unit]
+Description=Shutdown
+Documentation=man:systemd.special(7)
+DefaultDependencies=no
+RefuseManualStart=yes
diff --git a/test/units/sleep.service b/test/units/sleep.service
new file mode 100644 (file)
index 0000000..946c44b
--- /dev/null
@@ -0,0 +1,6 @@
+[Unit]
+Description=Sleep for 1 minute
+
+[Service]
+Type=oneshot
+ExecStart=/bin/sleep 60
diff --git a/test/units/sockets.target b/test/units/sockets.target
new file mode 100644 (file)
index 0000000..9af67fd
--- /dev/null
@@ -0,0 +1,12 @@
+#  SPDX-License-Identifier: LGPL-2.1+
+#
+#  This file is part of systemd.
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU Lesser General Public License as published by
+#  the Free Software Foundation; either version 2.1 of the License, or
+#  (at your option) any later version.
+
+[Unit]
+Description=Sockets
+Documentation=man:systemd.special(7)
diff --git a/test/units/son.service b/test/units/son.service
new file mode 100644 (file)
index 0000000..50bb96a
--- /dev/null
@@ -0,0 +1,8 @@
+[Unit]
+Description=Son Service
+
+[Service]
+Slice=parent.slice
+Type=oneshot
+ExecStart=/bin/true
+CPUShares=100
diff --git a/test/units/sysinit.target b/test/units/sysinit.target
new file mode 100644 (file)
index 0000000..b6c16a1
--- /dev/null
@@ -0,0 +1,15 @@
+#  SPDX-License-Identifier: LGPL-2.1+
+#
+#  This file is part of systemd.
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU Lesser General Public License as published by
+#  the Free Software Foundation; either version 2.1 of the License, or
+#  (at your option) any later version.
+
+[Unit]
+Description=System Initialization
+Documentation=man:systemd.special(7)
+Conflicts=emergency.service emergency.target
+Wants=local-fs.target swap.target
+After=local-fs.target swap.target emergency.service emergency.target
diff --git a/test/units/test-honor-first-shutdown.service b/test/units/test-honor-first-shutdown.service
new file mode 100644 (file)
index 0000000..3170f97
--- /dev/null
@@ -0,0 +1,11 @@
+[Unit]
+Description=Honor First Shutdown feature
+After=multi-user.target
+
+[Service]
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+ExecStop=sh -c 'kill -KILL $MAINPID'
+FailureAction=reboot
+
+[Install]
+WantedBy=multi-user.target
diff --git a/test/units/test-honor-first-shutdown.sh b/test/units/test-honor-first-shutdown.sh
new file mode 100755 (executable)
index 0000000..17c1ec9
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/bash
+echo "Honor first shutdown test script"
+sleep infinity;
diff --git a/test/units/testsuite-01.service b/test/units/testsuite-01.service
new file mode 100644 (file)
index 0000000..85b9cf5
--- /dev/null
@@ -0,0 +1,8 @@
+[Unit]
+Description=TEST-01-BASIC
+After=multi-user.target
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=sh -e -x -c 'systemctl --state=failed --no-legend --no-pager >/failed ; systemctl daemon-reload ; echo OK >/testok'
+Type=oneshot
diff --git a/test/units/testsuite-02.service b/test/units/testsuite-02.service
new file mode 100644 (file)
index 0000000..13e7ec3
--- /dev/null
@@ -0,0 +1,8 @@
+[Unit]
+Description=TEST-02-CRYPTSETUP
+After=multi-user.target
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=sh -x -e -c 'mountpoint /var; systemctl --state=failed --no-legend --no-pager >/failed; echo OK >/testok'
+Type=oneshot
diff --git a/test/units/testsuite-03.service b/test/units/testsuite-03.service
new file mode 100644 (file)
index 0000000..fe18fdc
--- /dev/null
@@ -0,0 +1,8 @@
+[Unit]
+Description=TEST-03-JOBS
+After=multi-user.target
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/testsuite-03.sh b/test/units/testsuite-03.sh
new file mode 100755 (executable)
index 0000000..85efeeb
--- /dev/null
@@ -0,0 +1,98 @@
+#!/usr/bin/env bash
+set -ex
+
+# Test merging of a --job-mode=ignore-dependencies job into a previously
+# installed job.
+
+systemctl start --no-block hello-after-sleep.target
+
+systemctl list-jobs > /root/list-jobs.txt
+while ! grep 'sleep\.service.*running' /root/list-jobs.txt; do
+    systemctl list-jobs > /root/list-jobs.txt
+done
+
+grep 'hello\.service.*waiting' /root/list-jobs.txt
+
+# This is supposed to finish quickly, not wait for sleep to finish.
+START_SEC=$(date -u '+%s')
+systemctl start --job-mode=ignore-dependencies hello
+END_SEC=$(date -u '+%s')
+ELAPSED=$(($END_SEC-$START_SEC))
+
+[ "$ELAPSED" -lt 3 ]
+
+# sleep should still be running, hello not.
+systemctl list-jobs > /root/list-jobs.txt
+grep 'sleep\.service.*running' /root/list-jobs.txt
+grep 'hello\.service' /root/list-jobs.txt && exit 1
+systemctl stop sleep.service hello-after-sleep.target
+
+# Some basic testing that --show-transaction does something useful
+! systemctl is-active systemd-importd
+systemctl -T start systemd-importd
+systemctl is-active systemd-importd
+systemctl --show-transaction stop systemd-importd
+! systemctl is-active systemd-importd
+
+# Test for a crash when enqueuing a JOB_NOP when other job already exists
+systemctl start --no-block hello-after-sleep.target
+# hello.service should still be waiting, so these try-restarts will collapse
+# into NOPs.
+systemctl try-restart --job-mode=fail hello.service
+systemctl try-restart hello.service
+systemctl stop hello.service sleep.service hello-after-sleep.target
+
+# TODO: add more job queueing/merging tests here.
+
+# Test for irreversible jobs
+systemctl start unstoppable.service
+
+# This is expected to fail with 'job cancelled'
+systemctl stop unstoppable.service && exit 1
+# But this should succeed
+systemctl stop --job-mode=replace-irreversibly unstoppable.service
+
+# We're going to shutdown soon. Let's see if it succeeds when
+# there's an active service that tries to be unstoppable.
+# Shutdown of the container/VM will hang if not.
+systemctl start unstoppable.service
+
+# Test waiting for a started unit(s) to terminate again
+cat <<EOF >  /run/systemd/system/wait2.service
+[Unit]
+Description=Wait for 2 seconds
+[Service]
+ExecStart=/bin/sh -ec 'sleep 2'
+EOF
+cat <<EOF >  /run/systemd/system/wait5fail.service
+[Unit]
+Description=Wait for 5 seconds and fail
+[Service]
+ExecStart=/bin/sh -ec 'sleep 5; false'
+EOF
+
+# wait2 succeeds
+START_SEC=$(date -u '+%s')
+systemctl start --wait wait2.service
+END_SEC=$(date -u '+%s')
+ELAPSED=$(($END_SEC-$START_SEC))
+[[ "$ELAPSED" -ge 2 ]] && [[ "$ELAPSED" -le 4 ]] || exit 1
+
+# wait5fail fails, so systemctl should fail
+START_SEC=$(date -u '+%s')
+! systemctl start --wait wait2.service wait5fail.service || exit 1
+END_SEC=$(date -u '+%s')
+ELAPSED=$(($END_SEC-$START_SEC))
+[[ "$ELAPSED" -ge 5 ]] && [[ "$ELAPSED" -le 7 ]] || exit 1
+
+# Test time-limited scopes
+START_SEC=$(date -u '+%s')
+set +e
+systemd-run --scope --property=RuntimeMaxSec=3s sleep 10
+RESULT=$?
+END_SEC=$(date -u '+%s')
+ELAPSED=$(($END_SEC-$START_SEC))
+[[ "$ELAPSED" -ge 3 ]] && [[ "$ELAPSED" -le 5 ]] || exit 1
+[[ "$RESULT" -ne 0 ]] || exit 1
+
+touch /testok
diff --git a/test/units/testsuite-04.service b/test/units/testsuite-04.service
new file mode 100644 (file)
index 0000000..3d2b4a8
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=TEST-04-JOURNAL
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/testsuite-04.sh b/test/units/testsuite-04.sh
new file mode 100755 (executable)
index 0000000..3dce73b
--- /dev/null
@@ -0,0 +1,124 @@
+#!/usr/bin/env bash
+set -x
+set -e
+set -o pipefail
+
+# Test stdout stream
+
+# Skip empty lines
+ID=$(journalctl --new-id128 | sed -n 2p)
+>/expected
+printf $'\n\n\n' | systemd-cat -t "$ID" --level-prefix false
+journalctl --sync
+journalctl -b -o cat -t "$ID" >/output
+cmp /expected /output
+
+ID=$(journalctl --new-id128 | sed -n 2p)
+>/expected
+printf $'<5>\n<6>\n<7>\n' | systemd-cat -t "$ID" --level-prefix true
+journalctl --sync
+journalctl -b -o cat -t "$ID" >/output
+cmp /expected /output
+
+# Remove trailing spaces
+ID=$(journalctl --new-id128 | sed -n 2p)
+printf "Trailing spaces\n">/expected
+printf $'<5>Trailing spaces \t \n' | systemd-cat -t "$ID" --level-prefix true
+journalctl --sync
+journalctl -b -o cat -t "$ID" >/output
+cmp /expected /output
+
+ID=$(journalctl --new-id128 | sed -n 2p)
+printf "Trailing spaces\n">/expected
+printf $'Trailing spaces \t \n' | systemd-cat -t "$ID" --level-prefix false
+journalctl --sync
+journalctl -b -o cat -t "$ID" >/output
+cmp /expected /output
+
+# Don't remove leading spaces
+ID=$(journalctl --new-id128 | sed -n 2p)
+printf $' \t Leading spaces\n'>/expected
+printf $'<5> \t Leading spaces\n' | systemd-cat -t "$ID" --level-prefix true
+journalctl --sync
+journalctl -b -o cat -t "$ID" >/output
+cmp /expected /output
+
+ID=$(journalctl --new-id128 | sed -n 2p)
+printf $' \t Leading spaces\n'>/expected
+printf $' \t Leading spaces\n' | systemd-cat -t "$ID" --level-prefix false
+journalctl --sync
+journalctl -b -o cat -t "$ID" >/output
+cmp /expected /output
+
+# --output-fields restricts output
+ID=$(journalctl --new-id128 | sed -n 2p)
+printf $'foo' | systemd-cat -t "$ID" --level-prefix false
+journalctl --sync
+journalctl -b -o export --output-fields=MESSAGE,FOO --output-fields=PRIORITY,MESSAGE -t "$ID" >/output
+[[ `grep -c . /output` -eq 6 ]]
+grep -q '^__CURSOR=' /output
+grep -q '^MESSAGE=foo$' /output
+grep -q '^PRIORITY=6$' /output
+! grep -q '^FOO=' /output
+! grep -q '^SYSLOG_FACILITY=' /output
+
+# `-b all` negates earlier use of -b (-b and -m are otherwise exclusive)
+journalctl -b -1 -b all -m > /dev/null
+
+# -b always behaves like -b0
+journalctl -q -b-1 -b0 | head -1 > /expected
+journalctl -q -b-1 -b  | head -1 > /output
+cmp /expected /output
+# ... even when another option follows (both of these should fail due to -m)
+{ journalctl -ball -b0 -m 2>&1 || :; } | head -1 > /expected
+{ journalctl -ball -b  -m 2>&1 || :; } | head -1 > /output
+cmp /expected /output
+
+# https://github.com/systemd/systemd/issues/13708
+ID=$(systemd-id128 new)
+systemd-cat -t "$ID" bash -c 'echo parent; (echo child) & wait' &
+PID=$!
+wait %%
+journalctl --sync
+# We can drop this grep when https://github.com/systemd/systemd/issues/13937
+# has a fix.
+journalctl -b -o export -t "$ID" --output-fields=_PID | grep '^_PID=' >/output
+[[ `grep -c . /output` -eq 2 ]]
+grep -q "^_PID=$PID" /output
+grep -vq "^_PID=$PID" /output
+
+# https://github.com/systemd/systemd/issues/15654
+ID=$(journalctl --new-id128 | sed -n 2p)
+printf "This will\nusually fail\nand be truncated\n">/expected
+systemd-cat -t "$ID" /bin/sh -c 'env echo -n "This will";echo;env echo -n "usually fail";echo;env echo -n "and be truncated";echo;'
+journalctl --sync
+journalctl -b -o cat -t "$ID" >/output
+cmp /expected /output
+[[ $(journalctl -b -o cat -t "$ID" --output-fields=_TRANSPORT | grep -Pc "^stdout$") -eq 3 ]]
+[[ $(journalctl -b -o cat -t "$ID" --output-fields=_LINE_BREAK | grep -Pc "^pid-change$") -eq 3 ]]
+[[ $(journalctl -b -o cat -t "$ID" --output-fields=_PID | sort -u | grep -c "^.*$") -eq 3 ]]
+[[ $(journalctl -b -o cat -t "$ID" --output-fields=MESSAGE | grep -Pc "^(This will|usually fail|and be truncated)$") -eq 3 ]]
+
+# Add new tests before here, the journald restarts below
+# may make tests flappy.
+
+# Don't lose streams on restart
+systemctl start forever-print-hola
+sleep 3
+systemctl restart systemd-journald
+sleep 3
+systemctl stop forever-print-hola
+[[ ! -f "/i-lose-my-logs" ]]
+
+# https://github.com/systemd/systemd/issues/4408
+rm -f /i-lose-my-logs
+systemctl start forever-print-hola
+sleep 3
+systemctl kill --signal=SIGKILL systemd-journald
+sleep 3
+[[ ! -f "/i-lose-my-logs" ]]
+
+# https://github.com/systemd/systemd/issues/15528
+journalctl --follow --file=/var/log/journal/*/* | head -n1 || [[ $? -eq 1 ]]
+
+touch /testok
diff --git a/test/units/testsuite-05.service b/test/units/testsuite-05.service
new file mode 100644 (file)
index 0000000..66356fd
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=TEST-05-RLIMITS
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/testsuite-05.sh b/test/units/testsuite-05.sh
new file mode 100755 (executable)
index 0000000..9168e72
--- /dev/null
@@ -0,0 +1,25 @@
+#!/usr/bin/env bash
+set -x
+set -e
+set -o pipefail
+
+P=/run/systemd/system.conf.d
+mkdir $P
+
+cat >$P/rlimits.conf <<EOF
+[Manager]
+DefaultLimitNOFILE=10000:16384
+EOF
+
+systemctl daemon-reload
+
+[[ "$(systemctl show -P DefaultLimitNOFILESoft)" = "10000" ]]
+[[ "$(systemctl show -P DefaultLimitNOFILE)" = "16384" ]]
+
+[[ "$(systemctl show -P LimitNOFILESoft testsuite-05.service)" = "10000" ]]
+[[ "$(systemctl show -P LimitNOFILE testsuite-05.service)" = "16384" ]]
+
+systemd-run --wait -t bash -c '[[ "$(ulimit -n -S)" = "10000" ]]'
+systemd-run --wait -t bash -c '[[ "$(ulimit -n -H)" = "16384" ]]'
+
+touch /testok
diff --git a/test/units/testsuite-06.service b/test/units/testsuite-06.service
new file mode 100644 (file)
index 0000000..3f8dad3
--- /dev/null
@@ -0,0 +1,10 @@
+[Unit]
+Description=TEST-06-SELINUX
+
+Requires=load-systemd-test-module.service
+After=load-systemd-test-module.service
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/testsuite-06.sh b/test/units/testsuite-06.sh
new file mode 100755 (executable)
index 0000000..f9b106d
--- /dev/null
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+set -x
+set -e
+set -o pipefail
+
+echo 1 >/sys/fs/selinux/enforce || {
+    echo "Can't make selinux enforcing, skipping test"
+    touch /testok
+    exit
+}
+
+runcon -t systemd_test_start_t systemctl start hola
+runcon -t systemd_test_reload_t systemctl reload hola
+runcon -t systemd_test_stop_t systemctl stop hola
+
+touch /testok
diff --git a/test/units/testsuite-07.service b/test/units/testsuite-07.service
new file mode 100644 (file)
index 0000000..2506c21
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=TEST-07-ISSUE-1981
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/testsuite-07.sh b/test/units/testsuite-07.sh
new file mode 100755 (executable)
index 0000000..fbb2d1d
--- /dev/null
@@ -0,0 +1,35 @@
+#!/usr/bin/env bash
+set -x
+set -e
+
+>/failed
+
+cat <<'EOL' >/lib/systemd/system/my.service
+[Service]
+Type=oneshot
+ExecStart=/bin/echo Timer runs me
+EOL
+
+cat <<'EOL' >/lib/systemd/system/my.timer
+[Timer]
+OnBootSec=10s
+OnUnitInactiveSec=1h
+EOL
+
+systemctl unmask my.timer
+
+systemctl start my.timer
+
+mkdir -p /etc/systemd/system/my.timer.d/
+cat <<'EOL' >/etc/systemd/system/my.timer.d/override.conf
+[Timer]
+OnBootSec=10s
+OnUnitInactiveSec=1h
+EOL
+
+systemctl daemon-reload
+
+systemctl mask my.timer
+
+touch /testok
+rm /failed
diff --git a/test/units/testsuite-08.service b/test/units/testsuite-08.service
new file mode 100644 (file)
index 0000000..d961dc7
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=TEST-08-ISSUE-2730
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=sh -x -c 'mount -o remount,rw /dev/sda1 && echo OK >/testok; systemctl poweroff'
+Type=oneshot
diff --git a/test/units/testsuite-09.service b/test/units/testsuite-09.service
new file mode 100644 (file)
index 0000000..fc59e80
--- /dev/null
@@ -0,0 +1,10 @@
+[Unit]
+Description=TEST-09-ISSUE-2691
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=sh -c '>/testok'
+ExecStop=sh -c 'kill -SEGV $$$$'
+Type=oneshot
+RemainAfterExit=yes
+TimeoutStopSec=270s
diff --git a/test/units/testsuite-10.service b/test/units/testsuite-10.service
new file mode 100644 (file)
index 0000000..24f0da3
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=TEST-10-ISSUE-2467
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+Type=oneshot
+ExecStart=sh -e -x -c 'rm -f /tmp/nonexistent; systemctl start test10.socket; printf x >test.file; socat -t20 OPEN:test.file UNIX-CONNECT:/run/test.ctl; >/testok'
diff --git a/test/units/testsuite-11.service b/test/units/testsuite-11.service
new file mode 100644 (file)
index 0000000..1544fd6
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=TEST-11-ISSUE-3166
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/testsuite-11.sh b/test/units/testsuite-11.sh
new file mode 100755 (executable)
index 0000000..708c7ce
--- /dev/null
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+set -x
+
+systemctl start fail-on-restart.service
+active_state=$(systemctl show --value --property ActiveState fail-on-restart.service)
+while [[ "$active_state" == "activating" || "$active_state" == "active" ]]; do
+    sleep 1
+    active_state=$(systemctl show --value --property ActiveState fail-on-restart.service)
+done
+systemctl is-failed fail-on-restart.service || exit 1
+touch /testok
diff --git a/test/units/testsuite-12.service b/test/units/testsuite-12.service
new file mode 100644 (file)
index 0000000..72894ef
--- /dev/null
@@ -0,0 +1,8 @@
+[Unit]
+Description=TEST-12-ISSUE-3171
+After=multi-user.target
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/testsuite-12.sh b/test/units/testsuite-12.sh
new file mode 100755 (executable)
index 0000000..b5888a2
--- /dev/null
@@ -0,0 +1,47 @@
+#!/usr/bin/env bash
+set -x
+set -e
+set -o pipefail
+
+U=/run/systemd/system/test12.socket
+cat <<'EOF' >$U
+[Unit]
+Description=Test 12 socket
+[Socket]
+Accept=yes
+ListenStream=/run/test12.socket
+SocketGroup=adm
+SocketMode=0660
+EOF
+
+cat <<'EOF' > /run/systemd/system/test12@.service
+[Unit]
+Description=Test service
+[Service]
+StandardInput=socket
+ExecStart=/bin/sh -x -c cat
+EOF
+
+systemctl start test12.socket
+systemctl is-active test12.socket
+[[ "$(stat --format='%G' /run/test12.socket)" == adm ]]
+echo A | nc -w1 -U /run/test12.socket
+
+mv $U ${U}.disabled
+systemctl daemon-reload
+systemctl is-active test12.socket
+[[ "$(stat --format='%G' /run/test12.socket)" == adm ]]
+echo B | nc -w1 -U /run/test12.socket && exit 1
+
+mv ${U}.disabled $U
+systemctl daemon-reload
+systemctl is-active test12.socket
+echo C | nc -w1 -U /run/test12.socket && exit 1
+[[ "$(stat --format='%G' /run/test12.socket)" == adm ]]
+
+systemctl restart test12.socket
+systemctl is-active test12.socket
+echo D | nc -w1 -U /run/test12.socket
+[[ "$(stat --format='%G' /run/test12.socket)" == adm ]]
+
+touch /testok
diff --git a/test/units/testsuite-13.service b/test/units/testsuite-13.service
new file mode 100644 (file)
index 0000000..5086793
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=TEST-13-NSPAWN-SMOKE
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/testsuite-13.sh b/test/units/testsuite-13.sh
new file mode 100755 (executable)
index 0000000..b8fbf00
--- /dev/null
@@ -0,0 +1,183 @@
+#!/usr/bin/env bash
+set -x
+set -e
+set -u
+set -o pipefail
+
+export SYSTEMD_LOG_LEVEL=debug
+
+# check cgroup-v2
+is_v2_supported=no
+mkdir -p /tmp/cgroup2
+if mount -t cgroup2 cgroup2 /tmp/cgroup2; then
+    is_v2_supported=yes
+    umount /tmp/cgroup2
+fi
+rmdir /tmp/cgroup2
+
+# check cgroup namespaces
+is_cgns_supported=no
+if [[ -f /proc/1/ns/cgroup ]]; then
+    is_cgns_supported=yes
+fi
+
+is_user_ns_supported=no
+# On some systems (e.g. CentOS 7) the default limit for user namespaces
+# is set to 0, which causes the following unshare syscall to fail, even
+# with enabled user namespaces support. By setting this value explicitly
+# we can ensure the user namespaces support to be detected correctly.
+sysctl -w user.max_user_namespaces=10000
+if unshare -U sh -c :; then
+    is_user_ns_supported=yes
+fi
+
+function check_bind_tmp_path {
+    # https://github.com/systemd/systemd/issues/4789
+    local _root="/var/lib/machines/testsuite-13.bind-tmp-path"
+    rm -rf "$_root"
+    /usr/lib/systemd/tests/testdata/create-busybox-container "$_root"
+    >/tmp/bind
+    systemd-nspawn --register=no -D "$_root" --bind=/tmp/bind /bin/sh -c 'test -e /tmp/bind'
+}
+
+function check_norbind {
+    # https://github.com/systemd/systemd/issues/13170
+    local _root="/var/lib/machines/testsuite-13.norbind-path"
+    rm -rf "$_root"
+    mkdir -p /tmp/binddir/subdir
+    echo -n "outer" > /tmp/binddir/subdir/file
+    mount -t tmpfs tmpfs /tmp/binddir/subdir
+    echo -n "inner" > /tmp/binddir/subdir/file
+    /usr/lib/systemd/tests/testdata/create-busybox-container "$_root"
+    systemd-nspawn --register=no -D "$_root" --bind=/tmp/binddir:/mnt:norbind /bin/sh -c 'CONTENT=$(cat /mnt/subdir/file); if [[ $CONTENT != "outer" ]]; then echo "*** unexpected content: $CONTENT"; return 1; fi'
+}
+
+function check_notification_socket {
+    # https://github.com/systemd/systemd/issues/4944
+    local _cmd='echo a | $(busybox which nc) -U -u -w 1 /run/systemd/nspawn/notify'
+    # /testsuite-13.nc-container is prepared by test.sh
+    systemd-nspawn --register=no -D /testsuite-13.nc-container /bin/sh -x -c "$_cmd"
+    systemd-nspawn --register=no -D /testsuite-13.nc-container -U /bin/sh -x -c "$_cmd"
+}
+
+function check_os_release {
+    local _cmd='. /tmp/os-release
+if [ -n "${ID:+set}" ] && [ "${ID}" != "${container_host_id}" ]; then exit 1; fi
+if [ -n "${VERSION_ID:+set}" ] && [ "${VERSION_ID}" != "${container_host_version_id}" ]; then exit 1; fi
+if [ -n "${BUILD_ID:+set}" ] && [ "${BUILD_ID}" != "${container_host_build_id}" ]; then exit 1; fi
+if [ -n "${VARIANT_ID:+set}" ] && [ "${VARIANT_ID}" != "${container_host_variant_id}" ]; then exit 1; fi
+cd /tmp; (cd /run/host; md5sum os-release) | md5sum -c
+if echo test >> /run/host/os-release; then exit 1; fi
+'
+
+    local _os_release_source="/etc/os-release"
+    if [ ! -r "${_os_release_source}" ]; then
+        _os_release_source="/usr/lib/os-release"
+    elif [ -L "${_os_release_source}" ] && rm /etc/os-release; then
+        # Ensure that /etc always wins if available
+        cp /usr/lib/os-release /etc
+        echo MARKER=1 >> /etc/os-release
+    fi
+
+    systemd-nspawn --register=no -D /testsuite-13.nc-container --bind="${_os_release_source}":/tmp/os-release /bin/sh -x -e -c "$_cmd"
+
+    if grep -q MARKER /etc/os-release; then
+        rm /etc/os-release
+        ln -s ../usr/lib/os-release /etc/os-release
+    fi
+}
+
+function run {
+    if [[ "$1" = "yes" && "$is_v2_supported" = "no" ]]; then
+        printf "Unified cgroup hierarchy is not supported. Skipping.\n" >&2
+        return 0
+    fi
+    if [[ "$2" = "yes" && "$is_cgns_supported" = "no" ]];  then
+        printf "CGroup namespaces are not supported. Skipping.\n" >&2
+        return 0
+    fi
+
+    local _root="/var/lib/machines/testsuite-13.unified-$1-cgns-$2-api-vfs-writable-$3"
+    rm -rf "$_root"
+    /usr/lib/systemd/tests/testdata/create-busybox-container "$_root"
+    SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" -b
+    SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" --private-network -b
+
+    if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" -U -b; then
+        [[ "$is_user_ns_supported" = "yes" && "$3" = "network" ]] && return 1
+    else
+        [[ "$is_user_ns_supported" = "no" && "$3" = "network" ]] && return 1
+    fi
+
+    if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" --private-network -U -b; then
+        [[ "$is_user_ns_supported" = "yes" && "$3" = "yes" ]] && return 1
+    else
+        [[ "$is_user_ns_supported" = "no" && "$3" = "yes" ]] && return 1
+    fi
+
+    local _netns_opt="--network-namespace-path=/proc/self/ns/net"
+
+    # --network-namespace-path and network-related options cannot be used together
+    if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-interface=lo -b; then
+        return 1
+    fi
+
+    if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-macvlan=lo -b; then
+        return 1
+    fi
+
+    if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-ipvlan=lo -b; then
+        return 1
+    fi
+
+    if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-veth -b; then
+        return 1
+    fi
+
+    if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-veth-extra=lo -b; then
+        return 1
+    fi
+
+    if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-bridge=lo -b; then
+        return 1
+    fi
+
+    if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --network-zone=zone -b; then
+        return 1
+    fi
+
+    # allow combination of --network-namespace-path and --private-network
+    if ! SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" --private-network -b; then
+        return 1
+    fi
+
+    # test --network-namespace-path works with a network namespace created by "ip netns"
+    ip netns add nspawn_test
+    _netns_opt="--network-namespace-path=/run/netns/nspawn_test"
+    SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$1" SYSTEMD_NSPAWN_USE_CGNS="$2" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$3" systemd-nspawn --register=no -D "$_root" "$_netns_opt" /bin/ip a | grep -v -E '^1: lo.*UP'
+    local r=$?
+    ip netns del nspawn_test
+
+    if [ $r -ne 0 ]; then
+        return 1
+    fi
+
+    return 0
+}
+
+check_bind_tmp_path
+
+check_norbind
+
+check_notification_socket
+
+check_os_release
+
+for api_vfs_writable in yes no network; do
+    run no no $api_vfs_writable
+    run yes no $api_vfs_writable
+    run no yes $api_vfs_writable
+    run yes yes $api_vfs_writable
+done
+
+touch /testok
diff --git a/test/units/testsuite-14.service b/test/units/testsuite-14.service
new file mode 100644 (file)
index 0000000..1606c68
--- /dev/null
@@ -0,0 +1,8 @@
+[Unit]
+Description=TEST-14-MACHINE-ID
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+ExecStart=/bin/sh -e -x -c 'systemctl --state=failed --no-legend --no-pager >/failed ; echo OK >/testok'
+Type=oneshot
diff --git a/test/units/testsuite-14.sh b/test/units/testsuite-14.sh
new file mode 100755 (executable)
index 0000000..95ac9b6
--- /dev/null
@@ -0,0 +1,38 @@
+#!/usr/bin/env bash
+set -e
+set -x
+
+function setup_root {
+    local _root="$1"
+    mkdir -p "$_root"
+    mount -t tmpfs tmpfs "$_root"
+    mkdir -p "$_root/etc" "$_root/run"
+}
+
+function check {
+    printf "Expected\n"
+    cat "$1"
+    printf "\nGot\n"
+    cat "$2"
+    cmp "$1" "$2"
+}
+
+r="$(pwd)/overwrite-broken-machine-id"
+setup_root "$r"
+systemd-machine-id-setup --print --root "$r"
+echo abc >>"$r/etc/machine-id"
+id=$(systemd-machine-id-setup --print --root "$r")
+echo $id >expected
+check expected "$r/etc/machine-id"
+
+r="$(pwd)/transient-machine-id"
+setup_root "$r"
+systemd-machine-id-setup --print --root "$r"
+echo abc >>"$r/etc/machine-id"
+mount -o remount,ro "$r"
+mount -t tmpfs tmpfs "$r/run"
+transient_id=$(systemd-machine-id-setup --print --root "$r")
+mount -o remount,rw "$r"
+commited_id=$(systemd-machine-id-setup --print --commit --root "$r")
+[[ "$transient_id" = "$commited_id" ]]
+check "$r/etc/machine-id" "$r/run/machine-id"
diff --git a/test/units/testsuite-15.service b/test/units/testsuite-15.service
new file mode 100644 (file)
index 0000000..09571ed
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=TEST-15-DROPIN
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/testsuite-15.sh b/test/units/testsuite-15.sh
new file mode 100755 (executable)
index 0000000..b872a24
--- /dev/null
@@ -0,0 +1,474 @@
+#! /bin/bash
+set -e
+set -x
+
+_clear_service () {
+    systemctl stop $1.service 2>/dev/null || :
+    rm -f  /{etc,run,usr/lib}/systemd/system/$1.service
+    rm -fr /{etc,run,usr/lib}/systemd/system/$1.service.d
+    rm -fr /{etc,run,usr/lib}/systemd/system/$1.service.{wants,requires}
+    if [[ $1 == *@ ]]; then
+        systemctl stop $1*.service 2>/dev/null || :
+        rm -f  /{etc,run,usr/lib}/systemd/system/$1*.service
+        rm -fr /{etc,run,usr/lib}/systemd/system/$1*.service.d
+        rm -fr /{etc,run,usr/lib}/systemd/system/$1*.service.{wants,requires}
+    fi
+}
+
+clear_services () {
+    for u in $*; do
+        _clear_service $u
+    done
+    systemctl daemon-reload
+}
+
+create_service () {
+    clear_services $1
+
+    cat >/etc/systemd/system/$1.service<<EOF
+[Unit]
+Description=$1 unit
+
+[Service]
+ExecStart=/bin/sleep 100000
+EOF
+    mkdir -p /{etc,run,usr/lib}/systemd/system/$1.service.d
+    mkdir -p /etc/systemd/system/$1.service.{wants,requires}
+    mkdir -p /run/systemd/system/$1.service.{wants,requires}
+    mkdir -p /usr/lib/systemd/system/$1.service.{wants,requires}
+}
+
+create_services () {
+    for u in $*; do
+        create_service $u
+    done
+}
+
+check_ok () {
+    [ $# -eq 3 ] || return
+
+    x="$(systemctl show --value -p $2 $1)"
+    case "$x" in
+        *$3*) return 0 ;;
+        *)    return 1 ;;
+    esac
+}
+
+check_ko () {
+    ! check_ok "$@"
+}
+
+test_basic_dropins () {
+    echo "Testing basic dropins..."
+
+    echo "*** test a wants b wants c"
+    create_services test15-a test15-b test15-c
+    ln -s ../test15-b.service /etc/systemd/system/test15-a.service.wants/
+    ln -s ../test15-c.service /etc/systemd/system/test15-b.service.wants/
+    check_ok test15-a Wants test15-b.service
+    check_ok test15-b Wants test15-c.service
+
+    echo "*** test a wants,requires b"
+    create_services test15-a test15-b test15-c
+    ln -s ../test15-b.service /etc/systemd/system/test15-a.service.wants/
+    ln -s ../test15-b.service /etc/systemd/system/test15-a.service.requires/
+    check_ok test15-a Wants test15-b.service
+    check_ok test15-a Requires test15-b.service
+
+    echo "*** test a wants nonexistent"
+    create_service test15-a
+    ln -s ../nonexistent.service /etc/systemd/system/test15-a.service.wants/
+    check_ok test15-a Wants nonexistent.service
+    systemctl start test15-a
+    systemctl stop  test15-a
+
+    echo "*** test a requires nonexistent"
+    ln -sf ../nonexistent.service /etc/systemd/system/test15-a.service.requires/
+    systemctl daemon-reload
+    check_ok test15-a Requires nonexistent.service
+
+    # 'b' is already loaded when 'c' pulls it in via a dropin.
+    echo "*** test a,c require b"
+    create_services test15-a test15-b test15-c
+    ln -sf ../test15-b.service /etc/systemd/system/test15-a.service.requires/
+    ln -sf ../test15-b.service /etc/systemd/system/test15-c.service.requires/
+    systemctl start test15-a
+    check_ok test15-c Requires test15-b.service
+    systemctl stop test15-a test15-b
+
+    # 'b'  is already loaded when 'c' pulls it in via an alias dropin.
+    echo "*** test a wants alias"
+    create_services test15-a test15-b test15-c
+    ln -sf test15-c.service /etc/systemd/system/test15-c1.service
+    ln -sf ../test15-c.service  /etc/systemd/system/test15-a.service.wants/
+    ln -sf ../test15-c1.service /etc/systemd/system/test15-b.service.wants/
+    systemctl start test15-a
+    check_ok test15-a Wants test15-c.service
+    check_ok test15-b Wants test15-c.service
+    systemctl stop test15-a test15-c
+
+    echo "*** test service.d/ top level drop-in"
+    create_services test15-a test15-b
+    check_ko test15-a ExecCondition "/bin/echo a"
+    check_ko test15-b ExecCondition "/bin/echo b"
+    mkdir -p /usr/lib/systemd/system/service.d
+    cat >/usr/lib/systemd/system/service.d/override.conf <<EOF
+[Service]
+ExecCondition=/bin/echo %n
+EOF
+    systemctl daemon-reload
+    check_ok test15-a ExecCondition "/bin/echo test15-a"
+    check_ok test15-b ExecCondition "/bin/echo test15-b"
+    rm -rf /usr/lib/systemd/system/service.d
+
+    clear_services test15-a test15-b test15-c
+}
+
+test_hierarchical_dropins () {
+    echo "Testing hierarchical dropins..."
+    echo "*** test service.d/ top level drop-in"
+    create_services a-b-c
+    check_ko a-b-c ExecCondition "/bin/echo service.d"
+    check_ko a-b-c ExecCondition "/bin/echo a-.service.d"
+    check_ko a-b-c ExecCondition "/bin/echo a-b-.service.d"
+    check_ko a-b-c ExecCondition "/bin/echo a-b-c.service.d"
+
+    for dropin in service.d a-.service.d a-b-.service.d a-b-c.service.d; do
+        mkdir -p /usr/lib/systemd/system/$dropin
+        echo "
+[Service]
+ExecCondition=/bin/echo $dropin
+        " > /usr/lib/systemd/system/$dropin/override.conf
+        systemctl daemon-reload
+        check_ok a-b-c ExecCondition "/bin/echo $dropin"
+    done
+    for dropin in service.d a-.service.d a-b-.service.d a-b-c.service.d; do
+        rm -rf /usr/lib/systemd/system/$dropin
+    done
+
+    clear_services a-b-c
+}
+
+test_template_dropins () {
+    echo "Testing template dropins..."
+
+    create_services foo bar@ yup@
+
+    # Declare some deps to check if the body was loaded
+    cat >>/etc/systemd/system/bar@.service <<EOF
+[Unit]
+After=bar-template-after.device
+EOF
+
+    cat >>/etc/systemd/system/yup@.service <<EOF
+[Unit]
+After=yup-template-after.device
+EOF
+
+    ln -s /etc/systemd/system/bar@.service /etc/systemd/system/foo.service.wants/bar@1.service
+    check_ok foo Wants bar@1.service
+
+    echo "*** test bar-alias@.service→bar@.service, but instance symlinks point to yup@.service ***"
+    ln -s bar@.service  /etc/systemd/system/bar-alias@.service
+    ln -s bar@1.service /etc/systemd/system/bar-alias@1.service
+    ln -s yup@.service  /etc/systemd/system/bar-alias@2.service
+    ln -s yup@3.service /etc/systemd/system/bar-alias@3.service
+
+    # create some dropin deps
+    mkdir -p /etc/systemd/system/bar@{,0,1,2,3}.service.requires/
+    mkdir -p /etc/systemd/system/yup@{,0,1,2,3}.service.requires/
+    mkdir -p /etc/systemd/system/bar-alias@{,0,1,2,3}.service.requires/
+
+    ln -s ../bar-template-requires.device /etc/systemd/system/bar@.service.requires/
+    ln -s ../bar-0-requires.device /etc/systemd/system/bar@0.service.requires/
+    ln -s ../bar-1-requires.device /etc/systemd/system/bar@1.service.requires/
+    ln -s ../bar-2-requires.device /etc/systemd/system/bar@2.service.requires/
+    ln -s ../bar-3-requires.device /etc/systemd/system/bar@3.service.requires/
+
+    ln -s ../yup-template-requires.device /etc/systemd/system/yup@.service.requires/
+    ln -s ../yup-0-requires.device /etc/systemd/system/yup@0.service.requires/
+    ln -s ../yup-1-requires.device /etc/systemd/system/yup@1.service.requires/
+    ln -s ../yup-2-requires.device /etc/systemd/system/yup@2.service.requires/
+    ln -s ../yup-3-requires.device /etc/systemd/system/yup@3.service.requires/
+
+    ln -s ../bar-alias-template-requires.device /etc/systemd/system/bar-alias@.service.requires/
+    ln -s ../bar-alias-0-requires.device /etc/systemd/system/bar-alias@0.service.requires/
+    ln -s ../bar-alias-1-requires.device /etc/systemd/system/bar-alias@1.service.requires/
+    ln -s ../bar-alias-2-requires.device /etc/systemd/system/bar-alias@2.service.requires/
+    ln -s ../bar-alias-3-requires.device /etc/systemd/system/bar-alias@3.service.requires/
+
+    systemctl daemon-reload
+
+    echo '*** bar@0 is aliased by bar-alias@0 ***'
+    systemctl show -p Names,Requires bar@0
+    systemctl show -p Names,Requires bar-alias@0
+    check_ok bar@0 Names bar@0
+    check_ok bar@0 Names bar-alias@0
+
+    check_ok bar@0 After bar-template-after.device
+
+    check_ok bar@0 Requires bar-0-requires.device
+    check_ok bar@0 Requires bar-alias-0-requires.device
+    check_ok bar@0 Requires bar-template-requires.device
+    check_ok bar@0 Requires bar-alias-template-requires.device
+    check_ko bar@0 Requires yup-template-requires.device
+
+    check_ok bar-alias@0 After bar-template-after.device
+
+    check_ok bar-alias@0 Requires bar-0-requires.device
+    check_ok bar-alias@0 Requires bar-alias-0-requires.device
+    check_ok bar-alias@0 Requires bar-template-requires.device
+    check_ok bar-alias@0 Requires bar-alias-template-requires.device
+    check_ko bar-alias@0 Requires yup-template-requires.device
+    check_ko bar-alias@0 Requires yup-0-requires.device
+
+    echo '*** bar@1 is aliased by bar-alias@1 ***'
+    systemctl show -p Names,Requires bar@1
+    systemctl show -p Names,Requires bar-alias@1
+    check_ok bar@1 Names bar@1
+    check_ok bar@1 Names bar-alias@1
+
+    check_ok bar@1 After bar-template-after.device
+
+    check_ok bar@1 Requires bar-1-requires.device
+    check_ok bar@1 Requires bar-alias-1-requires.device
+    check_ok bar@1 Requires bar-template-requires.device
+    # See https://github.com/systemd/systemd/pull/13119#discussion_r308145418
+    check_ok bar@1 Requires bar-alias-template-requires.device
+    check_ko bar@1 Requires yup-template-requires.device
+    check_ko bar@1 Requires yup-1-requires.device
+
+    check_ok bar-alias@1 After bar-template-after.device
+
+    check_ok bar-alias@1 Requires bar-1-requires.device
+    check_ok bar-alias@1 Requires bar-alias-1-requires.device
+    check_ok bar-alias@1 Requires bar-template-requires.device
+    check_ok bar-alias@1 Requires bar-alias-template-requires.device
+    check_ko bar-alias@1 Requires yup-template-requires.device
+    check_ko bar-alias@1 Requires yup-1-requires.device
+
+    echo '*** bar-alias@2 aliases yup@2, bar@2 is independent ***'
+    systemctl show -p Names,Requires bar@2
+    systemctl show -p Names,Requires bar-alias@2
+    check_ok bar@2 Names bar@2
+    check_ko bar@2 Names bar-alias@2
+
+    check_ok bar@2 After bar-template-after.device
+
+    check_ok bar@2 Requires bar-2-requires.device
+    check_ko bar@2 Requires bar-alias-2-requires.device
+    check_ok bar@2 Requires bar-template-requires.device
+    check_ko bar@2 Requires bar-alias-template-requires.device
+    check_ko bar@2 Requires yup-template-requires.device
+    check_ko bar@2 Requires yup-2-requires.device
+
+    check_ko bar-alias@2 After bar-template-after.device
+
+    check_ko bar-alias@2 Requires bar-2-requires.device
+    check_ok bar-alias@2 Requires bar-alias-2-requires.device
+    check_ko bar-alias@2 Requires bar-template-requires.device
+    check_ok bar-alias@2 Requires bar-alias-template-requires.device
+    check_ok bar-alias@2 Requires yup-template-requires.device
+    check_ok bar-alias@2 Requires yup-2-requires.device
+
+    echo '*** bar-alias@3 aliases yup@3, bar@3 is independent ***'
+    systemctl show -p Names,Requires bar@3
+    systemctl show -p Names,Requires bar-alias@3
+    check_ok bar@3 Names bar@3
+    check_ko bar@3 Names bar-alias@3
+
+    check_ok bar@3 After bar-template-after.device
+
+    check_ok bar@3 Requires bar-3-requires.device
+    check_ko bar@3 Requires bar-alias-3-requires.device
+    check_ok bar@3 Requires bar-template-requires.device
+    check_ko bar@3 Requires bar-alias-template-requires.device
+    check_ko bar@3 Requires yup-template-requires.device
+    check_ko bar@3 Requires yup-3-requires.device
+
+    check_ko bar-alias@3 After bar-template-after.device
+
+    check_ko bar-alias@3 Requires bar-3-requires.device
+    check_ok bar-alias@3 Requires bar-alias-3-requires.device
+    check_ko bar-alias@3 Requires bar-template-requires.device
+    check_ok bar-alias@3 Requires bar-alias-template-requires.device
+    check_ok bar-alias@3 Requires yup-template-requires.device
+    check_ok bar-alias@3 Requires yup-3-requires.device
+
+    clear_services foo {bar,yup,bar-alias}@{,1,2,3}
+}
+
+test_alias_dropins () {
+    echo "Testing alias dropins..."
+
+    echo "*** test a wants b1 alias of b"
+    create_services test15-a test15-b
+    ln -sf test15-b.service /etc/systemd/system/test15-b1.service
+    ln -sf ../test15-b1.service /etc/systemd/system/test15-a.service.wants/
+    check_ok test15-a Wants test15-b.service
+    systemctl start test15-a
+    systemctl --quiet is-active test15-b
+    systemctl stop test15-a test15-b
+    rm /etc/systemd/system/test15-b1.service
+    clear_services test15-a test15-b
+
+    # Check that dependencies don't vary.
+    echo "*** test 2"
+    create_services test15-a test15-x test15-y
+    mkdir -p /etc/systemd/system/test15-a1.service.wants/
+    ln -sf test15-a.service /etc/systemd/system/test15-a1.service
+    ln -sf ../test15-x.service /etc/systemd/system/test15-a.service.wants/
+    ln -sf ../test15-y.service /etc/systemd/system/test15-a1.service.wants/
+    check_ok test15-a1 Wants test15-x.service # see [1]
+    check_ok test15-a1 Wants test15-y.service
+    systemctl start test15-a
+    check_ok test15-a1 Wants test15-x.service # see [2]
+    check_ok test15-a1 Wants test15-y.service
+    systemctl stop test15-a test15-x test15-y
+    rm /etc/systemd/system/test15-a1.service
+
+    clear_services test15-a test15-x test15-y
+}
+
+test_masked_dropins () {
+    echo "Testing masked dropins..."
+
+    create_services test15-a test15-b
+
+    # 'b' is masked for both deps
+    echo "*** test a wants,requires b is masked"
+    ln -sf /dev/null /etc/systemd/system/test15-a.service.wants/test15-b.service
+    ln -sf /dev/null /etc/systemd/system/test15-a.service.requires/test15-b.service
+    check_ko test15-a Wants test15-b.service
+    check_ko test15-a Requires test15-b.service
+
+    # 'a' wants 'b' and 'b' is masked at a lower level
+    echo "*** test a wants b, mask override"
+    ln -sf ../test15-b.service /etc/systemd/system/test15-a.service.wants/test15-b.service
+    ln -sf /dev/null /usr/lib/systemd/system/test15-a.service.wants/test15-b.service
+    check_ok test15-a Wants test15-b.service
+
+    # 'a' wants 'b' and 'b' is masked at a higher level
+    echo "*** test a wants b, mask"
+    ln -sf /dev/null /etc/systemd/system/test15-a.service.wants/test15-b.service
+    ln -sf ../test15-b.service /usr/lib/systemd/system/test15-a.service.wants/test15-b.service
+    check_ko test15-a Wants test15-b.service
+
+    # 'a' is masked but has an override config file
+    echo "*** test a is masked but has an override"
+    create_services test15-a test15-b
+    ln -sf /dev/null /etc/systemd/system/test15-a.service
+    cat >/usr/lib/systemd/system/test15-a.service.d/override.conf <<EOF
+[Unit]
+After=test15-b.service
+EOF
+    check_ok test15-a UnitFileState masked
+
+    # 'b1' is an alias for 'b': masking 'b' dep should not influence 'b1' dep
+    echo "*** test a wants b, b1, and one is masked"
+    create_services test15-a test15-b
+    ln -sf test15-b.service /etc/systemd/system/test15-b1.service
+    ln -sf /dev/null /etc/systemd/system/test15-a.service.wants/test15-b.service
+    ln -sf ../test15-b1.service /usr/lib/systemd/system/test15-a.service.wants/test15-b1.service
+    systemctl cat test15-a
+    systemctl show -p Wants,Requires test15-a
+    systemctl cat test15-b1
+    systemctl show -p Wants,Requires test15-b1
+    check_ok test15-a Wants test15-b.service
+    check_ko test15-a Wants test15-b1.service # the alias does not show up in the list of units
+    rm /etc/systemd/system/test15-b1.service
+
+    # 'b1' is an alias for 'b': masking 'b1' should not influence 'b' dep
+    echo "*** test a wants b, alias dep is masked"
+    create_services test15-a test15-b
+    ln -sf test15-b.service /etc/systemd/system/test15-b1.service
+    ln -sf /dev/null /etc/systemd/system/test15-a.service.wants/test15-b1.service
+    ln -sf ../test15-b.service /usr/lib/systemd/system/test15-a.service.wants/test15-b.service
+    check_ok test15-a Wants test15-b.service
+    check_ko test15-a Wants test15-b1.service # the alias does not show up in the list of units
+    rm /etc/systemd/system/test15-b1.service
+
+    # 'a' has Wants=b.service but also has a masking
+    # dropin 'b': 'b' should still be pulled in.
+    echo "*** test a wants b both ways"
+    create_services test15-a test15-b
+    ln -sf /dev/null /etc/systemd/system/test15-a.service.wants/test15-b.service
+    cat >/usr/lib/systemd/system/test15-a.service.d/wants-b.conf<<EOF
+[Unit]
+Wants=test15-b.service
+EOF
+    check_ok test15-a Wants test15-b.service
+
+    # mask a dropin that points to an nonexistent unit.
+    echo "*** test a wants nonexistent is masked"
+    create_services test15-a
+    ln -sf /dev/null /etc/systemd/system/test15-a.service.requires/nonexistent.service
+    ln -sf ../nonexistent.service /usr/lib/systemd/system/test15-a.service.requires/
+    check_ko test15-a Requires nonexistent.service
+
+    # 'b' is already loaded when 'c' pulls it in via a dropin but 'b' is
+    # masked at a higher level.
+    echo "*** test a wants b is masked"
+    create_services test15-a test15-b test15-c
+    ln -sf ../test15-b.service /etc/systemd/system/test15-a.service.requires/
+    ln -sf ../test15-b.service /run/systemd/system/test15-c.service.requires/
+    ln -sf /dev/null /etc/systemd/system/test15-c.service.requires/test15-b.service
+    systemctl start test15-a
+    check_ko test15-c Requires test15-b.service
+    systemctl stop test15-a test15-b
+
+    # 'b' is already loaded when 'c' pulls it in via a dropin but 'b' is
+    # masked at a lower level.
+    echo "*** test a requires b is masked"
+    create_services test15-a test15-b test15-c
+    ln -sf ../test15-b.service /etc/systemd/system/test15-a.service.requires/
+    ln -sf ../test15-b.service /etc/systemd/system/test15-c.service.requires/
+    ln -sf /dev/null /run/systemd/system/test15-c.service.requires/test15-b.service
+    systemctl start test15-a
+    check_ok test15-c Requires test15-b.service
+    systemctl stop test15-a test15-b
+
+    # 'a' requires 2 aliases of 'b' and one of them is a mask.
+    echo "*** test a requires alias of b, other alias masked"
+    create_services test15-a test15-b
+    ln -sf test15-b.service /etc/systemd/system/test15-b1.service
+    ln -sf test15-b.service /etc/systemd/system/test15-b2.service
+    ln -sf /dev/null /etc/systemd/system/test15-a.service.requires/test15-b1.service
+    ln -sf ../test15-b1.service /run/systemd/system/test15-a.service.requires/
+    ln -sf ../test15-b2.service /usr/lib/systemd/system/test15-a.service.requires/
+    check_ok test15-a Requires test15-b
+
+    # Same as above but now 'b' is masked.
+    echo "*** test a requires alias of b, b dep masked"
+    create_services test15-a test15-b
+    ln -sf test15-b.service /etc/systemd/system/test15-b1.service
+    ln -sf test15-b.service /etc/systemd/system/test15-b2.service
+    ln -sf ../test15-b1.service /run/systemd/system/test15-a.service.requires/
+    ln -sf ../test15-b2.service /usr/lib/systemd/system/test15-a.service.requires/
+    ln -sf /dev/null /etc/systemd/system/test15-a.service.requires/test15-b.service
+    check_ok test15-a Requires test15-b
+
+    clear_services test15-a test15-b
+}
+
+test_invalid_dropins () {
+    echo "Testing invalid dropins..."
+    # Assertion failed on earlier versions, command exits unsuccessfully on later versions
+    systemctl cat nonexistent@.service || true
+    create_services a
+    systemctl daemon-reload
+    # Assertion failed on earlier versions, command exits unsuccessfully on later versions
+    systemctl cat a@.service || true
+    systemctl stop a
+    clear_services a
+    return 0
+}
+
+test_basic_dropins
+test_hierarchical_dropins
+test_template_dropins
+test_alias_dropins
+test_masked_dropins
+test_invalid_dropins
+
+touch /testok
diff --git a/test/units/testsuite-16.service b/test/units/testsuite-16.service
new file mode 100644 (file)
index 0000000..b44baad
--- /dev/null
@@ -0,0 +1,19 @@
+[Unit]
+Description=TEST-16-EXTEND-TIMEOUT
+# Testsuite: Assess all other testsuite-*.services worked as expected
+
+Wants=success-all.service
+Wants=success-start.service
+Wants=success-runtime.service
+Wants=success-stop.service
+Wants=fail-start.service
+Wants=fail-stop.service
+Wants=fail-runtime.service
+StopWhenUnneeded=yes
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+Type=simple
+TimeoutStartSec=infinity
+ExecStartPre=/usr/lib/systemd/tests/testdata/units/%N.sh
+ExecStart=true
diff --git a/test/units/testsuite-16.sh b/test/units/testsuite-16.sh
new file mode 100755 (executable)
index 0000000..68e5561
--- /dev/null
@@ -0,0 +1,55 @@
+#!/usr/bin/env bash
+set -v -x
+
+rm -f /test.log
+
+TL=/test.log.XXXXXXXX
+
+function wait_for()
+{
+    service=${1}
+    result=${2:-success}
+    time=${3:-45}
+
+    while [[ ! -f /${service}.terminated && ! -f /${service}.success && $time -gt 0  ]]
+    do
+        sleep 1
+        time=$(( $time - 1 ))
+    done
+
+    if [[ ! -f /${service}.${result} ]]
+    then
+        journalctl -u ${service/_/-}.service >> "${TL}"
+    fi
+}
+
+# This checks all stages, start, runtime and stop, can be extended by
+# EXTEND_TIMEOUT_USEC
+
+wait_for success_all
+
+# These check that EXTEND_TIMEOUT_USEC that occurs at greater than the
+# extend timeout interval but less then the stage limit (TimeoutStartSec,
+# RuntimeMaxSec, TimeoutStopSec) still succeed.
+
+wait_for success_start
+wait_for success_runtime
+wait_for success_stop
+
+# These ensure that EXTEND_TIMEOUT_USEC will still timeout in the
+# approprate stage, after the stage limit, when the EXTEND_TIMEOUT_USEC
+# message isn't sent within the extend timeout interval.
+
+wait_for fail_start startfail
+wait_for fail_stop stopfail
+wait_for fail_runtime runtimefail
+
+if [[ -f "${TL}" ]]
+then
+    # no mv
+    cp "${TL}" /test.log
+    exit 1
+else
+    touch /testok
+    exit 0
+fi
diff --git a/test/units/testsuite-17.service b/test/units/testsuite-17.service
new file mode 100644 (file)
index 0000000..ed2017a
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=TEST-17-UDEV-WANTS
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/testsuite-17.sh b/test/units/testsuite-17.sh
new file mode 100755 (executable)
index 0000000..7f8740b
--- /dev/null
@@ -0,0 +1,74 @@
+#!/usr/bin/env bash
+set -ex
+set -o pipefail
+
+mkdir -p /run/udev/rules.d/
+
+rm -f /run/udev/rules.d/50-testsuite.rules
+udevadm control --reload
+udevadm trigger /dev/sda
+
+while : ; do
+    (
+        udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=foobar.service
+        udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=waldo.service
+        systemctl show -p WantedBy foobar.service | grep -q -v sda
+        systemctl show -p WantedBy waldo.service | grep -q -v sda
+    ) && break
+
+    sleep .5
+done
+
+cat > /run/udev/rules.d/50-testsuite.rules <<EOF
+ACTION!="remove", SUBSYSTEM=="block", KERNEL=="sda", ENV{SYSTEMD_WANTS}="foobar.service"
+EOF
+udevadm control --reload
+udevadm trigger /dev/sda
+
+while : ; do
+    (
+        udevadm info /dev/sda | grep -q SYSTEMD_WANTS=foobar.service
+        udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=waldo.service
+        systemctl show -p WantedBy foobar.service | grep -q sda
+        systemctl show -p WantedBy waldo.service | grep -q -v sda
+    ) && break
+
+    sleep .5
+done
+
+cat > /run/udev/rules.d/50-testsuite.rules <<EOF
+ACTION!="remove", SUBSYSTEM=="block", KERNEL=="sda", ENV{SYSTEMD_WANTS}="waldo.service"
+EOF
+udevadm control --reload
+udevadm trigger /dev/sda
+
+while : ; do
+    (
+        udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=foobar.service
+        udevadm info /dev/sda | grep -q SYSTEMD_WANTS=waldo.service
+        systemctl show -p WantedBy foobar.service | grep -q -v sda
+        systemctl show -p WantedBy waldo.service | grep -q sda
+    ) && break
+
+    sleep .5
+done
+
+rm /run/udev/rules.d/50-testsuite.rules
+
+udevadm control --reload
+udevadm trigger /dev/sda
+
+while : ; do
+    (
+        udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=foobar.service
+        udevadm info /dev/sda | grep -q -v SYSTEMD_WANTS=waldo.service
+        systemctl show -p WantedBy foobar.service | grep -q -v sda
+        systemctl show -p WantedBy waldo.service | grep -q -v sda
+    ) && break
+
+    sleep .5
+done
+
+echo OK >/testok
+
+exit 0
diff --git a/test/units/testsuite-18.service b/test/units/testsuite-18.service
new file mode 100644 (file)
index 0000000..e4a945d
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=TEST-18-FAILUREACTION
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/testsuite-18.sh b/test/units/testsuite-18.sh
new file mode 100755 (executable)
index 0000000..e471cda
--- /dev/null
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+set -ex
+set -o pipefail
+
+systemd-run --wait -p FailureAction=poweroff true
+! systemd-run --wait -p SuccessAction=poweroff false
+
+if ! test -f /firstphase ; then
+    echo OK > /firstphase
+    systemd-run --wait -p SuccessAction=reboot true
+else
+    echo OK > /testok
+    systemd-run --wait -p FailureAction=poweroff false
+fi
+
+sleep infinity
diff --git a/test/units/testsuite-19.service b/test/units/testsuite-19.service
new file mode 100644 (file)
index 0000000..d6ad5be
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=TEST-19-DELEGATE
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/testsuite-19.sh b/test/units/testsuite-19.sh
new file mode 100755 (executable)
index 0000000..57831c2
--- /dev/null
@@ -0,0 +1,39 @@
+#!/usr/bin/env bash
+set -ex
+set -o pipefail
+
+if grep -q cgroup2 /proc/filesystems ; then
+    systemd-run --wait --unit=test0.service -p "DynamicUser=1" -p "Delegate=" \
+                test -w /sys/fs/cgroup/system.slice/test0.service/ -a \
+                -w /sys/fs/cgroup/system.slice/test0.service/cgroup.procs -a \
+                -w /sys/fs/cgroup/system.slice/test0.service/cgroup.subtree_control
+
+    systemd-run --wait --unit=test1.service -p "DynamicUser=1" -p "Delegate=memory pids" \
+                grep -q memory /sys/fs/cgroup/system.slice/test1.service/cgroup.controllers
+
+    systemd-run --wait --unit=test2.service -p "DynamicUser=1" -p "Delegate=memory pids" \
+                grep -q pids /sys/fs/cgroup/system.slice/test2.service/cgroup.controllers
+
+    # "io" is not among the controllers enabled by default for all units, verify that
+    grep -qv io /sys/fs/cgroup/system.slice/cgroup.controllers
+
+    # Run a service with "io" enabled, and verify it works
+    systemd-run --wait --unit=test3.service -p "IOAccounting=yes" -p "Slice=system-foo-bar-baz.slice"  \
+                grep -q io /sys/fs/cgroup/system.slice/system-foo.slice/system-foo-bar.slice/system-foo-bar-baz.slice/test3.service/cgroup.controllers
+
+    # We want to check if "io" is removed again from the controllers
+    # list. However, PID 1 (rightfully) does this asynchronously. In order
+    # to force synchronization on this, let's start a short-lived service
+    # which requires PID 1 to refresh the cgroup tree, so that we can
+    # verify that this all works.
+    systemd-run --wait --unit=test4.service true
+
+    # And now check again, "io" should have vanished
+    grep -qv io /sys/fs/cgroup/system.slice/cgroup.controllers
+else
+    echo "Skipping TEST-19-DELEGATE, as the kernel doesn't actually support cgroup v2" >&2
+fi
+
+echo OK > /testok
+
+exit 0
diff --git a/test/units/testsuite-20.service b/test/units/testsuite-20.service
new file mode 100644 (file)
index 0000000..d31d531
--- /dev/null
@@ -0,0 +1,10 @@
+[Unit]
+Description=TEST-20-MAINPIDGAMES
+Before=getty-pre.target
+Wants=getty-pre.target
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
+NotifyAccess=all
diff --git a/test/units/testsuite-20.sh b/test/units/testsuite-20.sh
new file mode 100755 (executable)
index 0000000..d94f6b2
--- /dev/null
@@ -0,0 +1,139 @@
+#!/usr/bin/env bash
+set -ex
+set -o pipefail
+
+systemd-analyze log-level debug
+systemd-analyze log-target console
+
+test `systemctl show -P MainPID testsuite-20.service` -eq $$
+
+# Start a test process inside of our own cgroup
+sleep infinity &
+INTERNALPID=$!
+disown
+
+# Start a test process outside of our own cgroup
+systemd-run -p DynamicUser=1 --unit=test20-sleep.service /bin/sleep infinity
+EXTERNALPID=`systemctl show -P MainPID test20-sleep.service`
+
+# Update our own main PID to the external test PID, this should work
+systemd-notify MAINPID=$EXTERNALPID
+test `systemctl show -P MainPID testsuite-20.service` -eq $EXTERNALPID
+
+# Update our own main PID to the internal test PID, this should work, too
+systemd-notify MAINPID=$INTERNALPID
+test `systemctl show -P MainPID testsuite-20.service` -eq $INTERNALPID
+
+# Update it back to our own PID, this should also work
+systemd-notify MAINPID=$$
+test `systemctl show -P MainPID testsuite-20.service` -eq $$
+
+# Try to set it to PID 1, which it should ignore, because that's the manager
+systemd-notify MAINPID=1
+test `systemctl show -P MainPID testsuite-20.service` -eq $$
+
+# Try to set it to PID 0, which is invalid and should be ignored
+systemd-notify MAINPID=0
+test `systemctl show -P MainPID testsuite-20.service` -eq $$
+
+# Try to set it to a valid but non-existing PID, which should be ignored. (Note
+# that we set the PID to a value well above any known /proc/sys/kernel/pid_max,
+# which means we can be pretty sure it doesn't exist by coincidence)
+systemd-notify MAINPID=1073741824
+test `systemctl show -P MainPID testsuite-20.service` -eq $$
+
+# Change it again to the external PID, without privileges this time. This should be ignored, because the PID is from outside of our cgroup and we lack privileges.
+systemd-notify --uid=1000 MAINPID=$EXTERNALPID
+test `systemctl show -P MainPID testsuite-20.service` -eq $$
+
+# Change it again to the internal PID, without privileges this time. This should work, as the process is on our cgroup, and that's enough even if we lack privileges.
+systemd-notify --uid=1000 MAINPID=$INTERNALPID
+test `systemctl show -P MainPID testsuite-20.service` -eq $INTERNALPID
+
+# Update it back to our own PID, this should also work
+systemd-notify --uid=1000 MAINPID=$$
+test `systemctl show -P MainPID testsuite-20.service` -eq $$
+
+cat >/tmp/test20-mainpid.sh <<EOF
+#!/usr/bin/env bash
+
+set -eux
+set -o pipefail
+
+# Create a number of children, and make one the main one
+sleep infinity &
+disown
+
+sleep infinity &
+MAINPID=\$!
+disown
+
+sleep infinity &
+disown
+
+echo \$MAINPID > /run/mainpidsh/pid
+EOF
+chmod +x /tmp/test20-mainpid.sh
+
+systemd-run --unit=test20-mainpidsh.service -p StandardOutput=tty -p StandardError=tty -p Type=forking -p RuntimeDirectory=mainpidsh -p PIDFile=/run/mainpidsh/pid /tmp/test20-mainpid.sh
+test `systemctl show -P MainPID test20-mainpidsh.service` -eq `cat /run/mainpidsh/pid`
+
+cat >/tmp/test20-mainpid2.sh <<EOF
+#!/usr/bin/env bash
+
+set -eux
+set -o pipefail
+
+# Create a number of children, and make one the main one
+sleep infinity &
+disown
+
+sleep infinity &
+MAINPID=\$!
+disown
+
+sleep infinity &
+disown
+
+echo \$MAINPID > /run/mainpidsh2/pid
+chown 1001:1001 /run/mainpidsh2/pid
+EOF
+chmod +x /tmp/test20-mainpid2.sh
+
+systemd-run --unit=test20-mainpidsh2.service -p StandardOutput=tty -p StandardError=tty -p Type=forking -p RuntimeDirectory=mainpidsh2 -p PIDFile=/run/mainpidsh2/pid /tmp/test20-mainpid2.sh
+test `systemctl show -P MainPID test20-mainpidsh2.service` -eq `cat /run/mainpidsh2/pid`
+
+cat >/dev/shm/test20-mainpid3.sh <<EOF
+#!/usr/bin/env bash
+
+set -eux
+set -o pipefail
+
+sleep infinity &
+disown
+
+sleep infinity &
+disown
+
+sleep infinity &
+disown
+
+# Let's try to play games, and link up a privileged PID file
+ln -s ../mainpidsh/pid /run/mainpidsh3/pid
+
+# Quick assertion that the link isn't dead
+test -f /run/mainpidsh3/pid
+EOF
+chmod 755 /dev/shm/test20-mainpid3.sh
+
+# This has to fail, as we shouldn't accept the dangerous PID file, and then inotify-wait on it to be corrected which we never do
+! systemd-run --unit=test20-mainpidsh3.service -p StandardOutput=tty -p StandardError=tty -p Type=forking -p RuntimeDirectory=mainpidsh3 -p PIDFile=/run/mainpidsh3/pid -p DynamicUser=1 -p TimeoutStartSec=2s /dev/shm/test20-mainpid3.sh
+
+# Test that this failed due to timeout, and not some other error
+test `systemctl show -P Result test20-mainpidsh3.service` = timeout
+
+systemd-analyze log-level info
+
+echo OK > /testok
+
+exit 0
diff --git a/test/units/testsuite-22.01.sh b/test/units/testsuite-22.01.sh
new file mode 100755 (executable)
index 0000000..d233e37
--- /dev/null
@@ -0,0 +1,13 @@
+#! /bin/bash
+#
+# With "e" don't attempt to set permissions when file doesn't exist, see
+# https://github.com/systemd/systemd/pull/6682.
+#
+
+set -e
+
+rm -fr /tmp/test
+
+echo "e /tmp/test - root root 1d" | systemd-tmpfiles --create -
+
+! test -e /tmp/test
diff --git a/test/units/testsuite-22.02.sh b/test/units/testsuite-22.02.sh
new file mode 100755 (executable)
index 0000000..d1bf1ea
--- /dev/null
@@ -0,0 +1,122 @@
+#! /bin/bash
+#
+# Basic tests for types creating directories
+#
+
+set -e
+set -x
+
+rm -fr /tmp/{C,d,D,e}
+mkdir  /tmp/{C,d,D,e}
+
+#
+# 'd'
+#
+mkdir /tmp/d/2
+chmod 777 /tmp/d/2
+
+systemd-tmpfiles --create - <<EOF
+d     /tmp/d/1    0755 daemon daemon - -
+d     /tmp/d/2    0755 daemon daemon - -
+EOF
+
+test -d /tmp/d/1
+test $(stat -c %U:%G:%a /tmp/d/1) = "daemon:daemon:755"
+
+test -d /tmp/d/2
+test $(stat -c %U:%G:%a /tmp/d/2) = "daemon:daemon:755"
+
+#
+# 'D'
+#
+mkdir /tmp/D/2
+chmod 777 /tmp/D/2
+touch /tmp/D/2/foo
+
+systemd-tmpfiles --create - <<EOF
+D     /tmp/D/1    0755 daemon daemon - -
+D     /tmp/D/2    0755 daemon daemon - -
+EOF
+
+test -d /tmp/D/1
+test $(stat -c %U:%G:%a /tmp/D/1) = "daemon:daemon:755"
+
+test -d /tmp/D/2
+test $(stat -c %U:%G:%a /tmp/D/2) = "daemon:daemon:755"
+
+systemd-tmpfiles --remove - <<EOF
+D     /tmp/D/2    0755 daemon daemon - -
+EOF
+
+# the content of '2' should be removed
+test "$(echo /tmp/D/2/*)" = "/tmp/D/2/*"
+
+#
+# 'e'
+#
+mkdir -p /tmp/e/2/{d1,d2}
+chmod 777 /tmp/e/2
+chmod 777 /tmp/e/2/d*
+
+systemd-tmpfiles --create - <<EOF
+e     /tmp/e/1     0755 daemon daemon - -
+e     /tmp/e/2/*   0755 daemon daemon - -
+EOF
+
+! test -d /tmp/e/1
+
+test -d /tmp/e/2
+test $(stat -c %U:%G:%a /tmp/e/2) = "root:root:777"
+
+test -d /tmp/e/2/d1
+test $(stat -c %U:%G:%a /tmp/e/2/d1) = "daemon:daemon:755"
+test -d /tmp/e/2/d2
+test $(stat -c %U:%G:%a /tmp/e/2/d2) = "daemon:daemon:755"
+
+# 'e' operates on directories only
+mkdir -p /tmp/e/3/{d1,d2}
+chmod 777 /tmp/e/3
+chmod 777 /tmp/e/3/d*
+touch /tmp/e/3/f1
+chmod 644 /tmp/e/3/f1
+
+! systemd-tmpfiles --create - <<EOF
+e     /tmp/e/3/*   0755 daemon daemon - -
+EOF
+
+# the directories should have been processed although systemd-tmpfiles failed
+# previously due to the presence of a file.
+test -d /tmp/e/3/d1
+test $(stat -c %U:%G:%a /tmp/e/3/d1) = "daemon:daemon:755"
+test -d /tmp/e/3/d2
+test $(stat -c %U:%G:%a /tmp/e/3/d2) = "daemon:daemon:755"
+
+test -f /tmp/e/3/f1
+test $(stat -c %U:%G:%a /tmp/e/3/f1) = "root:root:644"
+
+#
+# 'C'
+#
+
+mkdir /tmp/C/{1,2,3}-origin
+touch /tmp/C/{1,2,3}-origin/f1
+chmod 755 /tmp/C/{1,2,3}-origin/f1
+
+mkdir /tmp/C/{2,3}
+touch /tmp/C/3/f1
+
+systemd-tmpfiles --create - <<EOF
+C     /tmp/C/1    0755 daemon daemon - /tmp/C/1-origin
+C     /tmp/C/2    0755 daemon daemon - /tmp/C/2-origin
+EOF
+
+test -d /tmp/C/1
+test $(stat -c %U:%G:%a /tmp/C/1/f1) = "daemon:daemon:755"
+test -d /tmp/C/2
+test $(stat -c %U:%G:%a /tmp/C/2/f1) = "daemon:daemon:755"
+
+! systemd-tmpfiles --create - <<EOF
+C     /tmp/C/3    0755 daemon daemon - /tmp/C/3-origin
+EOF
+
+test $(stat -c %U:%G:%a /tmp/C/3/f1) = "root:root:644"
diff --git a/test/units/testsuite-22.03.sh b/test/units/testsuite-22.03.sh
new file mode 100755 (executable)
index 0000000..8d009fb
--- /dev/null
@@ -0,0 +1,236 @@
+#! /bin/bash
+#
+# Basic tests for types creating/writing files
+#
+
+set -e
+set -x
+
+rm -fr /tmp/{f,F,w}
+mkdir  /tmp/{f,F,w}
+touch /tmp/file-owned-by-root
+
+#
+# 'f'
+#
+systemd-tmpfiles --create - <<EOF
+f     /tmp/f/1    0644 - - - -
+f     /tmp/f/2    0644 - - - This string should be written
+EOF
+
+### '1' should exist and be empty
+test -f /tmp/f/1; ! test -s /tmp/f/1
+test $(stat -c %U:%G:%a /tmp/f/1) = "root:root:644"
+
+test $(stat -c %U:%G:%a /tmp/f/2) = "root:root:644"
+test "$(< /tmp/f/2)" = "This string should be written"
+
+### The perms are supposed to be updated even if the file already exists.
+systemd-tmpfiles --create - <<EOF
+f     /tmp/f/1    0666 daemon daemon - This string should not be written
+EOF
+
+# file should be empty
+! test -s /tmp/f/1
+test $(stat -c %U:%G:%a /tmp/f/1) = "daemon:daemon:666"
+
+### But we shouldn't try to set perms on an existing file which is not a
+### regular one.
+mkfifo /tmp/f/fifo
+chmod 644 /tmp/f/fifo
+
+! systemd-tmpfiles --create - <<EOF
+f     /tmp/f/fifo    0666 daemon daemon - This string should not be written
+EOF
+
+test -p /tmp/f/fifo
+test $(stat -c %U:%G:%a /tmp/f/fifo) = "root:root:644"
+
+### 'f' should not follow symlinks.
+ln -s missing /tmp/f/dangling
+ln -s /tmp/file-owned-by-root /tmp/f/symlink
+
+! systemd-tmpfiles --create - <<EOF
+f     /tmp/f/dangling    0644 daemon daemon - -
+f     /tmp/f/symlink     0644 daemon daemon - -
+EOF
+! test -e /tmp/f/missing
+test $(stat -c %U:%G:%a /tmp/file-owned-by-root) = "root:root:644"
+
+### Handle read-only filesystem gracefully: we shouldn't fail if the target
+### already exists and have the correct perms.
+mkdir /tmp/f/rw-fs
+mkdir /tmp/f/ro-fs
+
+touch /tmp/f/rw-fs/foo
+chmod 644 /tmp/f/rw-fs/foo
+
+mount -o bind,ro /tmp/f/rw-fs /tmp/f/ro-fs
+
+systemd-tmpfiles --create - <<EOF
+f     /tmp/f/ro-fs/foo    0644 - - - - This string should not be written
+EOF
+test -f /tmp/f/ro-fs/foo; ! test -s /tmp/f/ro-fs/foo
+
+! systemd-tmpfiles --create - <<EOF
+f     /tmp/f/ro-fs/foo    0666 - - - -
+EOF
+test $(stat -c %U:%G:%a /tmp/f/fifo) = "root:root:644"
+
+! systemd-tmpfiles --create - <<EOF
+f     /tmp/f/ro-fs/bar    0644 - - - -
+EOF
+! test -e /tmp/f/ro-fs/bar
+
+### 'f' shouldn't follow unsafe paths.
+mkdir /tmp/f/daemon
+ln -s /root /tmp/f/daemon/unsafe-symlink
+chown -R --no-dereference daemon:daemon /tmp/f/daemon
+
+! systemd-tmpfiles --create - <<EOF
+f     /tmp/f/daemon/unsafe-symlink/exploit    0644 daemon daemon - -
+EOF
+! test -e /tmp/f/daemon/unsafe-symlink/exploit
+
+#
+# 'F'
+#
+echo "This should be truncated" >/tmp/F/truncated
+echo "This should be truncated" >/tmp/F/truncated-with-content
+
+systemd-tmpfiles --create - <<EOF
+F     /tmp/F/created                0644 - - - -
+F     /tmp/F/created-with-content   0644 - - - new content
+F     /tmp/F/truncated              0666 daemon daemon - -
+F     /tmp/F/truncated-with-content 0666 daemon daemon - new content
+EOF
+
+test -f /tmp/F/created; ! test -s /tmp/F/created
+test -f /tmp/F/created-with-content
+test "$(< /tmp/F/created-with-content)" = "new content"
+test -f /tmp/F/truncated; ! test -s /tmp/F/truncated
+test $(stat -c %U:%G:%a /tmp/F/truncated) = "daemon:daemon:666"
+test -s /tmp/F/truncated-with-content
+test $(stat -c %U:%G:%a /tmp/F/truncated-with-content) = "daemon:daemon:666"
+
+### We shouldn't try to truncate anything but regular files since the behavior is
+### unspecified in the other cases.
+mkfifo /tmp/F/fifo
+
+! systemd-tmpfiles --create - <<EOF
+F     /tmp/F/fifo                0644 - - - -
+EOF
+
+test -p /tmp/F/fifo
+
+### 'F' should not follow symlinks.
+ln -s missing /tmp/F/dangling
+ln -s /tmp/file-owned-by-root /tmp/F/symlink
+
+! systemd-tmpfiles --create - <<EOF
+f     /tmp/F/dangling    0644 daemon daemon - -
+f     /tmp/F/symlink     0644 daemon daemon - -
+EOF
+! test -e /tmp/F/missing
+test $(stat -c %U:%G:%a /tmp/file-owned-by-root) = "root:root:644"
+
+### Handle read-only filesystem gracefully: we shouldn't fail if the target
+### already exists and is empty.
+mkdir /tmp/F/rw-fs
+mkdir /tmp/F/ro-fs
+
+touch /tmp/F/rw-fs/foo
+chmod 644 /tmp/F/rw-fs/foo
+
+mount -o bind,ro /tmp/F/rw-fs /tmp/F/ro-fs
+
+systemd-tmpfiles --create - <<EOF
+F     /tmp/F/ro-fs/foo    0644 - - - -
+EOF
+test -f /tmp/F/ro-fs/foo; ! test -s /tmp/F/ro-fs/foo
+
+echo "truncating is not allowed anymore" >/tmp/F/rw-fs/foo
+! systemd-tmpfiles --create - <<EOF
+F     /tmp/F/ro-fs/foo    0644 - - - -
+EOF
+
+! systemd-tmpfiles --create - <<EOF
+F     /tmp/F/ro-fs/foo    0644 - - - - This string should not be written
+EOF
+test -f /tmp/F/ro-fs/foo; ! test -s /tmp/F/ro-fs/foo
+
+# Trying to change the perms should fail.
+>/tmp/F/rw-fs/foo
+! systemd-tmpfiles --create - <<EOF
+F     /tmp/F/ro-fs/foo    0666 - - - -
+EOF
+test $(stat -c %U:%G:%a /tmp/F/ro-fs/foo) = "root:root:644"
+
+### Try to create a new file.
+! systemd-tmpfiles --create - <<EOF
+F     /tmp/F/ro-fs/bar    0644 - - - -
+EOF
+! test -e /tmp/F/ro-fs/bar
+
+### 'F' shouldn't follow unsafe paths.
+mkdir /tmp/F/daemon
+ln -s /root /tmp/F/daemon/unsafe-symlink
+chown -R --no-dereference daemon:daemon /tmp/F/daemon
+
+! systemd-tmpfiles --create - <<EOF
+F     /tmp/F/daemon/unsafe-symlink/exploit    0644 daemon daemon - -
+EOF
+! test -e /tmp/F/daemon/unsafe-symlink/exploit
+
+#
+# 'w'
+#
+touch /tmp/w/overwritten
+
+### nop if the target does not exist.
+systemd-tmpfiles --create - <<EOF
+w     /tmp/w/unexistent    0644 - - - new content
+EOF
+! test -e /tmp/w/unexistent
+
+### no argument given -> fails.
+! systemd-tmpfiles --create - <<EOF
+w     /tmp/w/unexistent    0644 - - - -
+EOF
+
+### write into an empty file.
+systemd-tmpfiles --create - <<EOF
+w     /tmp/w/overwritten    0644 - - - old content
+EOF
+test -f /tmp/w/overwritten
+test "$(< /tmp/w/overwritten)" = "old content"
+
+### new content is overwritten
+systemd-tmpfiles --create - <<EOF
+w     /tmp/w/overwritten    0644 - - - new content
+EOF
+test -f /tmp/w/overwritten
+test "$(< /tmp/w/overwritten)" = "new content"
+
+### writing into an 'exotic' file should be allowed.
+systemd-tmpfiles --create - <<EOF
+w     /dev/null    - - - - new content
+EOF
+
+### 'w' follows symlinks
+ln -s ./overwritten /tmp/w/symlink
+systemd-tmpfiles --create - <<EOF
+w     /tmp/w/symlink    - - - - $(readlink -e /tmp/w/symlink)
+EOF
+readlink -e /tmp/w/symlink
+test "$(< /tmp/w/overwritten)" = "/tmp/w/overwritten"
+
+### 'w' shouldn't follow unsafe paths.
+mkdir /tmp/w/daemon
+ln -s /root /tmp/w/daemon/unsafe-symlink
+chown -R --no-dereference daemon:daemon /tmp/w/daemon
+
+! systemd-tmpfiles --create - <<EOF
+f     /tmp/w/daemon/unsafe-symlink/exploit    0644 daemon daemon - -
+EOF
+! test -e /tmp/w/daemon/unsafe-symlink/exploit
diff --git a/test/units/testsuite-22.04.sh b/test/units/testsuite-22.04.sh
new file mode 100755 (executable)
index 0000000..f916086
--- /dev/null
@@ -0,0 +1,44 @@
+#! /bin/bash
+#
+# Basic tests for types creating fifos
+#
+
+set -e
+set -x
+
+rm -fr /tmp/p
+mkdir  /tmp/p
+touch  /tmp/p/f1
+
+systemd-tmpfiles --create - <<EOF
+p     /tmp/p/fifo1    0666 - - - -
+EOF
+
+test -p /tmp/p/fifo1
+test $(stat -c %U:%G:%a /tmp/p/fifo1) = "root:root:666"
+
+# it should refuse to overwrite an existing file
+! systemd-tmpfiles --create - <<EOF
+p     /tmp/p/f1    0666 - - - -
+EOF
+
+test -f /tmp/p/f1
+
+# unless '+' prefix is used
+systemd-tmpfiles --create - <<EOF
+p+     /tmp/p/f1    0666 - - - -
+EOF
+
+test -p /tmp/p/f1
+test $(stat -c %U:%G:%a /tmp/p/f1) = "root:root:666"
+
+#
+# Must be fixed
+#
+# mkdir /tmp/p/daemon
+# #ln -s /root /tmp/F/daemon/unsafe-symlink
+# chown -R --no-dereference daemon:daemon /tmp/p/daemon
+#
+# systemd-tmpfiles --create - <<EOF
+# p      /tmp/p/daemon/fifo2    0666 daemon daemon - -
+# EOF
diff --git a/test/units/testsuite-22.05.sh b/test/units/testsuite-22.05.sh
new file mode 100755 (executable)
index 0000000..13c4ac8
--- /dev/null
@@ -0,0 +1,45 @@
+#! /bin/bash
+
+set -e
+set -x
+
+rm -fr /tmp/{z,Z}
+mkdir  /tmp/{z,Z}
+
+#
+# 'z'
+#
+mkdir /tmp/z/d{1,2}
+touch /tmp/z/f1 /tmp/z/d1/f11 /tmp/z/d2/f21
+
+systemd-tmpfiles --create - <<EOF
+z     /tmp/z/f1    0755 daemon daemon - -
+z     /tmp/z/d1    0755 daemon daemon - -
+EOF
+
+test $(stat -c %U:%G /tmp/z/f1) = "daemon:daemon"
+test $(stat -c %U:%G /tmp/z/d1) = "daemon:daemon"
+test $(stat -c %U:%G /tmp/z/d1/f11) = "root:root"
+
+systemd-tmpfiles --create - <<EOF
+z     /tmp/z/d2/*    0755 daemon daemon - -
+EOF
+
+test $(stat -c %U:%G /tmp/z/d2/f21) = "daemon:daemon"
+
+#
+# 'Z'
+#
+mkdir /tmp/Z/d1 /tmp/Z/d1/d11
+touch /tmp/Z/f1 /tmp/Z/d1/f11 /tmp/Z/d1/d11/f111
+
+systemd-tmpfiles --create - <<EOF
+Z     /tmp/Z/f1    0755 daemon daemon - -
+Z     /tmp/Z/d1    0755 daemon daemon - -
+EOF
+
+test $(stat -c %U:%G /tmp/Z/f1) = "daemon:daemon"
+test $(stat -c %U:%G /tmp/Z/d1) = "daemon:daemon"
+test $(stat -c %U:%G /tmp/Z/d1/d11) = "daemon:daemon"
+test $(stat -c %U:%G /tmp/Z/d1/f11) = "daemon:daemon"
+test $(stat -c %U:%G /tmp/Z/d1/d11/f111) = "daemon:daemon"
diff --git a/test/units/testsuite-22.06.sh b/test/units/testsuite-22.06.sh
new file mode 100755 (executable)
index 0000000..cd65ba6
--- /dev/null
@@ -0,0 +1,38 @@
+#! /bin/bash
+#
+# Inspired by https://github.com/systemd/systemd/issues/9508
+#
+
+set -e
+
+test_snippet() {
+        systemd-tmpfiles "$@" - <<EOF
+d /var/tmp/foobar-test-06
+d /var/tmp/foobar-test-06/important
+R /var/tmp/foobar-test-06
+EOF
+}
+
+test_snippet --create --remove
+test -d /var/tmp/foobar-test-06
+test -d /var/tmp/foobar-test-06/important
+
+test_snippet --remove
+! test -f /var/tmp/foobar-test-06
+! test -f /var/tmp/foobar-test-06/important
+
+test_snippet --create
+test -d /var/tmp/foobar-test-06
+test -d /var/tmp/foobar-test-06/important
+
+touch /var/tmp/foobar-test-06/something-else
+
+test_snippet --create
+test -d /var/tmp/foobar-test-06
+test -d /var/tmp/foobar-test-06/important
+test -f /var/tmp/foobar-test-06/something-else
+
+test_snippet --create --remove
+test -d /var/tmp/foobar-test-06
+test -d /var/tmp/foobar-test-06/important
+! test -f /var/tmp/foobar-test-06/something-else
diff --git a/test/units/testsuite-22.07.sh b/test/units/testsuite-22.07.sh
new file mode 100755 (executable)
index 0000000..39c04b9
--- /dev/null
@@ -0,0 +1,31 @@
+#! /bin/bash
+#
+# Verifies the issues described by https://github.com/systemd/systemd/issues/10191
+#
+
+set -e
+set -x
+
+rm -rf /tmp/test-prefix
+
+mkdir /tmp/test-prefix
+touch /tmp/test-prefix/file
+
+systemd-tmpfiles --remove - <<EOF
+r /tmp/test-prefix
+r /tmp/test-prefix/file
+EOF
+
+! test -f /tmp/test-prefix/file
+! test -f /tmp/test-prefix
+
+mkdir /tmp/test-prefix
+touch /tmp/test-prefix/file
+
+systemd-tmpfiles --remove - <<EOF
+r /tmp/test-prefix/file
+r /tmp/test-prefix
+EOF
+
+! test -f /tmp/test-prefix/file
+! test -f /tmp/test-prefix
diff --git a/test/units/testsuite-22.08.sh b/test/units/testsuite-22.08.sh
new file mode 100755 (executable)
index 0000000..e7bf044
--- /dev/null
@@ -0,0 +1,32 @@
+#! /bin/bash
+#
+# Verify tmpfiles can run in a root directory under a path prefix that contains
+# directories owned by unprivileged users, for example when a root file system
+# is mounted in a regular user's home directory.
+#
+# https://github.com/systemd/systemd/pull/11820
+#
+
+set -e
+
+rm -fr /tmp/root /tmp/user
+mkdir -p /tmp/root /tmp/user/root
+chown daemon:daemon /tmp/user
+
+# Verify the command works as expected with no prefix or a root-owned prefix.
+echo 'd /tmp/root/test1' | systemd-tmpfiles --create -
+test -d /tmp/root/test1
+echo 'd /test2' | systemd-tmpfiles --root=/tmp/root --create -
+test -d /tmp/root/test2
+
+# Verify the command fails to write to a root-owned subdirectory under an
+# unprivileged user's directory when it's not part of the prefix, as expected
+# by the unsafe_transition function.
+! echo 'd /tmp/user/root/test' | systemd-tmpfiles --create -
+! test -e /tmp/user/root/test
+! echo 'd /user/root/test' | systemd-tmpfiles --root=/tmp --create -
+! test -e /tmp/user/root/test
+
+# Verify the above works when all user-owned directories are in the prefix.
+echo 'd /test' | systemd-tmpfiles --root=/tmp/user/root --create -
+test -d /tmp/user/root/test
diff --git a/test/units/testsuite-22.09.sh b/test/units/testsuite-22.09.sh
new file mode 100755 (executable)
index 0000000..c558dfd
--- /dev/null
@@ -0,0 +1,59 @@
+#!/usr/bin/env bash
+
+set -e
+set -x
+
+# Make sure that the "stat" output is not locale dependent.
+export LANG=C LC_ALL=C
+
+# first, create file without suid/sgid
+systemd-tmpfiles --create - <<EOF
+f     /tmp/xxx    0755 1 1 - -
+f     /tmp/yyy    0755 1 1 - -
+EOF
+
+test "$(stat -c %F:%u:%g:%a /tmp/xxx)" = "regular empty file:1:1:755"
+test "$(stat -c %F:%u:%g:%a /tmp/yyy)" = "regular empty file:1:1:755"
+
+# then, add suid/sgid
+systemd-tmpfiles --create - <<EOF
+f     /tmp/xxx    04755
+f     /tmp/yyy    02755
+EOF
+
+test "$(stat -c %F:%u:%g:%a /tmp/xxx)" = "regular empty file:1:1:4755"
+test "$(stat -c %F:%u:%g:%a /tmp/yyy)" = "regular empty file:1:1:2755"
+
+# then, chown the files to somebody else
+systemd-tmpfiles --create - <<EOF
+f     /tmp/xxx    - 2 2
+f     /tmp/yyy    - 2 2
+EOF
+
+test "$(stat -c %F:%u:%g:%a /tmp/xxx)" = "regular empty file:2:2:4755"
+test "$(stat -c %F:%u:%g:%a /tmp/yyy)" = "regular empty file:2:2:2755"
+
+# then, chown the files to a third user/group but also drop to a mask that has
+# both more and fewer bits set
+systemd-tmpfiles --create - <<EOF
+f     /tmp/xxx    0770 3 3
+f     /tmp/yyy    0770 3 3
+EOF
+
+test "$(stat -c %F:%u:%g:%a /tmp/xxx)" = "regular empty file:3:3:770"
+test "$(stat -c %F:%u:%g:%a /tmp/yyy)" = "regular empty file:3:3:770"
+
+# return to the beginning
+systemd-tmpfiles --create - <<EOF
+f     /tmp/xxx    0755 1 1 - -
+f     /tmp/yyy    0755 1 1 - -
+EOF
+
+test "$(stat -c %F:%u:%g:%a /tmp/xxx)" = "regular empty file:1:1:755"
+test "$(stat -c %F:%u:%g:%a /tmp/yyy)" = "regular empty file:1:1:755"
+
+# remove everything
+systemd-tmpfiles --remove - <<EOF
+r /tmp/xxx
+r /tmp/yyy
+EOF
diff --git a/test/units/testsuite-22.service b/test/units/testsuite-22.service
new file mode 100644 (file)
index 0000000..55e3056
--- /dev/null
@@ -0,0 +1,12 @@
+[Unit]
+Description=TEST-22-TMPFILES
+After=systemd-tmpfiles-setup.service
+Before=getty-pre.target
+Wants=getty-pre.target
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
+StandardOutput=tty
+StandardError=tty
diff --git a/test/units/testsuite-22.sh b/test/units/testsuite-22.sh
new file mode 100755 (executable)
index 0000000..afce85a
--- /dev/null
@@ -0,0 +1,13 @@
+#!/usr/bin/env bash
+
+set -x
+set -e
+
+>/failed
+
+for t in ${0%.sh}.*.sh; do
+    echo "Running $t"; ./$t
+done
+
+touch /testok
+rm /failed
diff --git a/test/units/testsuite-23.service b/test/units/testsuite-23.service
new file mode 100644 (file)
index 0000000..b3b3297
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=TEST-23-TYPE-EXEC
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/testsuite-23.sh b/test/units/testsuite-23.sh
new file mode 100755 (executable)
index 0000000..5e2966f
--- /dev/null
@@ -0,0 +1,34 @@
+#!/usr/bin/env bash
+set -ex
+set -o pipefail
+
+systemd-analyze log-level debug
+systemd-analyze log-target console
+
+# Create a binary for which execve() will fail
+touch /tmp/brokenbinary
+chmod +x /tmp/brokenbinary
+
+# These three commands should succeed.
+systemd-run --unit=one -p Type=simple /bin/sleep infinity
+systemd-run --unit=two -p Type=simple -p User=idontexist /bin/sleep infinity
+systemd-run --unit=three -p Type=simple /tmp/brokenbinary
+
+# And now, do the same with Type=exec, where the latter two should fail
+systemd-run --unit=four -p Type=exec /bin/sleep infinity
+! systemd-run --unit=five -p Type=exec -p User=idontexist /bin/sleep infinity
+! systemd-run --unit=six -p Type=exec /tmp/brokenbinary
+
+systemd-run --unit=seven -p KillSignal=SIGTERM -p RestartKillSignal=SIGINT -p Type=exec /bin/sleep infinity
+# Both TERM and SIGINT happen to have the same number on all architectures
+test $(systemctl show --value -p KillSignal seven.service) -eq 15
+test $(systemctl show --value -p RestartKillSignal seven.service) -eq 2
+
+systemctl restart seven.service
+systemctl stop seven.service
+
+systemd-analyze log-level info
+
+echo OK > /testok
+
+exit 0
diff --git a/test/units/testsuite-24.service b/test/units/testsuite-24.service
new file mode 100644 (file)
index 0000000..43d4816
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=TEST-24-UNIT-TESTS
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/testsuite-24.sh b/test/units/testsuite-24.sh
new file mode 100755 (executable)
index 0000000..1ff1c33
--- /dev/null
@@ -0,0 +1,88 @@
+#!/usr/bin/env bash
+#set -ex
+#set -o pipefail
+
+NPROC=$(nproc)
+MAX_QUEUE_SIZE=${NPROC:-2}
+IFS=$'\n' TEST_LIST=($(ls /usr/lib/systemd/tests/test-*))
+
+# reset state
+rm /failed-tests /skipped-tests /skipped
+
+# Check & report test results
+# Arguments:
+#   $1: test path
+#   $2: test exit code
+function report_result() {
+    if [[ $# -ne 2 ]]; then
+        echo >&2 "check_result: missing arguments"
+        exit 1
+    fi
+
+    local name="${1##*/}"
+    local ret=$2
+
+    if [[ $ret -ne 0 && $ret != 77 ]]; then
+        echo "$name failed with $ret"
+        echo "$name" >>/failed-tests
+        {
+            echo "--- $name begin ---"
+            cat "/$name.log"
+            echo "--- $name end ---"
+        } >>/failed
+    elif [[ $ret == 77 ]]; then
+        echo "$name skipped"
+        echo "$name" >>/skipped-tests
+        {
+            echo "--- $name begin ---"
+            cat "/$name.log"
+            echo "--- $name end ---"
+        } >>/skipped
+    else
+        echo "$name OK"
+        echo "$name" >>/testok
+    fi
+
+    systemd-cat echo "--- $name ---"
+    systemd-cat cat "/$name.log"
+}
+
+# Associative array for running tasks, where running[test-path]=PID
+declare -A running=()
+for task in "${TEST_LIST[@]}"; do
+    # If there's MAX_QUEUE_SIZE running tasks, keep checking the running queue
+    # until one of the tasks finishes, so we can replace it.
+    while [[ ${#running[@]} -ge $MAX_QUEUE_SIZE ]]; do
+        for key in "${!running[@]}"; do
+            if ! kill -0 ${running[$key]} &>/dev/null; then
+                # Task has finished, report its result and drop it from the queue
+                wait ${running[$key]}
+                ec=$?
+                report_result "$key" $ec
+                unset running["$key"]
+                # Break from inner for loop and outer while loop to skip
+                # the sleep below when we find a free slot in the queue
+                break 2
+            fi
+        done
+
+        # Precisely* calculated constant to keep the spinlock from burning the CPU(s)
+        sleep 0.01
+    done
+
+    if [[ -x $task ]]; then
+        log_file="/${task##*/}.log"
+        $task &>"$log_file" &
+        running[$task]=$!
+    fi
+done
+
+# Wait for remaining running tasks
+for key in "${!running[@]}"; do
+    wait ${running[$key]}
+    ec=$?
+    report_result "$key" $ec
+    unset running["$key"]
+done
+
+exit 0
diff --git a/test/units/testsuite-25.service b/test/units/testsuite-25.service
new file mode 100644 (file)
index 0000000..45d8b69
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=TEST-25-IMPORT
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/testsuite-25.sh b/test/units/testsuite-25.sh
new file mode 100755 (executable)
index 0000000..e3dd43a
--- /dev/null
@@ -0,0 +1,143 @@
+#!/usr/bin/env bash
+set -ex
+set -o pipefail
+
+export SYSTEMD_PAGER=cat
+
+dd if=/dev/urandom of=/var/tmp/testimage.raw bs=$((1024*1024+7)) count=5
+
+# Test import
+machinectl import-raw /var/tmp/testimage.raw
+machinectl image-status testimage
+test -f /var/lib/machines/testimage.raw
+cmp /var/tmp/testimage.raw /var/lib/machines/testimage.raw
+
+# Test export
+machinectl export-raw testimage /var/tmp/testimage2.raw
+cmp /var/tmp/testimage.raw /var/tmp/testimage2.raw
+rm /var/tmp/testimage2.raw
+
+# Test compressed export (gzip)
+machinectl export-raw testimage /var/tmp/testimage2.raw.gz
+gunzip /var/tmp/testimage2.raw.gz
+cmp /var/tmp/testimage.raw /var/tmp/testimage2.raw
+rm /var/tmp/testimage2.raw
+
+# Test clone
+machinectl clone testimage testimage3
+test -f /var/lib/machines/testimage3.raw
+machinectl image-status testimage3
+test -f /var/lib/machines/testimage.raw
+machinectl image-status testimage
+cmp /var/tmp/testimage.raw /var/lib/machines/testimage.raw
+cmp /var/tmp/testimage.raw /var/lib/machines/testimage3.raw
+
+# Test removal
+machinectl remove testimage
+! test -f /var/lib/machines/testimage.raw
+! machinectl image-status testimage
+
+# Test export of clone
+machinectl export-raw testimage3 /var/tmp/testimage3.raw
+cmp /var/tmp/testimage.raw /var/tmp/testimage3.raw
+rm /var/tmp/testimage3.raw
+
+# Test rename
+machinectl rename testimage3 testimage4
+test -f /var/lib/machines/testimage4.raw
+machinectl image-status testimage4
+! test -f /var/lib/machines/testimage3.raw
+! machinectl image-status testimage3
+cmp /var/tmp/testimage.raw /var/lib/machines/testimage4.raw
+
+# Test export of rename
+machinectl export-raw testimage4 /var/tmp/testimage4.raw
+cmp /var/tmp/testimage.raw /var/tmp/testimage4.raw
+rm /var/tmp/testimage4.raw
+
+# Test removal
+machinectl remove testimage4
+! test -f /var/lib/machines/testimage4.raw
+! machinectl image-status testimage4
+
+# → And now, let's test directory trees ← #
+
+# Set up a directory we can import
+mkdir /var/tmp/scratch
+mv /var/tmp/testimage.raw /var/tmp/scratch/
+touch /var/tmp/scratch/anotherfile
+mkdir /var/tmp/scratch/adirectory
+echo "piep" > /var/tmp/scratch/adirectory/athirdfile
+
+# Test import-fs
+machinectl import-fs /var/tmp/scratch/
+test -d /var/lib/machines/scratch
+machinectl image-status scratch
+
+# Test export-tar
+machinectl export-tar scratch /var/tmp/scratch.tar.gz
+test -f /var/tmp/scratch.tar.gz
+mkdir /var/tmp/extract
+(cd /var/tmp/extract ; tar xzf /var/tmp/scratch.tar.gz)
+diff -r /var/tmp/scratch/ /var/tmp/extract/
+rm -rf /var/tmp/extract
+
+# Test import-tar
+machinectl import-tar /var/tmp/scratch.tar.gz scratch2
+test -d /var/lib/machines/scratch2
+machinectl image-status scratch2
+diff -r /var/tmp/scratch/ /var/lib/machines/scratch2
+
+# Test removal
+machinectl remove scratch
+! test -f /var/lib/machines/scratch
+! machinectl image-status scratch
+
+# Test clone
+machinectl clone scratch2 scratch3
+test -d /var/lib/machines/scratch2
+machinectl image-status scratch2
+test -d /var/lib/machines/scratch3
+machinectl image-status scratch3
+diff -r /var/tmp/scratch/ /var/lib/machines/scratch3
+
+# Test removal
+machinectl remove scratch2
+! test -f /var/lib/machines/scratch2
+! machinectl image-status scratch2
+
+# Test rename
+machinectl rename scratch3 scratch4
+test -d /var/lib/machines/scratch4
+machinectl image-status scratch4
+! test -f /var/lib/machines/scratch3
+! machinectl image-status scratch3
+diff -r /var/tmp/scratch/ /var/lib/machines/scratch4
+
+# Test removal
+machinectl remove scratch4
+! test -f /var/lib/machines/scratch4
+! machinectl image-status scratch4
+
+# Test import-tar hyphen/stdin pipe behavior
+cat /var/tmp/scratch.tar.gz | machinectl import-tar - scratch5
+test -d /var/lib/machines/scratch5
+machinectl image-status scratch5
+diff -r /var/tmp/scratch/ /var/lib/machines/scratch5
+
+# Test export-tar hyphen/stdout pipe behavior
+mkdir -p /var/tmp/extract
+machinectl export-tar scratch5 - | tar xvf - -C /var/tmp/extract/
+diff -r /var/tmp/scratch/ /var/tmp/extract/
+rm -rf /var/tmp/extract
+
+rm -rf /var/tmp/scratch
+
+# Test removal
+machinectl remove scratch5
+! test -f /var/lib/machines/scratch5
+! machinectl image-status scratch5
+
+echo OK > /testok
+
+exit 0
diff --git a/test/units/testsuite-26.service b/test/units/testsuite-26.service
new file mode 100644 (file)
index 0000000..65b6683
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=TEST-26-SETENV
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/testsuite-26.sh b/test/units/testsuite-26.sh
new file mode 100755 (executable)
index 0000000..89c0937
--- /dev/null
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+set -ex
+set -o pipefail
+
+# Make sure PATH is set
+systemctl show-environment | grep -q '^PATH='
+
+# Let's add an entry and override a built-in one
+systemctl set-environment PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/testaddition FOO=BAR
+
+# Check that both are set
+systemctl show-environment | grep -q '^PATH=.*testaddition$'
+systemctl show-environment | grep -q '^FOO=BAR$'
+
+systemctl daemon-reload
+
+# Check again after the reload
+systemctl show-environment | grep -q '^PATH=.*testaddition$'
+systemctl show-environment | grep -q '^FOO=BAR$'
+
+# Drop both
+systemctl unset-environment FOO PATH
+
+# Check that one is gone and the other reverted to the built-in
+! (systemctl show-environment | grep -q '^FOO=$')
+! (systemctl show-environment | grep -q '^PATH=.*testaddition$')
+systemctl show-environment | grep -q '^PATH='
+
+echo OK > /testok
+
+exit 0
diff --git a/test/units/testsuite-27.service b/test/units/testsuite-27.service
new file mode 100644 (file)
index 0000000..52185f0
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=TEST-27-STDOUTFILE
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/testsuite-27.sh b/test/units/testsuite-27.sh
new file mode 100755 (executable)
index 0000000..9d92e6e
--- /dev/null
@@ -0,0 +1,50 @@
+#!/usr/bin/env bash
+set -ex
+set -o pipefail
+
+systemd-analyze log-level debug
+systemd-analyze log-target console
+
+systemd-run --wait --unit=test27-one \
+            -p StandardOutput=file:/tmp/stdout \
+            -p StandardError=file:/tmp/stderr \
+            -p Type=exec \
+            sh -c 'echo x ; echo y >&2'
+cmp /tmp/stdout <<EOF
+x
+EOF
+cmp /tmp/stderr <<EOF
+y
+EOF
+
+systemd-run --wait --unit=test27-two \
+            -p StandardOutput=file:/tmp/stdout \
+            -p StandardError=file:/tmp/stderr \
+            -p Type=exec \
+            sh -c 'echo z ; echo a >&2'
+cmp /tmp/stdout <<EOF
+z
+EOF
+cmp /tmp/stderr <<EOF
+a
+EOF
+
+systemd-run --wait --unit=test27-three \
+            -p StandardOutput=append:/tmp/stdout \
+            -p StandardError=append:/tmp/stderr \
+            -p Type=exec \
+            sh -c 'echo b ; echo c >&2'
+cmp /tmp/stdout <<EOF
+z
+b
+EOF
+cmp /tmp/stderr <<EOF
+a
+c
+EOF
+
+systemd-analyze log-level info
+
+echo OK >/testok
+
+exit 0
diff --git a/test/units/testsuite-28.service b/test/units/testsuite-28.service
new file mode 100644 (file)
index 0000000..7ea8630
--- /dev/null
@@ -0,0 +1,11 @@
+[Unit]
+Description=TEST-28-PERCENTJ-WANTEDBY
+# Testsuite: Ensure %j Wants directives work
+Wants=specifier-j-wants.service
+After=specifier-j-wants.service
+Requires=testsuite-28-pre.service
+After=testsuite-28-pre.service
+
+[Service]
+ExecStart=true
+Type=oneshot
diff --git a/test/units/testsuite-29.service b/test/units/testsuite-29.service
new file mode 100644 (file)
index 0000000..90c2187
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=TEST-29-UDEV-ID_RENAMING
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/testsuite-29.sh b/test/units/testsuite-29.sh
new file mode 100755 (executable)
index 0000000..5abdb53
--- /dev/null
@@ -0,0 +1,43 @@
+#!/usr/bin/env bash
+set -ex
+set -o pipefail
+
+mkdir -p /run/udev/rules.d/
+
+cat > /run/udev/rules.d/50-testsuite.rules <<EOF
+ACTION=="remove", GOTO="lo_end"
+
+SUBSYSTEM=="net", KERNEL=="lo", TAG+="systemd", ENV{SYSTEMD_ALIAS}+="/sys/subsystem/net/devices/lo"
+
+ACTION!="change", GOTO="lo_end"
+
+SUBSYSTEM=="net", KERNEL=="lo", ENV{ID_RENAMING}="1"
+
+LABEL="lo_end"
+EOF
+
+udevadm control --log-priority=debug --reload --timeout=600
+udevadm trigger --action=add --settle /sys/devices/virtual/net/lo
+udevadm info /sys/devices/virtual/net/lo
+sleep 1
+STATE=$(systemctl show --property=ActiveState --value sys-devices-virtual-net-lo.device)
+[[ $STATE == "active" ]] || exit 1
+
+udevadm trigger --action=change --settle /sys/devices/virtual/net/lo
+udevadm info /sys/devices/virtual/net/lo
+sleep 1
+STATE=$(systemctl show --property=ActiveState --value sys-devices-virtual-net-lo.device)
+[[ $STATE == "inactive" ]] || exit 1
+
+udevadm trigger --action=move --settle /sys/devices/virtual/net/lo
+udevadm info /sys/devices/virtual/net/lo
+sleep 1
+STATE=$(systemctl show --property=ActiveState --value sys-devices-virtual-net-lo.device)
+[[ $STATE == "active" ]] || exit 1
+
+rm -f /run/udev/rules.d/50-testsuite.rules
+udevadm control --reload --timeout=600
+
+echo OK > /testok
+
+exit 0
diff --git a/test/units/testsuite-30.service b/test/units/testsuite-30.service
new file mode 100644 (file)
index 0000000..eb342f3
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=TEST-30-ONCLOCKCHANGE
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/testsuite-30.sh b/test/units/testsuite-30.sh
new file mode 100755 (executable)
index 0000000..a507ffc
--- /dev/null
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+set -ex
+set -o pipefail
+
+systemd-analyze log-level debug
+systemd-analyze log-target console
+
+systemctl disable --now systemd-timesyncd.service
+
+timedatectl set-timezone Europe/Berlin
+timedatectl set-time 1980-10-15
+
+systemd-run --on-timezone-change touch /tmp/timezone-changed
+systemd-run --on-clock-change touch /tmp/clock-changed
+
+! test -f /tmp/timezone-changed
+! test -f /tmp/clock-changed
+
+timedatectl set-timezone Europe/Kiev
+
+while ! test -f /tmp/timezone-changed ; do sleep .5 ; done
+
+timedatectl set-time 2018-1-1
+
+while ! test -f /tmp/clock-changed ; do sleep .5 ; done
+
+systemd-analyze log-level info
+
+echo OK > /testok
+
+exit 0
diff --git a/test/units/testsuite-31.service b/test/units/testsuite-31.service
new file mode 100644 (file)
index 0000000..07dfb0b
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=TEST-31-DEVICE-ENUMERATION
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/testsuite-31.sh b/test/units/testsuite-31.sh
new file mode 100755 (executable)
index 0000000..fcff82d
--- /dev/null
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+set -e
+set -o pipefail
+
+if journalctl -b -t systemd --grep '\.device: Changed plugged -> dead'; then
+    exit 1
+fi
+
+echo OK > /testok
+exit 0
diff --git a/test/units/testsuite-32.service b/test/units/testsuite-32.service
new file mode 100644 (file)
index 0000000..aab95cb
--- /dev/null
@@ -0,0 +1,8 @@
+[Unit]
+Description=TEST-32-OOMPOLICY
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
+MemoryAccounting=yes
diff --git a/test/units/testsuite-32.sh b/test/units/testsuite-32.sh
new file mode 100755 (executable)
index 0000000..c1704ab
--- /dev/null
@@ -0,0 +1,39 @@
+#!/usr/bin/env bash
+set -ex
+set -o pipefail
+
+# Let's run this test only if the "memory.oom.group" cgroupfs attribute
+# exists. This test is a bit too strict, since the "memory.events"/"oom_kill"
+# logic has been around since a longer time than "memory.oom.group", but it's
+# an easier thing to test for, and also: let's not get confused by older
+# kernels where the concept was still new.
+
+if test -f /sys/fs/cgroup/system.slice/testsuite-32.service/memory.oom.group; then
+
+    systemd-analyze log-level debug
+    systemd-analyze log-target console
+
+    # Run a service that is guaranteed to be the first candidate for OOM killing
+    systemd-run --unit=oomtest.service \
+                -p Type=exec -p OOMScoreAdjust=1000 -p OOMPolicy=stop -p MemoryAccounting=yes \
+                sleep infinity
+
+    # Trigger an OOM killer run
+    echo 1 >/proc/sys/kernel/sysrq
+    echo f >/proc/sysrq-trigger
+
+    while : ; do
+        STATE=`systemctl show -P ActiveState oomtest.service`
+        [ "$STATE" = "failed" ] && break
+        sleep .5
+    done
+
+    RESULT=`systemctl show -P Result oomtest.service`
+    test "$RESULT" = "oom-kill"
+
+    systemd-analyze log-level info
+fi
+
+echo OK >/testok
+
+exit 0
diff --git a/test/units/testsuite-33.service b/test/units/testsuite-33.service
new file mode 100644 (file)
index 0000000..b64f1e0
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=TEST-33-CLEAN-UNIT
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/testsuite-33.sh b/test/units/testsuite-33.sh
new file mode 100755 (executable)
index 0000000..0a6ee57
--- /dev/null
@@ -0,0 +1,319 @@
+#!/usr/bin/env bash
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+set -ex
+set -o pipefail
+
+cat > /etc/systemd/system/testservice.service <<EOF
+[Service]
+ConfigurationDirectory=testservice
+RuntimeDirectory=testservice
+StateDirectory=testservice
+CacheDirectory=testservice
+LogsDirectory=testservice
+RuntimeDirectoryPreserve=yes
+ExecStart=/bin/sleep infinity
+Type=exec
+EOF
+
+systemctl daemon-reload
+
+! test -e /etc/testservice
+! test -e /run/testservice
+! test -e /var/lib/testservice
+! test -e /var/cache/testservice
+! test -e /var/log/testservice
+
+systemctl start testservice
+
+test -d /etc/testservice
+test -d /run/testservice
+test -d /var/lib/testservice
+test -d /var/cache/testservice
+test -d /var/log/testservice
+
+! systemctl clean testservice
+
+systemctl stop testservice
+
+test -d /etc/testservice
+test -d /run/testservice
+test -d /var/lib/testservice
+test -d /var/cache/testservice
+test -d /var/log/testservice
+
+systemctl clean testservice --what=configuration
+
+! test -e /etc/testservice
+test -d /run/testservice
+test -d /var/lib/testservice
+test -d /var/cache/testservice
+test -d /var/log/testservice
+
+systemctl clean testservice
+
+! test -e /etc/testservice
+! test -e /run/testservice
+test -d /var/lib/testservice
+! test -e /var/cache/testservice
+test -d /var/log/testservice
+
+systemctl clean testservice --what=logs
+
+! test -e /etc/testservice
+! test -e /run/testservice
+test -d /var/lib/testservice
+! test -e /var/cache/testservice
+! test -e /var/log/testservice
+
+systemctl clean testservice --what=all
+
+! test -e /etc/testservice
+! test -e /run/testservice
+! test -e /var/lib/testservice
+! test -e /var/cache/testservice
+! test -e /var/log/testservice
+
+cat > /etc/systemd/system/testservice.service <<EOF
+[Service]
+DynamicUser=yes
+ConfigurationDirectory=testservice
+RuntimeDirectory=testservice
+StateDirectory=testservice
+CacheDirectory=testservice
+LogsDirectory=testservice
+RuntimeDirectoryPreserve=yes
+ExecStart=/bin/sleep infinity
+Type=exec
+EOF
+
+systemctl daemon-reload
+
+! test -e /etc/testservice
+! test -e /run/testservice
+! test -e /var/lib/testservice
+! test -e /var/cache/testservice
+! test -e /var/log/testservice
+
+systemctl restart testservice
+
+test -d /etc/testservice
+test -d /run/private/testservice
+test -d /var/lib/private/testservice
+test -d /var/cache/private/testservice
+test -d /var/log/private/testservice
+test -L /run/testservice
+test -L /var/lib/testservice
+test -L /var/cache/testservice
+test -L /var/log/testservice
+
+! systemctl clean testservice
+
+systemctl stop testservice
+
+test -d /etc/testservice
+test -d /run/private/testservice
+test -d /var/lib/private/testservice
+test -d /var/cache/private/testservice
+test -d /var/log/private/testservice
+test -L /run/testservice
+test -L /var/lib/testservice
+test -L /var/cache/testservice
+test -L /var/log/testservice
+
+systemctl clean testservice --what=configuration
+
+! test -d /etc/testservice
+test -d /run/private/testservice
+test -d /var/lib/private/testservice
+test -d /var/cache/private/testservice
+test -d /var/log/private/testservice
+test -L /run/testservice
+test -L /var/lib/testservice
+test -L /var/cache/testservice
+test -L /var/log/testservice
+
+systemctl clean testservice
+
+! test -d /etc/testservice
+! test -d /run/private/testservice
+test -d /var/lib/private/testservice
+! test -d /var/cache/private/testservice
+test -d /var/log/private/testservice
+! test -L /run/testservice
+test -L /var/lib/testservice
+! test -L /var/cache/testservice
+test -L /var/log/testservice
+
+systemctl clean testservice --what=logs
+
+! test -d /etc/testservice
+! test -d /run/private/testservice
+test -d /var/lib/private/testservice
+! test -d /var/cache/private/testservice
+! test -d /var/log/private/testservice
+! test -L /run/testservice
+test -L /var/lib/testservice
+! test -L /var/cache/testservice
+! test -L /var/log/testservice
+
+systemctl clean testservice --what=all
+
+! test -d /etc/testservice
+! test -d /run/private/testservice
+! test -d /var/lib/private/testservice
+! test -d /var/cache/private/testservice
+! test -d /var/log/private/testservice
+! test -L /run/testservice
+! test -L /var/lib/testservice
+! test -L /var/cache/testservice
+! test -L /var/log/testservice
+
+cat > /etc/systemd/system/tmp-hoge.mount <<EOF
+[Mount]
+What=tmpfs
+Type=tmpfs
+ConfigurationDirectory=hoge
+RuntimeDirectory=hoge
+StateDirectory=hoge
+CacheDirectory=hoge
+LogsDirectory=hoge
+EOF
+
+systemctl daemon-reload
+
+! test -e /etc/hoge
+! test -e /run/hoge
+! test -e /var/lib/hoge
+! test -e /var/cache/hoge
+! test -e /var/log/hoge
+
+systemctl start tmp-hoge.mount
+
+test -d /etc/hoge
+test -d /run/hoge
+test -d /var/lib/hoge
+test -d /var/cache/hoge
+test -d /var/log/hoge
+
+! systemctl clean tmp-hoge.mount
+
+test -d /etc/hoge
+test -d /run/hoge
+test -d /var/lib/hoge
+test -d /var/cache/hoge
+test -d /var/log/hoge
+
+systemctl stop tmp-hoge.mount
+
+test -d /etc/hoge
+! test -d /run/hoge
+test -d /var/lib/hoge
+test -d /var/cache/hoge
+test -d /var/log/hoge
+
+systemctl clean tmp-hoge.mount --what=configuration
+
+! test -d /etc/hoge
+! test -d /run/hoge
+test -d /var/lib/hoge
+test -d /var/cache/hoge
+test -d /var/log/hoge
+
+systemctl clean tmp-hoge.mount
+
+! test -d /etc/hoge
+! test -d /run/hoge
+test -d /var/lib/hoge
+! test -d /var/cache/hoge
+test -d /var/log/hoge
+
+systemctl clean tmp-hoge.mount --what=logs
+
+! test -d /etc/hoge
+! test -d /run/hoge
+test -d /var/lib/hoge
+! test -d /var/cache/hoge
+! test -d /var/log/hoge
+
+systemctl clean tmp-hoge.mount --what=all
+
+! test -d /etc/hoge
+! test -d /run/hoge
+! test -d /var/lib/hoge
+! test -d /var/cache/hoge
+! test -d /var/log/hoge
+
+cat > /etc/systemd/system/testservice.socket <<EOF
+[Socket]
+ListenSequentialPacket=/run/testservice.socket
+RemoveOnStop=yes
+ExecStartPre=true
+ConfigurationDirectory=testsocket
+RuntimeDirectory=testsocket
+StateDirectory=testsocket
+CacheDirectory=testsocket
+LogsDirectory=testsocket
+EOF
+
+systemctl daemon-reload
+
+! test -e /etc/testsocket
+! test -e /run/testsocket
+! test -e /var/lib/testsocket
+! test -e /var/cache/testsocket
+! test -e /var/log/testsocket
+
+systemctl start testservice.socket
+
+test -d /etc/testsocket
+! test -d /run/testsocket
+test -d /var/lib/testsocket
+test -d /var/cache/testsocket
+test -d /var/log/testsocket
+
+! systemctl clean testservice.socket
+
+systemctl stop testservice.socket
+
+test -d /etc/testsocket
+! test -d /run/testsocket
+test -d /var/lib/testsocket
+test -d /var/cache/testsocket
+test -d /var/log/testsocket
+
+systemctl clean testservice.socket --what=configuration
+
+! test -e /etc/testsocket
+! test -d /run/testsocket
+test -d /var/lib/testsocket
+test -d /var/cache/testsocket
+test -d /var/log/testsocket
+
+systemctl clean testservice.socket
+
+! test -e /etc/testsocket
+! test -e /run/testsocket
+test -d /var/lib/testsocket
+! test -e /var/cache/testsocket
+test -d /var/log/testsocket
+
+systemctl clean testservice.socket --what=logs
+
+! test -e /etc/testsocket
+! test -e /run/testsocket
+test -d /var/lib/testsocket
+! test -e /var/cache/testsocket
+! test -e /var/log/testsocket
+
+systemctl clean testservice.socket --what=all
+
+! test -e /etc/testsocket
+! test -e /run/testsocket
+! test -e /var/lib/testsocket
+! test -e /var/cache/testsocket
+! test -e /var/log/testsocket
+
+echo OK > /testok
+
+exit 0
diff --git a/test/units/testsuite-34.service b/test/units/testsuite-34.service
new file mode 100644 (file)
index 0000000..361e328
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=TEST-34-DYNAMICUSERMIGRATE
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/testsuite-34.sh b/test/units/testsuite-34.sh
new file mode 100755 (executable)
index 0000000..6d94886
--- /dev/null
@@ -0,0 +1,46 @@
+#!/usr/bin/env bash
+set -ex
+set -o pipefail
+
+systemd-analyze log-level debug
+systemd-analyze log-target console
+
+# Set everything up without DynamicUser=1
+
+systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz touch /var/lib/zzz/test
+systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz test -f /var/lib/zzz/test
+! systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz test -f /var/lib/zzz/test-missing
+
+test -d /var/lib/zzz
+! test -L /var/lib/zzz
+! test -e /var/lib/private/zzz
+test -f /var/lib/zzz/test
+! test -f /var/lib/zzz/test-missing
+
+# Convert to DynamicUser=1
+
+systemd-run --wait -p DynamicUser=1 -p StateDirectory=zzz test -f /var/lib/zzz/test
+! systemd-run --wait -p DynamicUser=1 -p StateDirectory=zzz test -f /var/lib/zzz/test-missing
+
+test -L /var/lib/zzz
+test -d /var/lib/private/zzz
+
+test -f /var/lib/zzz/test
+! test -f /var/lib/zzz/test-missing
+
+# Convert back
+
+systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz test -f /var/lib/zzz/test
+! systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz test -f /var/lib/zzz/test-missing
+
+test -d /var/lib/zzz
+! test -L /var/lib/zzz
+! test -e /var/lib/private/zzz
+test -f /var/lib/zzz/test
+! test -f /var/lib/zzz/test-missing
+
+systemd-analyze log-level info
+
+echo OK > /testok
+
+exit 0
diff --git a/test/units/testsuite-36.service b/test/units/testsuite-36.service
new file mode 100644 (file)
index 0000000..a681153
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=TEST-36-NUMAPOLICY
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/testsuite-36.sh b/test/units/testsuite-36.sh
new file mode 100755 (executable)
index 0000000..aed9384
--- /dev/null
@@ -0,0 +1,341 @@
+#!/usr/bin/env bash
+set -ex
+set -o pipefail
+
+at_exit() {
+    if [ $? -ne 0 ]; then
+        # We're exiting with a non-zero EC, let's dump test artifacts
+        # for easier debugging
+        [ -f "$straceLog" ] && cat "$straceLog"
+        [ -f "$journalLog" ] && cat "$journalLog"
+    fi
+}
+
+trap at_exit EXIT
+
+systemd-analyze log-level debug
+systemd-analyze log-target journal
+
+# Log files
+straceLog='strace.log'
+journalLog='journal.log'
+
+# Systemd config files
+testUnit='numa-test.service'
+testUnitFile="/run/systemd/system/$testUnit"
+testUnitNUMAConf="$testUnitFile.d/numa.conf"
+
+# Sleep constants (we should probably figure out something better but nothing comes to mind)
+journalSleep=5
+sleepAfterStart=1
+
+# Journal cursor for easier navigation
+journalCursorFile="jounalCursorFile"
+
+startStrace() {
+    coproc strace -qq -p 1 -o $straceLog -e set_mempolicy -s 1024 $1
+    # Wait for strace to properly "initialize"
+    sleep $sleepAfterStart
+}
+
+stopStrace() {
+    kill -s TERM $COPROC_PID
+    # Make sure the strace process is indeed dead
+    while kill -0 $COPROC_PID 2>/dev/null; do sleep 0.1; done
+}
+
+startJournalctl() {
+    # Save journal's cursor for later navigation
+    journalctl --no-pager --cursor-file="$journalCursorFile" -n0 -ocat
+}
+
+stopJournalctl() {
+    local unit="${1:-init.scope}"
+    # Using journalctl --sync should be better than using SIGRTMIN+1, as
+    # the --sync wait until the synchronization is complete
+    echo "Force journald to write all queued messages"
+    journalctl --sync
+    journalctl -u $unit --cursor-file="$journalCursorFile" > "$journalLog"
+}
+
+checkNUMA() {
+    # NUMA enabled system should have at least NUMA node0
+    test -e /sys/devices/system/node/node0
+}
+
+writePID1NUMAPolicy() {
+    echo [Manager] > $confDir/numa.conf
+    echo NUMAPolicy=$1 >> $confDir/numa.conf
+    echo NUMAMask=$2>> $confDir/numa.conf
+}
+
+writeTestUnit() {
+    mkdir -p $testUnitFile.d/
+    echo [Service] > $testUnitFile
+    echo ExecStart=/bin/sleep 3600 >> $testUnitFile
+}
+
+writeTestUnitNUMAPolicy() {
+    echo [Service] > $testUnitNUMAConf
+    echo NUMAPolicy=$1 >> $testUnitNUMAConf
+    echo NUMAMask=$2>> $testUnitNUMAConf
+    systemctl daemon-reload
+}
+
+pid1ReloadWithStrace() {
+    startStrace
+    systemctl daemon-reload
+    sleep $sleepAfterStart
+    stopStrace
+}
+
+pid1ReloadWithJournal() {
+    startJournalctl
+    systemctl daemon-reload
+    stopJournalctl
+}
+
+pid1StartUnitWithStrace() {
+    startStrace '-f'
+    systemctl start $1
+    sleep $sleepAfterStart
+    stopStrace
+}
+
+pid1StartUnitWithJournal() {
+    startJournalctl
+    systemctl start $1
+    sleep $sleepAfterStart
+    stopJournalctl
+}
+
+pid1StopUnit() {
+    systemctl stop $1
+}
+
+systemctlCheckNUMAProperties() {
+    local LOGFILE="$(mktemp)"
+    systemctl show -p NUMAPolicy $1 > "$LOGFILE"
+    grep "NUMAPolicy=$2" "$LOGFILE"
+
+    > "$LOGFILE"
+
+    if [ -n "$3" ]; then
+        systemctl show -p NUMAMask $1 > "$LOGFILE"
+        grep "NUMAMask=$3" "$LOGFILE"
+    fi
+}
+
+writeTestUnit
+
+# Create systemd config drop-in directory
+confDir="/run/systemd/system.conf.d/"
+mkdir -p "$confDir"
+
+if ! checkNUMA; then
+    echo >&2 "NUMA is not supported on this machine, switching to a simple sanity check"
+
+    echo "PID1 NUMAPolicy=default && NUMAMask=0 check without NUMA support"
+    writePID1NUMAPolicy "default" "0"
+    startJournalctl
+    systemctl daemon-reload
+    stopJournalctl
+    grep "NUMA support not available, ignoring" "$journalLog"
+
+    echo "systemd-run NUMAPolicy=default && NUMAMask=0 check without NUMA support"
+    runUnit='numa-systemd-run-test.service'
+    startJournalctl
+    systemd-run -p NUMAPolicy=default -p NUMAMask=0 --unit $runUnit sleep 1000
+    sleep $sleepAfterStart
+    pid1StopUnit $runUnit
+    stopJournalctl $runUnit
+    grep "NUMA support not available, ignoring" "$journalLog"
+
+else
+    echo "PID1 NUMAPolicy support - Default policy w/o mask"
+    writePID1NUMAPolicy "default"
+    pid1ReloadWithStrace
+    # Kernel requires that nodemask argument is set to NULL when setting default policy
+    grep "set_mempolicy(MPOL_DEFAULT, NULL" $straceLog
+
+    echo "PID1 NUMAPolicy support - Default policy w/ mask"
+    writePID1NUMAPolicy "default" "0"
+    pid1ReloadWithStrace
+    grep "set_mempolicy(MPOL_DEFAULT, NULL" $straceLog
+
+    echo "PID1 NUMAPolicy support - Bind policy w/o mask"
+    writePID1NUMAPolicy "bind"
+    pid1ReloadWithJournal
+    grep "Failed to set NUMA memory policy: Invalid argument" $journalLog
+
+    echo "PID1 NUMAPolicy support - Bind policy w/ mask"
+    writePID1NUMAPolicy "bind" "0"
+    pid1ReloadWithStrace
+    grep -P "set_mempolicy\(MPOL_BIND, \[0x0*1\]" $straceLog
+
+    echo "PID1 NUMAPolicy support - Interleave policy w/o mask"
+    writePID1NUMAPolicy "interleave"
+    pid1ReloadWithJournal
+    grep "Failed to set NUMA memory policy: Invalid argument" $journalLog
+
+    echo "PID1 NUMAPolicy support - Interleave policy w/ mask"
+    writePID1NUMAPolicy "interleave" "0"
+    pid1ReloadWithStrace
+    grep -P "set_mempolicy\(MPOL_INTERLEAVE, \[0x0*1\]" $straceLog
+
+    echo "PID1 NUMAPolicy support - Preferred policy w/o mask"
+    writePID1NUMAPolicy "preferred"
+    pid1ReloadWithJournal
+    # Preferred policy with empty node mask is actually allowed and should reset allocation policy to default
+    ! grep "Failed to set NUMA memory policy: Invalid argument" $journalLog
+
+    echo "PID1 NUMAPolicy support - Preferred policy w/ mask"
+    writePID1NUMAPolicy "preferred" "0"
+    pid1ReloadWithStrace
+    grep -P "set_mempolicy\(MPOL_PREFERRED, \[0x0*1\]" $straceLog
+
+    echo "PID1 NUMAPolicy support - Local policy w/o mask"
+    writePID1NUMAPolicy "local"
+    pid1ReloadWithStrace
+    # Kernel requires that nodemask argument is set to NULL when setting default policy
+    # The unpatched versions of strace don't recognize the MPOL_LOCAL constant and
+    # return a numerical constant instead (with a comment):
+    #   set_mempolicy(0x4 /* MPOL_??? */, NULL, 0) = 0
+    # Let's cover this scenario as well
+    grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" $straceLog
+
+    echo "PID1 NUMAPolicy support - Local policy w/ mask"
+    writePID1NUMAPolicy "local" "0"
+    pid1ReloadWithStrace
+    grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" $straceLog
+
+    echo "Unit file NUMAPolicy support - Default policy w/o mask"
+    writeTestUnitNUMAPolicy "default"
+    pid1StartUnitWithStrace $testUnit
+    systemctlCheckNUMAProperties $testUnit "default"
+    pid1StopUnit $testUnit
+    grep "set_mempolicy(MPOL_DEFAULT, NULL" $straceLog
+
+    echo "Unit file NUMAPolicy support - Default policy w/ mask"
+    writeTestUnitNUMAPolicy "default" "0"
+    pid1StartUnitWithStrace $testUnit
+    systemctlCheckNUMAProperties $testUnit "default" "0"
+    pid1StopUnit $testUnit
+    # Maks must be ignored
+    grep "set_mempolicy(MPOL_DEFAULT, NULL" $straceLog
+
+    echo "Unit file NUMAPolicy support - Bind policy w/o mask"
+    writeTestUnitNUMAPolicy "bind"
+    pid1StartUnitWithJournal $testUnit
+    pid1StopUnit $testUnit
+    grep "numa-test.service: Main process exited, code=exited, status=242/NUMA" $journalLog
+
+    echo "Unit file NUMAPolicy support - Bind policy w/ mask"
+    writeTestUnitNUMAPolicy "bind" "0"
+    pid1StartUnitWithStrace $testUnit
+    systemctlCheckNUMAProperties $testUnit "bind" "0"
+    pid1StopUnit $testUnit
+    grep -P "set_mempolicy\(MPOL_BIND, \[0x0*1\]" $straceLog
+
+    echo "Unit file NUMAPolicy support - Interleave policy w/o mask"
+    writeTestUnitNUMAPolicy "interleave"
+    pid1StartUnitWithStrace $testUnit
+    pid1StopUnit $testUnit
+    grep "numa-test.service: Main process exited, code=exited, status=242/NUMA" $journalLog
+
+    echo "Unit file NUMAPolicy support - Interleave policy w/ mask"
+    writeTestUnitNUMAPolicy "interleave" "0"
+    pid1StartUnitWithStrace $testUnit
+    systemctlCheckNUMAProperties $testUnit "interleave" "0"
+    pid1StopUnit $testUnit
+    grep -P "set_mempolicy\(MPOL_INTERLEAVE, \[0x0*1\]" $straceLog
+
+    echo "Unit file NUMAPolicy support - Preferred policy w/o mask"
+    writeTestUnitNUMAPolicy "preferred"
+    pid1StartUnitWithJournal $testUnit
+    systemctlCheckNUMAProperties $testUnit "preferred"
+    pid1StopUnit $testUnit
+    ! grep "numa-test.service: Main process exited, code=exited, status=242/NUMA" $journalLog
+
+    echo "Unit file NUMAPolicy support - Preferred policy w/ mask"
+    writeTestUnitNUMAPolicy "preferred" "0"
+    pid1StartUnitWithStrace $testUnit
+    systemctlCheckNUMAProperties $testUnit "preferred" "0"
+    pid1StopUnit $testUnit
+    grep -P "set_mempolicy\(MPOL_PREFERRED, \[0x0*1\]" $straceLog
+
+    echo "Unit file NUMAPolicy support - Local policy w/o mask"
+    writeTestUnitNUMAPolicy "local"
+    pid1StartUnitWithStrace $testUnit
+    systemctlCheckNUMAProperties $testUnit "local"
+    pid1StopUnit $testUnit
+    grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" $straceLog
+
+    echo "Unit file NUMAPolicy support - Local policy w/ mask"
+    writeTestUnitNUMAPolicy "local" "0"
+    pid1StartUnitWithStrace $testUnit
+    systemctlCheckNUMAProperties $testUnit "local" "0"
+    pid1StopUnit $testUnit
+    # Maks must be ignored
+    grep -E "set_mempolicy\((MPOL_LOCAL|0x4 [^,]*), NULL" $straceLog
+
+    echo "Unit file CPUAffinity=NUMA support"
+    writeTestUnitNUMAPolicy "bind" "0"
+    echo "CPUAffinity=numa" >> $testUnitNUMAConf
+    systemctl daemon-reload
+    systemctl start $testUnit
+    systemctlCheckNUMAProperties $testUnit "bind" "0"
+    pid=$(systemctl show --value -p MainPID $testUnit)
+    cpulist=$(cat /sys/devices/system/node/node0/cpulist)
+    affinity_systemd=$(systemctl show --value -p CPUAffinity $testUnit)
+    [ $cpulist = $affinity_systemd ]
+    pid1StopUnit $testUnit
+
+    echo "systemd-run NUMAPolicy support"
+    runUnit='numa-systemd-run-test.service'
+
+    systemd-run -p NUMAPolicy=default --unit $runUnit sleep 1000
+    systemctlCheckNUMAProperties $runUnit "default"
+    pid1StopUnit $runUnit
+
+    systemd-run -p NUMAPolicy=default -p NUMAMask=0 --unit $runUnit sleep 1000
+    systemctlCheckNUMAProperties $runUnit "default" ""
+    pid1StopUnit $runUnit
+
+    systemd-run -p NUMAPolicy=bind -p NUMAMask=0 --unit $runUnit sleep 1000
+    systemctlCheckNUMAProperties $runUnit "bind" "0"
+    pid1StopUnit $runUnit
+
+    systemd-run -p NUMAPolicy=interleave -p NUMAMask=0 --unit $runUnit sleep 1000
+    systemctlCheckNUMAProperties $runUnit "interleave" "0"
+    pid1StopUnit $runUnit
+
+    systemd-run -p NUMAPolicy=preferred -p NUMAMask=0 --unit $runUnit sleep 1000
+    systemctlCheckNUMAProperties $runUnit "preferred" "0"
+    pid1StopUnit $runUnit
+
+    systemd-run -p NUMAPolicy=local --unit $runUnit sleep 1000
+    systemctlCheckNUMAProperties $runUnit "local"
+    pid1StopUnit $runUnit
+
+    systemd-run -p NUMAPolicy=local -p NUMAMask=0 --unit $runUnit sleep 1000
+    systemctlCheckNUMAProperties $runUnit "local" ""
+    pid1StopUnit $runUnit
+
+    systemd-run -p NUMAPolicy=local -p NUMAMask=0 -p CPUAffinity=numa --unit $runUnit sleep 1000
+    systemctlCheckNUMAProperties $runUnit "local" ""
+    systemctl cat $runUnit | grep -q 'CPUAffinity=numa'
+    pid1StopUnit $runUnit
+
+fi
+
+# Cleanup
+rm -rf $testDir
+rm -rf $confDir
+systemctl daemon-reload
+
+systemd-analyze log-level info
+
+echo OK > /testok
+
+exit 0
diff --git a/test/units/testsuite-37.service b/test/units/testsuite-37.service
new file mode 100644 (file)
index 0000000..d25c6d2
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=TEST-37-RUNTIMEDIRECTORYPRESERVE
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/testsuite-37.sh b/test/units/testsuite-37.sh
new file mode 100755 (executable)
index 0000000..32a9dd8
--- /dev/null
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+set -ex
+set -o pipefail
+
+systemd-mount -p RuntimeDirectory=hoge -p RuntimeDirectoryPreserve=yes -t tmpfs tmpfs /tmp/aaa
+
+touch /run/hoge/foo
+touch /tmp/aaa/bbb
+
+systemctl restart tmp-aaa.mount
+
+test -e /run/hoge/foo
+! test -e /tmp/aaa/bbb
+
+echo OK > /testok
+
+exit 0
diff --git a/test/units/testsuite-38-sleep.service b/test/units/testsuite-38-sleep.service
new file mode 100644 (file)
index 0000000..859f97b
--- /dev/null
@@ -0,0 +1,2 @@
+[Service]
+ExecStart=/bin/sleep 3600
diff --git a/test/units/testsuite-38.service b/test/units/testsuite-38.service
new file mode 100644 (file)
index 0000000..c848840
--- /dev/null
@@ -0,0 +1,6 @@
+[Unit]
+Description=TEST-38-FREEZER
+
+[Service]
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/testsuite-38.sh b/test/units/testsuite-38.sh
new file mode 100755 (executable)
index 0000000..18b7bd6
--- /dev/null
@@ -0,0 +1,297 @@
+#!/usr/bin/env bash
+
+set -ex
+set -o pipefail
+
+systemd-analyze log-level debug
+systemd-analyze log-target console
+
+unit=testsuite-38-sleep.service
+
+start_test_service() {
+    systemctl daemon-reload
+    systemctl start "${unit}"
+}
+
+dbus_freeze() {
+    local suffix=
+    suffix="${1##*.}"
+
+    local name="$(echo ${1%.$suffix} | sed s/-/_2d/g)"
+    local object_path="/org/freedesktop/systemd1/unit/${name}_2e${suffix}"
+
+    busctl call \
+           org.freedesktop.systemd1 \
+           "${object_path}" \
+           org.freedesktop.systemd1.Unit \
+           Freeze
+}
+
+dbus_thaw() {
+    local suffix=
+    suffix="${1##*.}"
+
+    local name="$(echo ${1%.$suffix} | sed s/-/_2d/g)"
+    local object_path="/org/freedesktop/systemd1/unit/${name}_2e${suffix}"
+
+    busctl call \
+           org.freedesktop.systemd1 \
+           "${object_path}" \
+           org.freedesktop.systemd1.Unit \
+           Thaw
+}
+
+dbus_freeze_unit() {
+    busctl call \
+           org.freedesktop.systemd1 \
+           /org/freedesktop/systemd1 \
+           org.freedesktop.systemd1.Manager \
+           FreezeUnit \
+           s \
+           "$1"
+}
+
+dbus_thaw_unit() {
+    busctl call \
+           org.freedesktop.systemd1 \
+           /org/freedesktop/systemd1 \
+           org.freedesktop.systemd1.Manager \
+           ThawUnit \
+           s \
+           "$1"
+}
+
+dbus_can_freeze() {
+    local suffix=
+    suffix="${1##*.}"
+
+    local name="$(echo ${1%.$suffix} | sed s/-/_2d/g)"
+    local object_path="/org/freedesktop/systemd1/unit/${name}_2e${suffix}"
+
+    busctl get-property \
+           org.freedesktop.systemd1 \
+           "${object_path}" \
+           org.freedesktop.systemd1.Unit \
+           CanFreeze
+}
+
+check_freezer_state() {
+    local suffix=
+    suffix="${1##*.}"
+
+    local name="$(echo ${1%.$suffix} | sed s/-/_2d/g)"
+    local object_path="/org/freedesktop/systemd1/unit/${name}_2e${suffix}"
+
+    state=$(busctl get-property \
+                   org.freedesktop.systemd1 \
+                   "${object_path}" \
+                   org.freedesktop.systemd1.Unit \
+                   FreezerState | cut -d " " -f2 | tr -d '"')
+
+    [ "$state" = "$2" ] || {
+        echo "error: unexpected freezer state, expected: $2, actual: $state" >&2
+        exit 1
+    }
+}
+
+check_cgroup_state() {
+    grep -q "frozen $2" /sys/fs/cgroup/system.slice/"$1"/cgroup.events
+}
+
+test_dbus_api() {
+    echo "Test that DBus API works:"
+    echo -n "  - Freeze(): "
+    dbus_freeze "${unit}"
+    check_freezer_state "${unit}" "frozen"
+    check_cgroup_state "$unit" 1
+    echo "[ OK ]"
+
+    echo -n "  - Thaw(): "
+    dbus_thaw "${unit}"
+    check_freezer_state "${unit}" "running"
+    check_cgroup_state "$unit" 0
+    echo "[ OK ]"
+
+    echo -n "  - FreezeUnit(): "
+    dbus_freeze_unit "${unit}"
+    check_freezer_state "${unit}" "frozen"
+    check_cgroup_state "$unit" 1
+    echo "[ OK ]"
+
+    echo -n "  - ThawUnit(): "
+    dbus_thaw_unit "${unit}"
+    check_freezer_state "${unit}" "running"
+    check_cgroup_state "$unit" 0
+    echo "[ OK ]"
+
+    echo -n "  - CanFreeze(): "
+    output=$(dbus_can_freeze "${unit}")
+    [ "$output" = "b true" ]
+    echo "[ OK ]"
+
+    echo
+}
+
+test_jobs() {
+    local pid_before=
+    local pid_after=
+    echo "Test that it is possible to apply jobs on frozen units:"
+
+    systemctl start "${unit}"
+    dbus_freeze "${unit}"
+    check_freezer_state "${unit}" "frozen"
+
+    echo -n "  - restart: "
+    pid_before=$(systemctl show -p MainPID "${unit}" --value)
+    systemctl restart "${unit}"
+    pid_after=$(systemctl show -p MainPID "${unit}" --value)
+    [ "$pid_before" != "$pid_after" ] && echo "[ OK ]"
+
+    dbus_freeze "${unit}"
+    check_freezer_state "${unit}" "frozen"
+
+    echo -n "  - stop: "
+    timeout 5s systemctl stop "${unit}"
+    echo "[ OK ]"
+
+    echo
+}
+
+test_systemctl() {
+    echo "Test that systemctl freeze/thaw verbs:"
+
+    systemctl start "$unit"
+
+    echo -n "  - freeze: "
+    systemctl freeze "$unit"
+    check_freezer_state "${unit}" "frozen"
+    check_cgroup_state "$unit" 1
+    # Freezing already frozen unit should be NOP and return quickly
+    timeout 3s systemctl freeze "$unit"
+    echo "[ OK ]"
+
+    echo -n "  - thaw: "
+    systemctl thaw "$unit"
+    check_freezer_state "${unit}" "running"
+    check_cgroup_state "$unit" 0
+    # Likewise thawing already running unit shouldn't block
+    timeout 3s systemctl thaw "$unit"
+    echo "[ OK ]"
+
+    systemctl stop "$unit"
+
+    echo
+}
+
+test_systemctl_show() {
+    echo "Test systemctl show integration:"
+
+    systemctl start "$unit"
+
+    echo -n "  - FreezerState property: "
+    state=$(systemctl show -p FreezerState --value "$unit")
+    [ "$state" = "running" ]
+    systemctl freeze "$unit"
+    state=$(systemctl show -p FreezerState --value "$unit")
+    [ "$state" = "frozen" ]
+    systemctl thaw "$unit"
+    echo "[ OK ]"
+
+    echo -n "  - CanFreeze property: "
+    state=$(systemctl show -p CanFreeze --value "$unit")
+    [ "$state" = "yes" ]
+    echo "[ OK ]"
+
+    systemctl stop "$unit"
+    echo
+}
+
+test_recursive() {
+    local slice="bar.slice"
+    local unit="baz.service"
+
+    systemd-run --unit "$unit" --slice "$slice" sleep 3600 >/dev/null 2>&1
+
+    echo "Test recursive freezing:"
+
+    echo -n "  - freeze: "
+    systemctl freeze "$slice"
+    check_freezer_state "${slice}" "frozen"
+    check_freezer_state "${unit}" "frozen"
+    grep -q "frozen 1" /sys/fs/cgroup/"${slice}"/cgroup.events
+    grep -q "frozen 1" /sys/fs/cgroup/"${slice}"/"${unit}"/cgroup.events
+    echo "[ OK ]"
+
+    echo -n "  - thaw: "
+    systemctl thaw "$slice"
+    check_freezer_state "${unit}" "running"
+    check_freezer_state "${slice}" "running"
+    grep -q "frozen 0" /sys/fs/cgroup/"${slice}"/cgroup.events
+    grep -q "frozen 0" /sys/fs/cgroup/"${slice}"/"${unit}"/cgroup.events
+    echo "[ OK ]"
+
+    systemctl stop "$unit"
+    systemctl stop "$slice"
+
+    echo
+}
+
+test_preserve_state() {
+    local slice="bar.slice"
+    local unit="baz.service"
+
+    systemd-run --unit "$unit" --slice "$slice" sleep 3600 >/dev/null 2>&1
+
+    echo "Test that freezer state is preserved when recursive freezing is initiated from outside (e.g. by manager up the tree):"
+
+    echo -n "  - freeze from outside: "
+    echo 1 > /sys/fs/cgroup/"${slice}"/cgroup.freeze
+    # Give kernel some time to freeze the slice
+    sleep 1
+
+    # Our state should not be affected
+    check_freezer_state "${slice}" "running"
+    check_freezer_state "${unit}" "running"
+
+    # However actual kernel state should be frozen
+    grep -q "frozen 1" /sys/fs/cgroup/"${slice}"/cgroup.events
+    grep -q "frozen 1" /sys/fs/cgroup/"${slice}"/"${unit}"/cgroup.events
+    echo "[ OK ]"
+
+    echo -n "  - thaw from outside: "
+    echo 0 > /sys/fs/cgroup/"${slice}"/cgroup.freeze
+    sleep 1
+
+    check_freezer_state "${unit}" "running"
+    check_freezer_state "${slice}" "running"
+    grep -q "frozen 0" /sys/fs/cgroup/"${slice}"/cgroup.events
+    grep -q "frozen 0" /sys/fs/cgroup/"${slice}"/"${unit}"/cgroup.events
+    echo "[ OK ]"
+
+    echo -n "  - thaw from outside while inner service is frozen: "
+    systemctl freeze "$unit"
+    check_freezer_state "${unit}" "frozen"
+    echo 1 > /sys/fs/cgroup/"${slice}"/cgroup.freeze
+    echo 0 > /sys/fs/cgroup/"${slice}"/cgroup.freeze
+    check_freezer_state "${slice}" "running"
+    check_freezer_state "${unit}" "frozen"
+    echo "[ OK ]"
+
+    systemctl stop "$unit"
+    systemctl stop "$slice"
+
+    echo
+}
+
+test -e /sys/fs/cgroup/system.slice/cgroup.freeze && {
+    start_test_service
+    test_dbus_api
+    test_systemctl
+    test_systemctl_show
+    test_jobs
+    test_recursive
+    test_preserve_state
+}
+
+echo OK > /testok
+exit 0
diff --git a/test/units/testsuite-39.service b/test/units/testsuite-39.service
new file mode 100644 (file)
index 0000000..395fe80
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=TEST-39-EXECRELOAD
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/testsuite-39.sh b/test/units/testsuite-39.sh
new file mode 100755 (executable)
index 0000000..eb7363f
--- /dev/null
@@ -0,0 +1,64 @@
+#!/usr/bin/env bash
+
+set -ex
+set -o pipefail
+
+systemd-analyze log-level debug
+systemd-analyze log-target console
+
+export SYSTEMD_PAGER=
+SERVICE_PATH="$(mktemp /etc/systemd/system/execreloadXXX.service)"
+SERVICE_NAME="${SERVICE_PATH##*/}"
+
+echo "[#1] Failing ExecReload= should not kill the service"
+cat > "$SERVICE_PATH" << EOF
+[Service]
+ExecStart=/bin/sleep infinity
+ExecReload=/bin/false
+EOF
+
+systemctl daemon-reload
+systemctl start $SERVICE_NAME
+systemctl status $SERVICE_NAME
+# The reload SHOULD fail but SHOULD NOT affect the service state
+! systemctl reload $SERVICE_NAME
+systemctl status $SERVICE_NAME
+systemctl stop $SERVICE_NAME
+
+
+echo "[#2] Failing ExecReload= should not kill the service (multiple ExecReload=)"
+cat > "$SERVICE_PATH" << EOF
+[Service]
+ExecStart=/bin/sleep infinity
+ExecReload=/bin/true
+ExecReload=/bin/false
+ExecReload=/bin/true
+EOF
+
+systemctl daemon-reload
+systemctl start $SERVICE_NAME
+systemctl status $SERVICE_NAME
+# The reload SHOULD fail but SHOULD NOT affect the service state
+! systemctl reload $SERVICE_NAME
+systemctl status $SERVICE_NAME
+systemctl stop $SERVICE_NAME
+
+echo "[#3] Failing ExecReload=- should not affect reload's exit code"
+cat > "$SERVICE_PATH" << EOF
+[Service]
+ExecStart=/bin/sleep infinity
+ExecReload=-/bin/false
+EOF
+
+systemctl daemon-reload
+systemctl start $SERVICE_NAME
+systemctl status $SERVICE_NAME
+systemctl reload $SERVICE_NAME
+systemctl status $SERVICE_NAME
+systemctl stop $SERVICE_NAME
+
+systemd-analyze log-level info
+
+echo OK > /testok
+
+exit 0
diff --git a/test/units/testsuite-40.service b/test/units/testsuite-40.service
new file mode 100644 (file)
index 0000000..38b0bd8
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=TEST-40-EXEC-COMMAND-EX
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/testsuite-40.sh b/test/units/testsuite-40.sh
new file mode 100755 (executable)
index 0000000..957d220
--- /dev/null
@@ -0,0 +1,46 @@
+#!/usr/bin/env bash
+set -ex
+set -o pipefail
+
+systemd-analyze log-level debug
+systemd-analyze log-target console
+
+declare -A property
+
+property[1_one]=ExecCondition
+property[2_two]=ExecStartPre
+property[3_three]=ExecStart
+property[4_four]=ExecStartPost
+property[5_five]=ExecReload
+property[6_six]=ExecStop
+property[7_seven]=ExecStopPost
+
+# These should all get upgraded to the corresponding Ex property as the non-Ex variant
+# does not support the ":" prefix (no-env-expand).
+for c in "${!property[@]}"; do
+    systemd-run --unit="$c" -r -p "Type=oneshot" -p "${property[$c]}=:/bin/echo \${$c}" /bin/true
+    systemctl show -p "${property[$c]}" "$c" | grep -F "path=/bin/echo ; argv[]=/bin/echo \${$c} ; ignore_errors=no"
+    systemctl show -p "${property[$c]}Ex" "$c" | grep -F "path=/bin/echo ; argv[]=/bin/echo \${$c} ; flags=no-env-expand"
+done
+
+declare -A property_ex
+
+property_ex[1_one_ex]=ExecConditionEx
+property_ex[2_two_ex]=ExecStartPreEx
+property_ex[3_three_ex]=ExecStartEx
+property_ex[4_four_ex]=ExecStartPostEx
+property_ex[5_five_ex]=ExecReloadEx
+property_ex[6_six_ex]=ExecStopEx
+property_ex[7_seven_ex]=ExecStopPostEx
+
+for c in "${!property_ex[@]}"; do
+    systemd-run --unit="$c" -r -p "Type=oneshot" -p "${property_ex[$c]}=:/bin/echo \${$c}" /bin/true
+    systemctl show -p "${property_ex[$c]%??}" "$c" | grep -F "path=/bin/echo ; argv[]=/bin/echo \${$c} ; ignore_errors=no"
+    systemctl show -p "${property_ex[$c]}" "$c" | grep -F "path=/bin/echo ; argv[]=/bin/echo \${$c} ; flags=no-env-expand"
+done
+
+systemd-analyze log-level info
+
+echo OK > /testok
+
+exit 0
diff --git a/test/units/testsuite-41.service b/test/units/testsuite-41.service
new file mode 100644 (file)
index 0000000..766cb4c
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=TEST-41-ONESHOT-RESTART
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/testsuite-41.sh b/test/units/testsuite-41.sh
new file mode 100755 (executable)
index 0000000..81fa171
--- /dev/null
@@ -0,0 +1,49 @@
+#!/usr/bin/env bash
+set -ex
+set -o pipefail
+
+# wait this many secs for each test service to succeed in what is being tested
+MAX_SECS=60
+
+systemd-analyze log-level debug
+systemd-analyze log-target console
+
+# test one: Restart=on-failure should restart the service
+! systemd-run --unit=one -p Type=oneshot -p Restart=on-failure /bin/bash -c "exit 1"
+
+for ((secs=0; secs<$MAX_SECS; secs++)); do
+  [[ "$(systemctl show one.service -P NRestarts)" -le 0 ]] || break
+  sleep 1
+done
+if [[ "$(systemctl show one.service -P NRestarts)" -le 0 ]]; then
+  exit 1
+fi
+
+TMP_FILE="/tmp/test-41-oneshot-restart-test"
+
+: >$TMP_FILE
+
+# test two: make sure StartLimitBurst correctly limits the number of restarts
+# and restarts execution of the unit from the first ExecStart=
+! systemd-run --unit=two -p StartLimitIntervalSec=120 -p StartLimitBurst=3 -p Type=oneshot -p Restart=on-failure -p ExecStart="/bin/bash -c \"printf a >>  $TMP_FILE\"" /bin/bash -c "exit 1"
+
+# wait for at least 3 restarts
+for ((secs=0; secs<$MAX_SECS; secs++)); do
+  [[ $(cat $TMP_FILE) != "aaa" ]] || break
+  sleep 1
+done
+if [[ $(cat $TMP_FILE) != "aaa" ]]; then
+  exit 1
+fi
+
+# wait for 5 more seconds to make sure there aren't excess restarts
+sleep 5
+if [[ $(cat $TMP_FILE) != "aaa" ]]; then
+  exit 1
+fi
+
+systemd-analyze log-level info
+
+echo OK > /testok
+
+exit 0
diff --git a/test/units/testsuite-42.service b/test/units/testsuite-42.service
new file mode 100644 (file)
index 0000000..a5504b5
--- /dev/null
@@ -0,0 +1,9 @@
+[Unit]
+Description=TEST-42-EXECSTOPPOST
+Before=getty-pre.target
+Wants=getty-pre.target
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/testsuite-42.sh b/test/units/testsuite-42.sh
new file mode 100755 (executable)
index 0000000..154398d
--- /dev/null
@@ -0,0 +1,89 @@
+#!/usr/bin/env bash
+set -ex
+
+systemd-analyze log-level debug
+
+systemd-run --unit=simple1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=simple -p ExecStopPost='/bin/touch /run/simple1' true
+test -f /run/simple1
+
+! systemd-run --unit=simple2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=simple -p ExecStopPost='/bin/touch /run/simple2' false
+test -f /run/simple2
+
+systemd-run --unit=exec1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=exec -p ExecStopPost='/bin/touch /run/exec1' sleep 1
+test -f /run/exec1
+
+! systemd-run --unit=exec2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=exec -p ExecStopPost='/bin/touch /run/exec2' sh -c 'sleep 1; false'
+test -f /run/exec2
+
+cat > /tmp/forking1.sh <<EOF
+#!/usr/bin/env bash
+
+set -eux
+
+sleep 4 &
+MAINPID=\$!
+disown
+
+systemd-notify MAINPID=\$MAINPID
+EOF
+chmod +x /tmp/forking1.sh
+
+systemd-run --unit=forking1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=forking -p NotifyAccess=exec -p ExecStopPost='/bin/touch /run/forking1' /tmp/forking1.sh
+test -f /run/forking1
+
+cat > /tmp/forking2.sh <<EOF
+#!/usr/bin/env bash
+
+set -eux
+
+( sleep 4; exit 1 ) &
+MAINPID=\$!
+disown
+
+systemd-notify MAINPID=\$MAINPID
+EOF
+chmod +x /tmp/forking2.sh
+
+! systemd-run --unit=forking2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=forking -p NotifyAccess=exec -p ExecStopPost='/bin/touch /run/forking2' /tmp/forking2.sh
+test -f /run/forking2
+
+systemd-run --unit=oneshot1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=oneshot -p ExecStopPost='/bin/touch /run/oneshot1' true
+test -f /run/oneshot1
+
+! systemd-run --unit=oneshot2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=oneshot -p ExecStopPost='/bin/touch /run/oneshot2' false
+test -f /run/oneshot2
+
+systemd-run --unit=dbus1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=dbus -p BusName=systemd.test.ExecStopPost -p ExecStopPost='/bin/touch /run/dbus1' \
+    busctl call org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus RequestName su systemd.test.ExecStopPost 4 \
+    || :
+test -f /run/dbus1
+
+! systemd-run --unit=dbus2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=dbus -p BusName=systemd.test.ExecStopPost -p ExecStopPost='/bin/touch /run/dbus2' true
+test -f /run/dbus2
+
+cat > /tmp/notify1.sh <<EOF
+#!/usr/bin/env bash
+
+set -eux
+
+systemd-notify --ready
+EOF
+chmod +x /tmp/notify1.sh
+
+systemd-run --unit=notify1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=notify -p ExecStopPost='/bin/touch /run/notify1' /tmp/notify1.sh
+test -f /run/notify1
+
+! systemd-run --unit=notify2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=notify -p ExecStopPost='/bin/touch /run/notify2' true
+test -f /run/notify2
+
+systemd-run --unit=idle1.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=idle -p ExecStopPost='/bin/touch /run/idle1' true
+test -f /run/idle1
+
+! systemd-run --unit=idle2.service --wait -p StandardOutput=tty -p StandardError=tty -p Type=idle -p ExecStopPost='/bin/touch /run/idle2' false
+test -f /run/idle2
+
+systemd-analyze log-level info
+
+echo OK > /testok
+
+exit 0
diff --git a/test/units/testsuite-43.service b/test/units/testsuite-43.service
new file mode 100644 (file)
index 0000000..31248f1
--- /dev/null
@@ -0,0 +1,9 @@
+[Unit]
+Description=TEST-43-PRIVATEUSER-UNPRIV
+After=systemd-logind.service user@4711.service
+Wants=user@4711.service
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/testsuite-43.sh b/test/units/testsuite-43.sh
new file mode 100755 (executable)
index 0000000..ec84868
--- /dev/null
@@ -0,0 +1,68 @@
+#!/usr/bin/env bash
+set -ex
+set -o pipefail
+
+systemd-analyze log-level debug
+
+runas() {
+    declare userid=$1
+    shift
+    su "$userid" -s /bin/sh -c 'XDG_RUNTIME_DIR=/run/user/$UID exec "$@"' -- sh "$@"
+}
+
+runas testuser systemd-run --wait --user --unit=test-private-users \
+    -p PrivateUsers=yes -P echo hello
+
+runas testuser systemd-run --wait --user --unit=test-private-tmp-innerfile \
+    -p PrivateUsers=yes -p PrivateTmp=yes \
+    -P touch /tmp/innerfile.txt
+# File should not exist outside the job's tmp directory.
+test ! -e /tmp/innerfile.txt
+
+touch /tmp/outerfile.txt
+# File should not appear in unit's private tmp.
+runas testuser systemd-run --wait --user --unit=test-private-tmp-outerfile \
+    -p PrivateUsers=yes -p PrivateTmp=yes \
+    -P test ! -e /tmp/outerfile.txt
+
+# Confirm that creating a file in home works
+runas testuser systemd-run --wait --user --unit=test-unprotected-home \
+    -P touch /home/testuser/works.txt
+test -e /home/testuser/works.txt
+
+# Confirm that creating a file in home is blocked under read-only
+runas testuser systemd-run --wait --user --unit=test-protect-home-read-only \
+    -p PrivateUsers=yes -p ProtectHome=read-only \
+    -P bash -c '
+        test -e /home/testuser/works.txt
+        ! touch /home/testuser/blocked.txt
+    '
+test ! -e /home/testuser/blocked.txt
+
+# Check that tmpfs hides the whole directory
+runas testuser systemd-run --wait --user --unit=test-protect-home-tmpfs \
+    -p PrivateUsers=yes -p ProtectHome=tmpfs \
+    -P test ! -e /home/testuser
+
+# Confirm that home, /root, and /run/user are inaccessible under "yes"
+runas testuser systemd-run --wait --user --unit=test-protect-home-yes \
+    -p PrivateUsers=yes -p ProtectHome=yes \
+    -P bash -c '
+        test "$(stat -c %a /home)" = "0"
+        test "$(stat -c %a /root)" = "0"
+        test "$(stat -c %a /run/user)" = "0"
+    '
+
+# Confirm we cannot change groups because we only have one mapping in the user
+# namespace (no CAP_SETGID in the parent namespace to write the additional
+# mapping of the user supplied group and thus cannot change groups to an
+# unmapped group ID)
+! runas testuser systemd-run --wait --user --unit=test-group-fail \
+    -p PrivateUsers=yes -p Group=daemon \
+    -P true
+
+systemd-analyze log-level info
+
+echo OK > /testok
+
+exit 0
diff --git a/test/units/testsuite-44.service b/test/units/testsuite-44.service
new file mode 100644 (file)
index 0000000..bd4dd72
--- /dev/null
@@ -0,0 +1,12 @@
+[Unit]
+Description=TESTSUITE-44-LOG-NAMESPACE
+Before=getty-pre.target
+Wants=getty-pre.target
+Wants=systemd-journald@foobar.socket systemd-journald-varlink@foobar.socket
+After=systemd-journald@foobar.socket systemd-journald-varlink@foobar.socket
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
+LogTarget=foobar
diff --git a/test/units/testsuite-44.sh b/test/units/testsuite-44.sh
new file mode 100755 (executable)
index 0000000..9754163
--- /dev/null
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+set -ex
+
+systemd-analyze log-level debug
+
+systemd-run -p LogNamespace=foobar echo "hello world"
+
+journalctl --namespace=foobar --sync
+journalctl --namespace=foobar > /tmp/hello-world
+journalctl > /tmp/no-hello-world
+
+grep "hello world" /tmp/hello-world
+! grep "hello world" /tmp/no-hello-world
+
+systemd-analyze log-level info
+
+echo OK > /testok
+
+exit 0
diff --git a/test/units/testsuite-46.service b/test/units/testsuite-46.service
new file mode 100644 (file)
index 0000000..7698f35
--- /dev/null
@@ -0,0 +1,12 @@
+[Unit]
+Description=TEST-46-HOMED
+Wants=getty-pre.target
+Before=getty-pre.target
+Wants=systemd-homed.service
+After=systemd-homed.service
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
+NotifyAccess=all
diff --git a/test/units/testsuite-46.sh b/test/units/testsuite-46.sh
new file mode 100755 (executable)
index 0000000..00bbdf5
--- /dev/null
@@ -0,0 +1,81 @@
+#!/usr/bin/env bash
+set -ex
+set -o pipefail
+
+# Check if homectl is installed, and if it isn't bail out early instead of failing
+if ! test -x /usr/bin/homectl ; then
+        echo OK > /testok
+        exit 0
+fi
+
+inspect() {
+        # As updating disk-size-related attributes can take some time on
+        # some filesystems, let's drop these fields before comparing the
+        # outputs to avoid unexpected fails. To see the full outputs of both
+        # homectl & userdbctl (for debugging purposes) drop the fields just
+        # before the comparison.
+        homectl inspect $1 | tee /tmp/a
+        userdbctl user $1 | tee /tmp/b
+
+        local PATTERN='/^\s*Disk (Size|Free|Floor|Ceiling):/d'
+        diff <(sed -r "$PATTERN" /tmp/a) <(sed -r "$PATTERN" /tmp/b)
+        rm /tmp/a /tmp/b
+}
+
+systemd-analyze log-level debug
+systemd-analyze log-target console
+
+NEWPASSWORD=xEhErW0ndafV4s homectl create test-user --disk-size=20M
+inspect test-user
+
+PASSWORD=xEhErW0ndafV4s homectl authenticate test-user
+
+PASSWORD=xEhErW0ndafV4s homectl activate test-user
+inspect test-user
+
+PASSWORD=xEhErW0ndafV4s homectl update test-user --real-name="Inline test"
+inspect test-user
+
+homectl deactivate test-user
+inspect test-user
+
+PASSWORD=xEhErW0ndafV4s NEWPASSWORD=yPN4N0fYNKUkOq homectl passwd test-user
+inspect test-user
+
+PASSWORD=yPN4N0fYNKUkOq homectl activate test-user
+inspect test-user
+
+SYSTEMD_LOG_LEVEL=debug PASSWORD=yPN4N0fYNKUkOq NEWPASSWORD=xEhErW0ndafV4s homectl passwd test-user
+inspect test-user
+
+homectl deactivate test-user
+inspect test-user
+
+PASSWORD=xEhErW0ndafV4s homectl activate test-user
+inspect test-user
+
+PASSWORD=xEhErW0ndafV4s homectl deactivate test-user
+inspect test-user
+
+PASSWORD=xEhErW0ndafV4s homectl update test-user --real-name="Offline test"
+inspect test-user
+
+PASSWORD=xEhErW0ndafV4s homectl activate test-user
+inspect test-user
+
+PASSWORD=xEhErW0ndafV4s homectl deactivate test-user
+inspect test-user
+
+! PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz
+PASSWORD=xEhErW0ndafV4s homectl with test-user -- touch /home/test-user/xyz
+PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz
+PASSWORD=xEhErW0ndafV4s homectl with test-user -- rm /home/test-user/xyz
+! PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz
+
+homectl remove test-user
+
+systemd-analyze log-level info
+
+echo OK > /testok
+
+exit 0
diff --git a/test/units/testsuite-47-repro.service b/test/units/testsuite-47-repro.service
new file mode 100644 (file)
index 0000000..655eea6
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=Issue 14566 Repro
+
+[Service]
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+ExecStopPost=/bin/true
+KillMode=mixed
diff --git a/test/units/testsuite-47-repro.sh b/test/units/testsuite-47-repro.sh
new file mode 100755 (executable)
index 0000000..8c34289
--- /dev/null
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+sleep infinity &
+echo $! > /leakedtestpid
+wait $!
diff --git a/test/units/testsuite-47.service b/test/units/testsuite-47.service
new file mode 100644 (file)
index 0000000..3816c57
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=TEST-47-ISSUE-14566
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/testsuite-47.sh b/test/units/testsuite-47.sh
new file mode 100755 (executable)
index 0000000..50034cf
--- /dev/null
@@ -0,0 +1,25 @@
+#!/usr/bin/env bash
+set -ex
+set -o pipefail
+
+systemd-analyze log-level debug
+systemd-analyze log-target console
+
+systemctl start testsuite-47-repro
+sleep 4
+systemctl status testsuite-47-repro
+
+leaked_pid=$(cat /leakedtestpid)
+
+systemctl stop testsuite-47-repro
+sleep 4
+
+# Leaked PID will still be around if we're buggy.
+# I personally prefer to see 42.
+ps -p "$leaked_pid" && exit 42
+
+systemd-analyze log-level info
+
+echo OK > /testok
+
+exit 0
diff --git a/test/units/testsuite-48.service b/test/units/testsuite-48.service
new file mode 100644 (file)
index 0000000..9dc50ab
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=TEST-48-START-STOP-NO-RELOAD
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/testsuite-48.sh b/test/units/testsuite-48.sh
new file mode 100755 (executable)
index 0000000..03231e7
--- /dev/null
@@ -0,0 +1,85 @@
+#!/usr/bin/env bash
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+set -ex
+
+cat > /run/systemd/system/testservice-48.target <<EOF
+[Unit]
+Wants=testservice-48.service
+EOF
+
+systemctl daemon-reload
+
+systemctl start testservice-48.target
+
+# The filesystem on the test image, despite being ext4, seems to have a mtime
+# granularity of one second, which means the manager's unit cache won't be
+# marked as dirty when writing the unit file, unless we wait at least a full
+# second after the previous daemon-reload.
+# May 07 23:12:20 systemd-testsuite testsuite-48.sh[30]: + cat
+# May 07 23:12:20 systemd-testsuite testsuite-48.sh[30]: + ls -l --full-time /etc/systemd/system/testservice-48.service
+# May 07 23:12:20 systemd-testsuite testsuite-48.sh[52]: -rw-r--r-- 1 root root 50 2020-05-07 23:12:20.000000000 +0100 /
+# May 07 23:12:20 systemd-testsuite testsuite-48.sh[30]: + stat -f --format=%t /etc/systemd/system/testservice-48.servic
+# May 07 23:12:20 systemd-testsuite testsuite-48.sh[53]: ef53
+sleep 3.1
+
+cat > /run/systemd/system/testservice-48.service <<EOF
+[Service]
+ExecStart=/bin/sleep infinity
+EOF
+
+systemctl start testservice-48.service
+
+systemctl is-active testservice-48.service
+
+# Stop and remove, and try again to exercise https://github.com/systemd/systemd/issues/15992
+systemctl stop testservice-48.service
+rm -f /run/systemd/system/testservice-48.service
+systemctl daemon-reload
+
+sleep 3.1
+
+cat > /run/systemd/system/testservice-48.service <<EOF
+[Service]
+ExecStart=/bin/sleep infinity
+EOF
+
+# Start a non-existing unit first, so that the cache is reloaded for an unrelated
+# reason. Starting the existing unit later should still work thanks to the check
+# for the last load attempt vs cache timestamp.
+systemctl start testservice-48-nonexistent.service || true
+
+systemctl start testservice-48.service
+
+systemctl is-active testservice-48.service
+
+# Stop and remove, and try again to exercise the transaction setup code path by
+# having the target pull in the unloaded but available unit
+systemctl stop testservice-48.service testservice-48.target
+rm -f /run/systemd/system/testservice-48.service /run/systemd/system/testservice-48.target
+systemctl daemon-reload
+
+sleep 3.1
+
+cat > /run/systemd/system/testservice-48.target <<EOF
+[Unit]
+Conflicts=shutdown.target
+Wants=testservice-48.service
+EOF
+
+systemctl daemon-reload
+
+systemctl start testservice-48.target
+
+cat > /run/systemd/system/testservice-48.service <<EOF
+[Service]
+ExecStart=/bin/sleep infinity
+EOF
+
+systemctl restart testservice-48.target
+
+systemctl is-active testservice-48.service
+
+echo OK > /testok
+
+exit 0
diff --git a/test/units/testsuite-49.service b/test/units/testsuite-49.service
new file mode 100644 (file)
index 0000000..f471771
--- /dev/null
@@ -0,0 +1,6 @@
+[Unit]
+Description=TEST-49-UDEV-EVENT-TIMEOUT
+
+[Service]
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/testsuite-49.sh b/test/units/testsuite-49.sh
new file mode 100755 (executable)
index 0000000..ffa9801
--- /dev/null
@@ -0,0 +1,47 @@
+#!/usr/bin/env bash
+
+set -ex
+
+test_rule="/run/udev/rules.d/49-test.rules"
+
+setup() {
+    mkdir -p "${test_rule%/*}"
+    cp -f /etc/udev/udev.conf /etc/udev/udev.conf.bckp
+    echo 'KERNEL=="lo", SUBSYSTEM=="net", PROGRAM=="/bin/sleep 60"' > "${test_rule}"
+    echo "event_timeout=30" >> /etc/udev/udev.conf
+    echo "timeout_signal=SIGABRT" >> /etc/udev/udev.conf
+
+    systemctl restart systemd-udevd.service
+}
+
+teardown() {
+    set +e
+
+    mv -f /etc/udev/udev.conf.bckp /etc/udev/udev.conf
+    rm -f "$test_rule"
+    systemctl restart systemd-udevd.service
+}
+
+run_test() {
+    since="$(date +%T)"
+
+    echo add > /sys/class/net/lo/uevent
+
+    for n in {1..20}; do
+        sleep 5
+        if coredumpctl --since "$since" --no-legend --no-pager | grep /bin/udevadm ; then
+            return 0
+        fi
+    done
+
+    return 1
+}
+
+trap teardown EXIT
+
+setup
+run_test
+
+echo OK > /testok
+
+exit 0
diff --git a/test/units/testsuite-50.service b/test/units/testsuite-50.service
new file mode 100644 (file)
index 0000000..5a10a64
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=TEST-50-DISSECT
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/testsuite-50.sh b/test/units/testsuite-50.sh
new file mode 100755 (executable)
index 0000000..81e48e0
--- /dev/null
@@ -0,0 +1,129 @@
+#!/usr/bin/env bash
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+set -ex
+set -o pipefail
+
+export SYSTEMD_LOG_LEVEL=debug
+
+cleanup()
+{
+    if [ -z "${image_dir}" ]; then
+        return
+    fi
+    rm -rf "${image_dir}"
+}
+
+cd /tmp
+
+image_dir="$(mktemp -d -t -p /tmp tmp.XXXXXX)"
+if [ -z "${image_dir}" ] || [ ! -d "${image_dir}" ]; then
+    echo "mktemp under /tmp failed"
+    exit 1
+fi
+
+trap cleanup EXIT
+
+cp /usr/share/minimal.* "${image_dir}/"
+image="${image_dir}/minimal"
+roothash="$(cat ${image}.roothash)"
+
+/usr/lib/systemd/systemd-dissect ${image}.raw | grep -q -F "Found read-only 'root' partition of type squashfs with verity"
+/usr/lib/systemd/systemd-dissect ${image}.raw | grep -q -F "MARKER=1"
+/usr/lib/systemd/systemd-dissect ${image}.raw | grep -q -F -f /usr/lib/os-release
+
+mv ${image}.verity ${image}.fooverity
+mv ${image}.roothash ${image}.foohash
+/usr/lib/systemd/systemd-dissect ${image}.raw --root-hash=${roothash} --verity-data=${image}.fooverity | grep -q -F "Found read-only 'root' partition of type squashfs with verity"
+/usr/lib/systemd/systemd-dissect ${image}.raw --root-hash=${roothash} --verity-data=${image}.fooverity | grep -q -F "MARKER=1"
+/usr/lib/systemd/systemd-dissect ${image}.raw --root-hash=${roothash} --verity-data=${image}.fooverity | grep -q -F -f /usr/lib/os-release
+mv ${image}.fooverity ${image}.verity
+mv ${image}.foohash ${image}.roothash
+
+mkdir -p ${image_dir}/mount ${image_dir}/mount2
+/usr/lib/systemd/systemd-dissect --mount ${image}.raw ${image_dir}/mount
+cat ${image_dir}/mount/usr/lib/os-release | grep -q -F -f /usr/lib/os-release
+cat ${image_dir}/mount/etc/os-release | grep -q -F -f /usr/lib/os-release
+cat ${image_dir}/mount/usr/lib/os-release | grep -q -F "MARKER=1"
+# Verity volume should be shared (opened only once)
+/usr/lib/systemd/systemd-dissect --mount ${image}.raw ${image_dir}/mount2
+verity_count=$(ls -1 /dev/mapper/ | grep -c verity)
+# In theory we should check that count is exactly one. In practice, libdevmapper
+# randomly and unpredictably fails with an unhelpful EINVAL when a device is open
+# (and even mounted and in use), so best-effort is the most we can do for now
+if [ ${verity_count} -lt 1 ]; then
+    echo "Verity device ${image}.raw not found in /dev/mapper/"
+    exit 1
+fi
+umount ${image_dir}/mount
+umount ${image_dir}/mount2
+
+systemd-run -t --property RootImage=${image}.raw /usr/bin/cat /usr/lib/os-release | grep -q -F "MARKER=1"
+mv ${image}.verity ${image}.fooverity
+mv ${image}.roothash ${image}.foohash
+systemd-run -t --property RootImage=${image}.raw --property RootHash=${image}.foohash --property RootVerity=${image}.fooverity /usr/bin/cat /usr/lib/os-release | grep -q -F "MARKER=1"
+systemd-run -t --property RootImage=${image}.raw --property RootHash=${roothash} --property RootVerity=${image}.fooverity /usr/bin/cat /usr/lib/os-release | grep -q -F "MARKER=1"
+mv ${image}.fooverity ${image}.verity
+mv ${image}.foohash ${image}.roothash
+
+# Make a GPT disk on the fly, with the squashfs as partition 1 and the verity hash tree as partition 2
+machine="$(uname -m)"
+if [ "${machine}" = "x86_64" ]; then
+    root_guid=4f68bce3-e8cd-4db1-96e7-fbcaf984b709
+    verity_guid=2c7357ed-ebd2-46d9-aec1-23d437ec2bf5
+elif [ "${machine}" = "i386" ] || [ "${machine}" = "i686" ] || [ "${machine}" = "x86" ]; then
+    root_guid=44479540-f297-41b2-9af7-d131d5f0458a
+    verity_guid=d13c5d3b-b5d1-422a-b29f-9454fdc89d76
+elif [ "${machine}" = "aarch64" ] || [ "${machine}" = "aarch64_be" ] || [ "${machine}" = "armv8b" ] || [ "${machine}" = "armv8l" ]; then
+    root_guid=b921b045-1df0-41c3-af44-4c6f280d3fae
+    verity_guid=df3300ce-d69f-4c92-978c-9bfb0f38d820
+elif [ "${machine}" = "arm" ]; then
+    root_guid=69dad710-2ce4-4e3c-b16c-21a1d49abed3
+    verity_guid=7386cdf2-203c-47a9-a498-f2ecce45a2d6
+elif [ "${machine}" = "ia64" ]; then
+    root_guid=993d8d3d-f80e-4225-855a-9daf8ed7ea97
+    verity_guid=86ed10d5-b607-45bb-8957-d350f23d0571
+else
+    echo "Unexpected uname -m: ${machine} in testsuite-50.sh, please fix me"
+    exit 1
+fi
+# du rounds up to block size, which is more helpful for partitioning
+root_size="$(du -k ${image}.raw | cut -f1)"
+verity_size="$(du -k ${image}.verity | cut -f1)"
+# 4MB seems to be the minimum size blkid will accept, below that probing fails
+dd if=/dev/zero of=${image}.gpt bs=512 count=$((8192+${root_size}*2+${verity_size}*2))
+# sfdisk seems unhappy if the size overflows into the next unit, eg: 1580KiB will be interpreted as 1MiB
+# so do some basic rounding up if the minimal image is more than 1 MB
+if [ ${root_size} -ge 1024 ]; then
+    root_size="$((${root_size}/1024 + 1))MiB"
+else
+    root_size="${root_size}KiB"
+fi
+verity_size="${verity_size}KiB"
+uuid="$(head -c 32 ${image}.roothash | cut -c -8)-$(head -c 32 ${image}.roothash | cut -c 9-12)-$(head -c 32 ${image}.roothash | cut -c 13-16)-$(head -c 32 ${image}.roothash | cut -c 17-20)-$(head -c 32 ${image}.roothash | cut -c 21-)"
+echo -e "label: gpt\nsize=${root_size}, type=${root_guid}, uuid=${uuid}" | sfdisk ${image}.gpt
+uuid="$(tail -c 32 ${image}.roothash | cut -c -8)-$(tail -c 32 ${image}.roothash | cut -c 9-12)-$(tail -c 32 ${image}.roothash | cut -c 13-16)-$(tail -c 32 ${image}.roothash | cut -c 17-20)-$(tail -c 32 ${image}.roothash | cut -c 21-)"
+echo -e "size=${verity_size}, type=${verity_guid}, uuid=${uuid}" | sfdisk ${image}.gpt --append
+sfdisk --part-label ${image}.gpt 1 "Root Partition"
+sfdisk --part-label ${image}.gpt 2 "Verity Partition"
+loop="$(losetup --show -P -f ${image}.gpt)"
+dd if=${image}.raw of=${loop}p1
+dd if=${image}.verity of=${loop}p2
+losetup -d ${loop}
+
+/usr/lib/systemd/systemd-dissect --root-hash ${roothash} ${image}.gpt | grep -q "Found read-only 'root' partition (UUID $(head -c 32 ${image}.roothash)) of type squashfs for .* with verity on partition #1"
+/usr/lib/systemd/systemd-dissect --root-hash ${roothash} ${image}.gpt | grep -q "Found read-only 'root-verity' partition (UUID $(tail -c 32 ${image}.roothash)) of type DM_verity_hash for .* on partition #2"
+/usr/lib/systemd/systemd-dissect --root-hash ${roothash} ${image}.gpt | grep -q -F "MARKER=1"
+/usr/lib/systemd/systemd-dissect --root-hash ${roothash} ${image}.gpt | grep -q -F -f /usr/lib/os-release
+
+/usr/lib/systemd/systemd-dissect --root-hash ${roothash} --mount ${image}.gpt ${image_dir}/mount
+cat ${image_dir}/mount/usr/lib/os-release | grep -q -F -f /usr/lib/os-release
+cat ${image_dir}/mount/etc/os-release | grep -q -F -f /usr/lib/os-release
+cat ${image_dir}/mount/usr/lib/os-release | grep -q -F "MARKER=1"
+umount ${image_dir}/mount
+
+systemd-run -t --property RootImage=${image}.gpt --property RootHash=${roothash} /usr/bin/cat /usr/lib/os-release | grep -q -F "MARKER=1"
+
+echo OK > /testok
+
+exit 0
diff --git a/test/units/testsuite-51-repro-1.service b/test/units/testsuite-51-repro-1.service
new file mode 100644 (file)
index 0000000..96ecabe
--- /dev/null
@@ -0,0 +1,9 @@
+[Unit]
+Description=Issue 16115 Repro with on-abnormal
+
+[Service]
+Type=simple
+Restart=on-abnormal
+ExecCondition=/bin/false
+ExecStart=sleep 100
+RestartSec=1
diff --git a/test/units/testsuite-51-repro-2.service b/test/units/testsuite-51-repro-2.service
new file mode 100644 (file)
index 0000000..6015ad8
--- /dev/null
@@ -0,0 +1,9 @@
+[Unit]
+Description=Issue 16115 Repro with on-failure
+
+[Service]
+Type=simple
+Restart=on-failure
+ExecCondition=/bin/false
+ExecStart=sleep 100
+RestartSec=1
diff --git a/test/units/testsuite-51.service b/test/units/testsuite-51.service
new file mode 100644 (file)
index 0000000..903dc9a
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=TEST-51-ISSUE-16115
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/testsuite-51.sh b/test/units/testsuite-51.sh
new file mode 100755 (executable)
index 0000000..246412a
--- /dev/null
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+set -ex
+set -o pipefail
+
+systemctl start testsuite-51-repro-1
+systemctl start testsuite-51-repro-2
+sleep 5 # wait a bit in case there are restarts so we can count them below
+
+[[ "$(systemctl show testsuite-51-repro-1 -P NRestarts)" == "0" ]]
+[[ "$(systemctl show testsuite-51-repro-2 -P NRestarts)" == "0" ]]
+
+touch /testok
diff --git a/test/units/testsuite-53.service b/test/units/testsuite-53.service
new file mode 100644 (file)
index 0000000..d4dd8cc
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=TEST-53-ISSUE-16347
+
+[Service]
+ExecStartPre=rm -f /failed /testok
+ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
+Type=oneshot
diff --git a/test/units/testsuite-53.sh b/test/units/testsuite-53.sh
new file mode 100755 (executable)
index 0000000..3536c24
--- /dev/null
@@ -0,0 +1,30 @@
+#!/usr/bin/env bash
+set -ex
+set -o pipefail
+
+>/failed
+
+# Reset host date to current time, 3 days in the past.
+date -s "-3 days"
+
+# Run a timer for every 15 minutes.
+systemd-run --unit test-timer --on-calendar "*:0/15:0" true
+
+next_elapsed=$(systemctl show test-timer.timer -p NextElapseUSecRealtime --value)
+next_elapsed=$(date -d "${next_elapsed}" +%s)
+now=$(date +%s)
+time_delta=$((next_elapsed - now))
+
+# Check that the timer will elapse in less than 20 minutes.
+((0 < time_delta && time_delta < 1200)) || {
+    echo 'Timer elapse outside of the expected 20 minute window.'
+    echo "  next_elapsed=${next_elapsed}"
+    echo "  now=${now}"
+    echo "  time_delta=${time_delta}"
+    echo ''
+} >>/failed
+
+if test ! -s /failed ; then
+    rm -f /failed
+    touch /testok
+fi
diff --git a/test/units/testsuite.target b/test/units/testsuite.target
new file mode 100644 (file)
index 0000000..1a7e5b3
--- /dev/null
@@ -0,0 +1,6 @@
+[Unit]
+Description=Testsuite target
+Requires=multi-user.target
+After=multi-user.target
+Conflicts=rescue.target
+AllowIsolate=yes
diff --git a/test/units/timers.target b/test/units/timers.target
new file mode 100644 (file)
index 0000000..b1aa8c7
--- /dev/null
@@ -0,0 +1,15 @@
+#  SPDX-License-Identifier: LGPL-2.1+
+#
+#  This file is part of systemd.
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU Lesser General Public License as published by
+#  the Free Software Foundation; either version 2.1 of the License, or
+#  (at your option) any later version.
+
+[Unit]
+Description=Timers
+Documentation=man:systemd.special(7)
+
+DefaultDependencies=no
+Conflicts=shutdown.target
diff --git a/test/units/unit-.service.d/10-override.conf b/test/units/unit-.service.d/10-override.conf
new file mode 100644 (file)
index 0000000..916737d
--- /dev/null
@@ -0,0 +1,2 @@
+[Unit]
+Description=override0
diff --git a/test/units/unit-with-.service.d/20-override.conf b/test/units/unit-with-.service.d/20-override.conf
new file mode 100644 (file)
index 0000000..c6c2438
--- /dev/null
@@ -0,0 +1,2 @@
+[Unit]
+Documentation=man:override1
diff --git a/test/units/unit-with-multiple-.service.d/20-override.conf b/test/units/unit-with-multiple-.service.d/20-override.conf
new file mode 100644 (file)
index 0000000..62fafd2
--- /dev/null
@@ -0,0 +1,2 @@
+[Unit]
+Documentation=man:override2
diff --git a/test/units/unit-with-multiple-.service.d/30-override.conf b/test/units/unit-with-multiple-.service.d/30-override.conf
new file mode 100644 (file)
index 0000000..b9616da
--- /dev/null
@@ -0,0 +1,2 @@
+[Unit]
+Documentation=man:override3
diff --git a/test/units/unit-with-multiple-dashes.service b/test/units/unit-with-multiple-dashes.service
new file mode 100644 (file)
index 0000000..b38b360
--- /dev/null
@@ -0,0 +1,6 @@
+[Unit]
+Description=A unit with multiple dashes
+Documentation=man:test
+
+[Service]
+ExecStart=/bin/true
diff --git a/test/units/unit-with-multiple-dashes.service.d/10-override.conf b/test/units/unit-with-multiple-dashes.service.d/10-override.conf
new file mode 100644 (file)
index 0000000..982c362
--- /dev/null
@@ -0,0 +1,2 @@
+[Unit]
+Description=override4
diff --git a/test/units/unstoppable.service b/test/units/unstoppable.service
new file mode 100644 (file)
index 0000000..56b72c9
--- /dev/null
@@ -0,0 +1,5 @@
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart=/bin/echo "I'm unstoppable!"
+ExecStop=/bin/systemctl start --no-block unstoppable.service
diff --git a/test/unstoppable.service b/test/unstoppable.service
deleted file mode 100644 (file)
index 56b72c9..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-[Service]
-Type=oneshot
-RemainAfterExit=yes
-ExecStart=/bin/echo "I'm unstoppable!"
-ExecStop=/bin/systemctl start --no-block unstoppable.service
index e77f46d06b77fbe3fbe2514db07d60849c6e9f0a..0a9582d8b96456d7c0d19b55769fe1efd603915b 100644 (file)
@@ -8,6 +8,7 @@ tmpfiles = [['home.conf',            ''],
             ['systemd-nspawn.conf',  'ENABLE_MACHINED'],
             ['systemd-tmp.conf',     ''],
             ['portables.conf',       'ENABLE_PORTABLED'],
+            ['systemd-pstore.conf',  'ENABLE_PSTORE'],
             ['tmp.conf',             ''],
             ['x11.conf',             ''],
             ['legacy.conf',          'HAVE_SYSV_COMPAT'],
diff --git a/tmpfiles.d/systemd-pstore.conf b/tmpfiles.d/systemd-pstore.conf
new file mode 100644 (file)
index 0000000..8b6a1da
--- /dev/null
@@ -0,0 +1,29 @@
+# SPDX-License-Identifier: LGPL-2.1+
+#
+# The systemd-pstore.service(1) archives the contents of /sys/fs/pstore
+# upon boot so that there is room for a subsequent dump. This service
+# is enabled with:
+#  systemctl enable systemd-pstore
+#
+# With the service enabled, the kernel still needs to be configured
+# to write data into the pstore. The kernel has two parameters,
+# crash_kexec_post_notifiers and printk.always_kmsg_dump, that
+# control writes into pstore.
+#
+# The crash_kexec_post_notifiers parameter enables the kernel to write
+# dmesg (including stack trace) into pstore upon a panic, and
+# printk.always_kmsg_dump parameter enables the kernel to write dmesg
+# upon a normal shutdown (shutdown, reboot, halt).
+#
+# To configure the kernel parameters, uncomment the appropriate
+# line(s) below. The value written is either 'Y' to enable the
+# kernel parameter, or 'N' to disable the kernel parameter.
+#
+# After making a change to this file, do:
+#  systemd-tmpfiles --create path/to/tmpfiles.d/systemd-pstore.conf
+#
+# These changes are automatically applied on future re-boots.
+
+d /var/lib/systemd/pstore 0755 root root 14d
+#w- /sys/module/printk/parameters/always_kmsg_dump - - - - Y
+w- /sys/module/kernel/parameters/crash_kexec_post_notifiers - - - - Y
diff --git a/tools/autosuspend-update.sh b/tools/autosuspend-update.sh
new file mode 100755 (executable)
index 0000000..a4f99eb
--- /dev/null
@@ -0,0 +1,7 @@
+#!/bin/sh
+set -eu
+
+cd "$1"
+
+(curl -L 'https://chromium.googlesource.com/chromiumos/platform2/+/master/power_manager/udev/gen_autosuspend_rules.py?format=TEXT'; echo) \
+    | base64 -d > gen_autosuspend_rules.py
diff --git a/tools/check-api-docs.sh b/tools/check-api-docs.sh
new file mode 100755 (executable)
index 0000000..1094101
--- /dev/null
@@ -0,0 +1,42 @@
+#!/bin/sh
+set -eu
+
+sd_good=0
+sd_total=0
+udev_good=0
+udev_total=0
+
+deprecated="
+    -e sd_bus_try_close
+    -e sd_bus_process_priority
+    -e sd_bus_message_get_priority
+    -e sd_bus_message_set_priority
+    -e sd_seat_can_multi_session
+    -e sd_journal_open_container
+"
+
+for symbol in `nm -g --defined-only "$@" | grep " T " | cut -d" " -f3 | grep -wv $deprecated | sort -u` ; do
+    if test -f ${MESON_BUILD_ROOT}/man/$symbol.3 ; then
+        echo "✓ Symbol $symbol() is documented."
+        good=1
+    else
+        printf "  \x1b[1;31mSymbol $symbol() lacks documentation.\x1b[0m\n"
+        good=0
+    fi
+
+    case $symbol in
+        sd_*)
+            ((sd_good+=good))
+            ((sd_total+=1))
+            ;;
+        udev_*)
+            ((udev_good+=good))
+            ((udev_total+=1))
+            ;;
+        *)
+            echo 'unknown symbol prefix'
+            exit 1
+    esac
+done
+
+echo "libsystemd: $sd_good/$sd_total libudev: $udev_good/$udev_total"
diff --git a/tools/check-compilation.sh b/tools/check-compilation.sh
new file mode 100755 (executable)
index 0000000..ce39e16
--- /dev/null
@@ -0,0 +1,4 @@
+#!/bin/sh
+set -eu
+
+"$@" '-' -o/dev/null </dev/null
diff --git a/tools/check-help.sh b/tools/check-help.sh
new file mode 100755 (executable)
index 0000000..efe7ed4
--- /dev/null
@@ -0,0 +1,29 @@
+#!/bin/sh
+set -eu
+
+export SYSTEMD_LOG_LEVEL=info
+
+# output width
+if "$1"  --help | grep -v 'default:' | grep -E -q '.{80}.'; then
+    echo "$(basename "$1") --help output is too wide:"
+    "$1"  --help | awk 'length > 80' | grep -E --color=yes '.{80}'
+    exit 1
+fi
+
+# --help prints something. Also catches case where args are ignored.
+if ! "$1"  --help | grep -q .; then
+    echo "$(basename "$1") --help output is empty."
+    exit 2
+fi
+
+# no --help output to stdout
+if "$1" --help 2>&1 1>/dev/null | grep .; then
+    echo "$(basename "$1") --help prints to stderr"
+    exit 3
+fi
+
+# error output to stderr
+if ! "$1" --no-such-parameter 2>&1 1>/dev/null | grep -q .; then
+    echo "$(basename "$1") with an unknown parameter does not print to stderr"
+    exit 4
+fi
index 4e8593f320099b96c568baee3eff57ad762f0e77..66018a54fd4b6fe9d4ae35bd012023948e4bd8e0 100644 (file)
@@ -1,79 +1,77 @@
 #!/usr/bin/env python3
 # SPDX-License-Identifier: LGPL-2.1+
 
-from __future__ import print_function
-
 import gdb
 
 class sd_dump_hashmaps(gdb.Command):
-        "dump systemd's hashmaps"
-
-        def __init__(self):
-                super(sd_dump_hashmaps, self).__init__("sd_dump_hashmaps", gdb.COMMAND_DATA, gdb.COMPLETE_NONE)
-
-        def invoke(self, arg, from_tty):
-                d = gdb.parse_and_eval("hashmap_debug_list")
-                all_entry_sizes = gdb.parse_and_eval("all_entry_sizes")
-                all_direct_buckets = gdb.parse_and_eval("all_direct_buckets")
-                uchar_t = gdb.lookup_type("unsigned char")
-                ulong_t = gdb.lookup_type("unsigned long")
-                debug_offset = gdb.parse_and_eval("(unsigned long)&((HashmapBase*)0)->debug")
-
-                print("type, hash, indirect, entries, max_entries, buckets, creator")
-                while d:
-                        h = gdb.parse_and_eval("(HashmapBase*)((char*)%d - %d)" % (int(d.cast(ulong_t)), debug_offset))
-
-                        if h["has_indirect"]:
-                                storage_ptr = h["indirect"]["storage"].cast(uchar_t.pointer())
-                                n_entries = h["indirect"]["n_entries"]
-                                n_buckets = h["indirect"]["n_buckets"]
-                        else:
-                                storage_ptr = h["direct"]["storage"].cast(uchar_t.pointer())
-                                n_entries = h["n_direct_entries"]
-                                n_buckets = all_direct_buckets[int(h["type"])];
-
-                        t = ["plain", "ordered", "set"][int(h["type"])]
-
-                        print("{}, {}, {}, {}, {}, {}, {} ({}:{})".format(t, h["hash_ops"], bool(h["has_indirect"]), n_entries, d["max_entries"], n_buckets, d["func"], d["file"], d["line"]))
-
-                        if arg != "" and n_entries > 0:
-                                dib_raw_addr = storage_ptr + (all_entry_sizes[h["type"]] * n_buckets)
-
-                                histogram = {}
-                                for i in xrange(0, n_buckets):
-                                        dib = int(dib_raw_addr[i])
-                                        histogram[dib] = histogram.get(dib, 0) + 1
-
-                                for dib in sorted(iter(histogram)):
-                                        if dib != 255:
-                                                print("{:>3} {:>8} {} of entries".format(dib, histogram[dib], 100.0*histogram[dib]/n_entries))
-                                        else:
-                                                print("{:>3} {:>8} {} of slots".format(dib, histogram[dib], 100.0*histogram[dib]/n_buckets))
-                                print("mean DIB of entries: {}".format(sum([dib*histogram[dib] for dib in iter(histogram) if dib != 255])*1.0/n_entries))
-
-                                blocks = []
-                                current_len = 1
-                                prev = int(dib_raw_addr[0])
-                                for i in xrange(1, n_buckets):
-                                        dib = int(dib_raw_addr[i])
-                                        if (dib == 255) != (prev == 255):
-                                                if prev != 255:
-                                                        blocks += [[i, current_len]]
-                                                current_len = 1
-                                        else:
-                                                current_len += 1
-
-                                        prev = dib
-                                if prev != 255:
-                                        blocks += [[i, current_len]]
-                                # a block may be wrapped around
-                                if len(blocks) > 1 and blocks[0][0] == blocks[0][1] and blocks[-1][0] == n_buckets - 1:
-                                        blocks[0][1] += blocks[-1][1]
-                                        blocks = blocks[0:-1]
-                                print("max block: {}".format(max(blocks, key=lambda a: a[1])))
-                                print("sum block lens: {}".format(sum(b[1] for b in blocks)))
-                                print("mean block len: {}".format((1.0 * sum(b[1] for b in blocks) / len(blocks))))
-
-                        d = d["debug_list_next"]
+    "dump systemd's hashmaps"
+
+    def __init__(self):
+        super().__init__("sd_dump_hashmaps", gdb.COMMAND_DATA, gdb.COMPLETE_NONE)
+
+    def invoke(self, arg, from_tty):
+        d = gdb.parse_and_eval("hashmap_debug_list")
+        hashmap_type_info = gdb.parse_and_eval("hashmap_type_info")
+        uchar_t = gdb.lookup_type("unsigned char")
+        ulong_t = gdb.lookup_type("unsigned long")
+        debug_offset = gdb.parse_and_eval("(unsigned long)&((HashmapBase*)0)->debug")
+
+        print("type, hash, indirect, entries, max_entries, buckets, creator")
+        while d:
+            h = gdb.parse_and_eval(f"(HashmapBase*)((char*){int(d.cast(ulong_t))} - {debug_offset})")
+
+            if h["has_indirect"]:
+                storage_ptr = h["indirect"]["storage"].cast(uchar_t.pointer())
+                n_entries = h["indirect"]["n_entries"]
+                n_buckets = h["indirect"]["n_buckets"]
+            else:
+                storage_ptr = h["direct"]["storage"].cast(uchar_t.pointer())
+                n_entries = h["n_direct_entries"]
+                n_buckets = hashmap_type_info[h["type"]]["n_direct_buckets"]
+
+            t = ["plain", "ordered", "set"][int(h["type"])]
+
+            print(f'{t}, {h["hash_ops"]}, {bool(h["has_indirect"])}, {n_entries}, {d["max_entries"]}, {n_buckets}, {d["func"].string()}, {d["file"].string()}:{d["line"]}')
+
+            if arg != "" and n_entries > 0:
+                dib_raw_addr = storage_ptr + hashmap_type_info[h["type"]]["entry_size"] * n_buckets
+
+                histogram = {}
+                for i in range(0, n_buckets):
+                    dib = int(dib_raw_addr[i])
+                    histogram[dib] = histogram.get(dib, 0) + 1
+
+                for dib in sorted(histogram):
+                    if dib != 255:
+                        print(f"{dib:>3} {histogram[dib]:>8} {float(histogram[dib]/n_entries):.0%} of entries")
+                    else:
+                        print(f"{dib:>3} {histogram[dib]:>8} {float(histogram[dib]/n_buckets):.0%} of slots")
+                        s = sum(dib*count for (dib, count) in histogram.items() if dib != 255) / n_entries
+                        print(f"mean DIB of entries: {s}")
+
+                blocks = []
+                current_len = 1
+                prev = int(dib_raw_addr[0])
+                for i in range(1, n_buckets):
+                    dib = int(dib_raw_addr[i])
+                    if (dib == 255) != (prev == 255):
+                        if prev != 255:
+                            blocks += [[i, current_len]]
+                            current_len = 1
+                    else:
+                        current_len += 1
+
+                    prev = dib
+                if prev != 255:
+                    blocks += [[i, current_len]]
+                    # a block may be wrapped around
+                if len(blocks) > 1 and blocks[0][0] == blocks[0][1] and blocks[-1][0] == n_buckets - 1:
+                    blocks[0][1] += blocks[-1][1]
+                    blocks = blocks[0:-1]
+                    print("max block: {}".format(max(blocks, key=lambda a: a[1])))
+                    print("sum block lens: {}".format(sum(b[1] for b in blocks)))
+                    print("mean block len: {}".format(sum(b[1] for b in blocks) / len(blocks)))
+
+            d = d["debug_list_next"]
 
 sd_dump_hashmaps()
diff --git a/tools/git-contrib.sh b/tools/git-contrib.sh
new file mode 100755 (executable)
index 0000000..cc0f991
--- /dev/null
@@ -0,0 +1,6 @@
+#!/bin/sh
+set -eu
+
+git shortlog -s `git describe --abbrev=0`.. | \
+    awk '{ $1=""; print $0 "," }' | \
+    sort -u
diff --git a/tools/hwdb-update.sh b/tools/hwdb-update.sh
new file mode 100755 (executable)
index 0000000..39efd75
--- /dev/null
@@ -0,0 +1,32 @@
+#!/bin/sh
+set -eu
+
+cd "$1"
+
+unset permissive
+if [ "${2:-}" = "-p" ]; then
+    permissive=1
+    shift
+else
+    permissive=0
+fi
+
+if [ "${2:-}" != "-n" ]; then (
+    [ -z "$permissive" ] || set +e
+    set -x
+
+    curl -L -o usb.ids 'http://www.linux-usb.org/usb.ids'
+    curl -L -o pci.ids 'http://pci-ids.ucw.cz/v2.2/pci.ids'
+    curl -L -o ma-large.txt 'http://standards-oui.ieee.org/oui/oui.txt'
+    curl -L -o ma-medium.txt 'http://standards-oui.ieee.org/oui28/mam.txt'
+    curl -L -o ma-small.txt 'http://standards-oui.ieee.org/oui36/oui36.txt'
+    curl -L -o pnp_id_registry.html 'https://uefi.org/uefi-pnp-export'
+    curl -L -o acpi_id_registry.html 'https://uefi.org/uefi-acpi-export'
+) fi
+
+set -x
+./acpi-update.py >20-acpi-vendor.hwdb.base
+patch -p0 -o- 20-acpi-vendor.hwdb.base <20-acpi-vendor.hwdb.patch >20-acpi-vendor.hwdb
+! diff -u 20-acpi-vendor.hwdb.base 20-acpi-vendor.hwdb >20-acpi-vendor.hwdb.patch
+
+./ids_parser.py
index 25b261ea0d4a09b0e78543b404089b693211c31b..a20edc0f341d673534c828d03f732f4b2dff5469 100755 (executable)
@@ -1,14 +1,24 @@
 #!/usr/bin/env python3
 # SPDX-License-Identifier: LGPL-2.1+
 
-# Generate autosuspend rules for devices that have been whitelisted (IE tested)
-# by the Chromium OS team. Please keep this script in sync with:
+# Generate autosuspend rules for devices that have been tested to work properly
+# with autosuspend by the Chromium OS team. Based on
 # https://chromium.googlesource.com/chromiumos/platform2/+/master/power_manager/udev/gen_autosuspend_rules.py
 
-import sys
 import chromiumos.gen_autosuspend_rules
 
-if __name__ == '__main__':
-    if len(sys.argv) > 1:
-        sys.stdout = open(sys.argv[1], 'w')
-    chromiumos.gen_autosuspend_rules.main()
+print('# pci:v<00VENDOR>d<00DEVICE> (8 uppercase hexadecimal digits twice)')
+for entry in chromiumos.gen_autosuspend_rules.PCI_IDS:
+    vendor, device = entry.split(':')
+    vendor = int(vendor, 16)
+    device = int(device, 16)
+    print('pci:v{:08X}d{:08X}*'.format(vendor, device))
+
+print('# usb:v<VEND>p<PROD> (4 uppercase hexadecimal digits twice)')
+for entry in chromiumos.gen_autosuspend_rules.USB_IDS:
+    vendor, product = entry.split(':')
+    vendor = int(vendor, 16)
+    product = int(product, 16)
+    print('usb:v{:04X}p{:04X}*'.format(vendor, product))
+
+print(' ID_AUTOSUSPEND=1')
index da10575f32bcffad9041dc9c9482d94798ebeeb8..b04281490d6089ee4726ca6af5397f21659ffd4c 100755 (executable)
@@ -7,166 +7,6 @@ import re
 from xml_helper import xml_parse, xml_print, tree
 from copy import deepcopy
 
-TEMPLATE = '''\
-<refentry id="systemd.directives" conditional="HAVE_PYTHON">
-
-        <refentryinfo>
-                <title>systemd.directives</title>
-                <productname>systemd</productname>
-        </refentryinfo>
-
-        <refmeta>
-                <refentrytitle>systemd.directives</refentrytitle>
-                <manvolnum>7</manvolnum>
-        </refmeta>
-
-        <refnamediv>
-                <refname>systemd.directives</refname>
-                <refpurpose>Index of configuration directives</refpurpose>
-        </refnamediv>
-
-        <refsect1>
-                <title>Unit directives</title>
-
-                <para>Directives for configuring units, used in unit
-                files.</para>
-
-                <variablelist id='unit-directives' />
-        </refsect1>
-
-        <refsect1>
-                <title>Options on the kernel command line</title>
-
-                <para>Kernel boot options for configuring the behaviour of the
-                systemd process.</para>
-
-                <variablelist id='kernel-commandline-options' />
-        </refsect1>
-
-        <refsect1>
-                <title>Environment variables</title>
-
-                <para>Environment variables understood by the systemd manager
-                and other programs and environment variable-compatible settings.</para>
-
-                <variablelist id='environment-variables' />
-        </refsect1>
-
-        <refsect1>
-                <title>EFI variables</title>
-
-                <para>EFI variables understood by
-                <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>
-                and other programs.</para>
-
-                <variablelist id='efi-variables' />
-        </refsect1>
-
-        <refsect1>
-                <title>UDEV directives</title>
-
-                <para>Directives for configuring systemd units through the
-                udev database.</para>
-
-                <variablelist id='udev-directives' />
-        </refsect1>
-
-        <refsect1>
-                <title>Network directives</title>
-
-                <para>Directives for configuring network links through the
-                net-setup-link udev builtin and networks through
-                systemd-networkd.</para>
-
-                <variablelist id='network-directives' />
-        </refsect1>
-
-        <refsect1>
-                <title>Journal fields</title>
-
-                <para>Fields in the journal events with a well known meaning.</para>
-
-                <variablelist id='journal-directives' />
-        </refsect1>
-
-        <refsect1>
-                <title>PAM configuration directives</title>
-
-                <para>Directives for configuring PAM behaviour.</para>
-
-                <variablelist id='pam-directives' />
-        </refsect1>
-
-        <refsect1>
-                <title><filename>/etc/crypttab</filename> and
-                <filename>/etc/fstab</filename> options</title>
-
-                <para>Options which influence mounted filesystems and
-                encrypted volumes.</para>
-
-                <variablelist id='fstab-options' />
-        </refsect1>
-
-        <refsect1>
-                <title><citerefentry><refentrytitle>systemd.nspawn</refentrytitle><manvolnum>5</manvolnum></citerefentry>
-                directives</title>
-
-                <para>Directives for configuring systemd-nspawn containers.</para>
-
-                <variablelist id='nspawn-directives' />
-        </refsect1>
-
-        <refsect1>
-                <title>Program configuration options</title>
-
-                <para>Directives for configuring the behaviour of the
-                systemd process and other tools through configuration files.</para>
-
-                <variablelist id='config-directives' />
-        </refsect1>
-
-        <refsect1>
-                <title>Command line options</title>
-
-                <para>Command-line options accepted by programs in the
-                systemd suite.</para>
-
-                <variablelist id='options' />
-        </refsect1>
-
-        <refsect1>
-                <title>Constants</title>
-
-                <para>Various constant used and/or defined by systemd.</para>
-
-                <variablelist id='constants' />
-        </refsect1>
-
-        <refsect1>
-                <title>Miscellaneous options and directives</title>
-
-                <para>Other configuration elements which don't fit in
-                any of the above groups.</para>
-
-                <variablelist id='miscellaneous' />
-        </refsect1>
-
-        <refsect1>
-                <title>Files and directories</title>
-
-                <para>Paths and file names referred to in the
-                documentation.</para>
-
-                <variablelist id='filenames' />
-        </refsect1>
-
-        <refsect1>
-                <title>Colophon</title>
-                <para id='colophon' />
-        </refsect1>
-</refentry>
-'''
-
 COLOPHON = '''\
 This index contains {count} entries in {sections} sections,
 referring to {pages} individual manual pages.
@@ -180,9 +20,10 @@ def _extract_directives(directive_groups, formatting, page):
     storopt = directive_groups['options']
     for variablelist in t.iterfind('.//variablelist'):
         klass = variablelist.attrib.get('class')
+        searchpath = variablelist.attrib.get('xpath','./varlistentry/term/varname')
         storvar = directive_groups[klass or 'miscellaneous']
         # <option>s go in OPTIONS, unless class is specified
-        for xpath, stor in (('./varlistentry/term/varname', storvar),
+        for xpath, stor in ((searchpath, storvar),
                             ('./varlistentry/term/option',
                              storvar if klass else storopt)):
             for name in variablelist.iterfind(xpath):
@@ -199,6 +40,13 @@ def _extract_directives(directive_groups, formatting, page):
                         name.tail = ''
                     name.text = text
                     formatting[text] = name
+        extra = variablelist.attrib.get('extra-ref')
+        if extra:
+            stor[extra].append((pagename, section))
+            if extra not in formatting:
+                elt = tree.Element("varname")
+                elt.text= extra
+                formatting[extra] = elt
 
     storfile = directive_groups['filenames']
     for xpath, absolute_only in (('.//refsynopsisdiv//filename', False),
@@ -239,6 +87,18 @@ def _extract_directives(directive_groups, formatting, page):
         storfile[name.text].append((pagename, section))
         formatting[name.text] = name
 
+    storfile = directive_groups['specifiers']
+    for name in t.iterfind(".//table[@class='specifiers']//entry/literal"):
+        if name.text[0] != '%' or name.getparent().text is not None:
+            continue
+        if name.attrib.get('index') == 'false':
+            continue
+        storfile[name.text].append((pagename, section))
+        formatting[name.text] = name
+    for name in t.iterfind(".//literal[@class='specifiers']"):
+        storfile[name.text].append((pagename, section))
+        formatting[name.text] = name
+
 def _make_section(template, name, directives, formatting):
     varlist = template.find(".//*[@id='{}']".format(name))
     for varname, manpages in sorted(directives.items()):
@@ -290,9 +150,9 @@ def _make_page(template, directive_groups, formatting):
 
     return template
 
-def make_page(*xml_files):
+def make_page(template_path, xml_files):
     "Extract directives from xml_files and return XML index tree."
-    template = tree.fromstring(TEMPLATE)
+    template = xml_parse(template_path)
     names = [vl.get('id') for vl in template.iterfind('.//variablelist')]
     directive_groups = {name:collections.defaultdict(list)
                         for name in names}
@@ -307,4 +167,7 @@ def make_page(*xml_files):
 
 if __name__ == '__main__':
     with open(sys.argv[1], 'wb') as f:
-        f.write(xml_print(make_page(*sys.argv[2:])))
+        template_path = sys.argv[2]
+        xml_files = sys.argv[3:]
+        xml = make_page(template_path, xml_files)
+        f.write(xml_print(xml))
index 66027af02e526ad4e1a45fd4097fbbb8a7c7867d..4d206ca0b6bb741f9ce2e0d9d012d469e7a2682b 100755 (executable)
@@ -9,7 +9,7 @@ from xml_helper import xml_parse, xml_print, tree
 MDASH = ' — ' if sys.version_info.major >= 3 else ' -- '
 
 TEMPLATE = '''\
-<refentry id="systemd.index" conditional="HAVE_PYTHON">
+<refentry id="systemd.index">
 
   <refentryinfo>
     <title>systemd.index</title>
diff --git a/tools/make-man-rules.py b/tools/make-man-rules.py
deleted file mode 100755 (executable)
index d86afcb..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-#!/usr/bin/env python3
-# SPDX-License-Identifier: LGPL-2.1+
-
-from __future__ import print_function
-import collections
-import sys
-import os.path
-import pprint
-from xml_helper import xml_parse
-
-def man(page, number):
-    return '{}.{}'.format(page, number)
-
-def xml(file):
-    return os.path.basename(file)
-
-def add_rules(rules, name):
-    xml = xml_parse(name)
-    # print('parsing {}'.format(name), file=sys.stderr)
-    if xml.getroot().tag != 'refentry':
-        return
-    conditional = xml.getroot().get('conditional') or ''
-    rulegroup = rules[conditional]
-    refmeta = xml.find('./refmeta')
-    title = refmeta.find('./refentrytitle').text
-    number = refmeta.find('./manvolnum').text
-    refnames = xml.findall('./refnamediv/refname')
-    target = man(refnames[0].text, number)
-    if title != refnames[0].text:
-        raise ValueError('refmeta and refnamediv disagree: ' + name)
-    for refname in refnames:
-        assert all(refname not in group
-                   for group in rules.values()), "duplicate page name"
-        alias = man(refname.text, number)
-        rulegroup[alias] = target
-        # print('{} => {} [{}]'.format(alias, target, conditional), file=sys.stderr)
-
-def create_rules(xml_files):
-    " {conditional => {alias-name => source-name}} "
-    rules = collections.defaultdict(dict)
-    for name in xml_files:
-        try:
-            add_rules(rules, name)
-        except Exception:
-            print("Failed to process", name, file=sys.stderr)
-            raise
-    return rules
-
-def mjoin(files):
-    return ' \\\n\t'.join(sorted(files) or '#')
-
-MESON_HEADER = '''\
-# Do not edit. Generated by make-man-rules.py.
-# Update with:
-#     ninja -C build man/update-man-rules
-manpages = ['''
-
-MESON_FOOTER = '''\
-]
-# Really, do not edit.'''
-
-def make_mesonfile(rules, dist_files):
-    # reformat rules as
-    # grouped = [ [name, section, [alias...], condition], ...]
-    #
-    # but first create a dictionary like
-    # lists = { (name, condition) => [alias...]
-    grouped = collections.defaultdict(list)
-    for condition, items in rules.items():
-        for alias, name in items.items():
-            group = grouped[(name, condition)]
-            if name != alias:
-                group.append(alias)
-
-    lines = [ [p[0][:-2], p[0][-1], sorted(a[:-2] for a in aliases), p[1]]
-              for p, aliases in sorted(grouped.items()) ]
-    return '\n'.join((MESON_HEADER, pprint.pformat(lines)[1:-1], MESON_FOOTER))
-
-if __name__ == '__main__':
-    pages = sys.argv[1:]
-
-    rules = create_rules(pages)
-    dist_files = (xml(file) for file in pages
-                  if not file.endswith(".directives.xml") and
-                     not file.endswith(".index.xml"))
-    print(make_mesonfile(rules, dist_files))
diff --git a/tools/meson-autosuspend-update.sh b/tools/meson-autosuspend-update.sh
deleted file mode 100755 (executable)
index a4f99eb..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/sh
-set -eu
-
-cd "$1"
-
-(curl -L 'https://chromium.googlesource.com/chromiumos/platform2/+/master/power_manager/udev/gen_autosuspend_rules.py?format=TEXT'; echo) \
-    | base64 -d > gen_autosuspend_rules.py
diff --git a/tools/meson-check-api-docs.sh b/tools/meson-check-api-docs.sh
deleted file mode 100755 (executable)
index bd32056..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/bin/sh
-set -eu
-
-sd_good=0
-sd_total=0
-udev_good=0
-udev_total=0
-
-for symbol in `nm -g --defined-only "$@" | grep " T " | cut -d" " -f3 | sort -u` ; do
-    if test -f ${MESON_BUILD_ROOT}/man/$symbol.3 ; then
-        echo "✓ Symbol $symbol() is documented."
-        good=1
-    else
-        printf "  \x1b[1;31mSymbol $symbol() lacks documentation.\x1b[0m\n"
-        good=0
-    fi
-
-    case $symbol in
-        sd_*)
-            ((sd_good+=good))
-            ((sd_total+=1))
-            ;;
-        udev_*)
-            ((udev_good+=good))
-            ((udev_total+=1))
-            ;;
-        *)
-            echo 'unknown symbol prefix'
-            exit 1
-    esac
-done
-
-echo "libsystemd: $sd_good/$sd_total libudev: $udev_good/$udev_total"
diff --git a/tools/meson-check-compilation.sh b/tools/meson-check-compilation.sh
deleted file mode 100755 (executable)
index ce39e16..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/sh
-set -eu
-
-"$@" '-' -o/dev/null </dev/null
diff --git a/tools/meson-check-help.sh b/tools/meson-check-help.sh
deleted file mode 100755 (executable)
index efe7ed4..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/bin/sh
-set -eu
-
-export SYSTEMD_LOG_LEVEL=info
-
-# output width
-if "$1"  --help | grep -v 'default:' | grep -E -q '.{80}.'; then
-    echo "$(basename "$1") --help output is too wide:"
-    "$1"  --help | awk 'length > 80' | grep -E --color=yes '.{80}'
-    exit 1
-fi
-
-# --help prints something. Also catches case where args are ignored.
-if ! "$1"  --help | grep -q .; then
-    echo "$(basename "$1") --help output is empty."
-    exit 2
-fi
-
-# no --help output to stdout
-if "$1" --help 2>&1 1>/dev/null | grep .; then
-    echo "$(basename "$1") --help prints to stderr"
-    exit 3
-fi
-
-# error output to stderr
-if ! "$1" --no-such-parameter 2>&1 1>/dev/null | grep -q .; then
-    echo "$(basename "$1") with an unknown parameter does not print to stderr"
-    exit 4
-fi
diff --git a/tools/meson-git-contrib.sh b/tools/meson-git-contrib.sh
deleted file mode 100755 (executable)
index b40e97b..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/sh
-set -eu
-
-git shortlog -s `git describe --abbrev=0`.. | \
-    cut -c8- | \
-    sed 's/ / /g' | \
-    awk '{ print $$0 "," }' | \
-    sed -e 's/ / /g' | \
-    sort -u
diff --git a/tools/meson-hwdb-update.sh b/tools/meson-hwdb-update.sh
deleted file mode 100755 (executable)
index 39efd75..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/bin/sh
-set -eu
-
-cd "$1"
-
-unset permissive
-if [ "${2:-}" = "-p" ]; then
-    permissive=1
-    shift
-else
-    permissive=0
-fi
-
-if [ "${2:-}" != "-n" ]; then (
-    [ -z "$permissive" ] || set +e
-    set -x
-
-    curl -L -o usb.ids 'http://www.linux-usb.org/usb.ids'
-    curl -L -o pci.ids 'http://pci-ids.ucw.cz/v2.2/pci.ids'
-    curl -L -o ma-large.txt 'http://standards-oui.ieee.org/oui/oui.txt'
-    curl -L -o ma-medium.txt 'http://standards-oui.ieee.org/oui28/mam.txt'
-    curl -L -o ma-small.txt 'http://standards-oui.ieee.org/oui36/oui36.txt'
-    curl -L -o pnp_id_registry.html 'https://uefi.org/uefi-pnp-export'
-    curl -L -o acpi_id_registry.html 'https://uefi.org/uefi-acpi-export'
-) fi
-
-set -x
-./acpi-update.py >20-acpi-vendor.hwdb.base
-patch -p0 -o- 20-acpi-vendor.hwdb.base <20-acpi-vendor.hwdb.patch >20-acpi-vendor.hwdb
-! diff -u 20-acpi-vendor.hwdb.base 20-acpi-vendor.hwdb >20-acpi-vendor.hwdb.patch
-
-./ids_parser.py
index da0d13a341fed819dae7c8bf17a6ccd40b58594c..cdd5214125fb6374f8e8e817a0da68c0d583781b 100755 (executable)
@@ -5,8 +5,8 @@ set -eu
 # and we need to create the target directory...
 
 mkdir -vp "$(dirname "${DESTDIR:-}$2")"
-if [ "$(dirname $1)" = . ]; then
-    ln -vfs -T "$1" "${DESTDIR:-}$2"
+if [ "$(dirname $1)" = . -o "$(dirname $1)" = .. ]; then
+    ln -vfs -T -- "$1" "${DESTDIR:-}$2"
 else
-    ln -vfs -T --relative "${DESTDIR:-}$1" "${DESTDIR:-}$2"
+    ln -vfs -T --relative -- "${DESTDIR:-}$1" "${DESTDIR:-}$2"
 fi
index 79846f87879cfadc7be758e29fbf669ce0983cec..5b8690b687fe199d8e8dd121aab3f06129068f43 100755 (executable)
@@ -27,9 +27,15 @@ build=$WORK/build
 rm -rf $build
 mkdir -p $build
 
-fuzzflag="oss-fuzz=true"
 if [ -z "$FUZZING_ENGINE" ]; then
     fuzzflag="llvm-fuzz=true"
+else
+    fuzzflag="oss-fuzz=true"
+    if [[ "$SANITIZER" == undefined ]]; then
+        UBSAN_FLAGS="-fsanitize=pointer-overflow -fno-sanitize-recover=pointer-overflow"
+        CFLAGS="$CFLAGS $UBSAN_FLAGS"
+        CXXFLAGS="$CXXFLAGS $UBSAN_FLAGS"
+    fi
 fi
 
 meson $build -D$fuzzflag -Db_lundef=false
diff --git a/tools/update-dbus-docs.py b/tools/update-dbus-docs.py
new file mode 100755 (executable)
index 0000000..f95faaa
--- /dev/null
@@ -0,0 +1,283 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: LGPL-2.1+
+
+import collections
+import sys
+import os
+import shlex
+import subprocess
+import io
+from lxml import etree
+
+PARSER = etree.XMLParser(no_network=True,
+                         remove_comments=False,
+                         strip_cdata=False,
+                         resolve_entities=False)
+
+PRINT_ERRORS = True
+
+class NoCommand(Exception):
+    pass
+
+BORING_INTERFACES = [
+    'org.freedesktop.DBus.Peer',
+    'org.freedesktop.DBus.Introspectable',
+    'org.freedesktop.DBus.Properties',
+]
+
+def print_method(declarations, elem, *, prefix, file, is_signal=False):
+    name = elem.get('name')
+    klass = 'signal' if is_signal else 'method'
+    declarations[klass].append(name)
+
+    print(f'''{prefix}{name}(''', file=file, end='')
+    lead = ',\n' + prefix + ' ' * len(name) + ' '
+
+    for num, arg in enumerate(elem.findall('./arg')):
+        argname = arg.get('name')
+
+        if argname is None:
+            if PRINT_ERRORS:
+                print(f'method {name}: argument {num+1} has no name', file=sys.stderr)
+            argname = 'UNNAMED'
+
+        type = arg.get('type')
+        if not is_signal:
+            direction = arg.get('direction')
+            print(f'''{lead if num > 0 else ''}{direction:3} {type} {argname}''', file=file, end='')
+        else:
+            print(f'''{lead if num > 0 else ''}{type} {argname}''', file=file, end='')
+
+    print(f');', file=file)
+
+ACCESS_MAP = {
+    'read' : 'readonly',
+    'write' : 'readwrite',
+}
+
+def value_ellipsis(type):
+    if type == 's':
+        return "'...'";
+    if type[0] == 'a':
+        inner = value_ellipsis(type[1:])
+        return f"[{inner}{', ...' if inner != '...' else ''}]";
+    return '...'
+
+def print_property(declarations, elem, *, prefix, file):
+    name = elem.get('name')
+    type = elem.get('type')
+    access = elem.get('access')
+
+    declarations['property'].append(name)
+
+    # @org.freedesktop.DBus.Property.EmitsChangedSignal("false")
+    # @org.freedesktop.systemd1.Privileged("true")
+    # readwrite b EnableWallMessages = false;
+
+    for anno in elem.findall('./annotation'):
+        anno_name = anno.get('name')
+        anno_value = anno.get('value')
+        print(f'''{prefix}@{anno_name}("{anno_value}")''', file=file)
+
+    access = ACCESS_MAP.get(access, access)
+    print(f'''{prefix}{access} {type} {name} = {value_ellipsis(type)};''', file=file)
+
+def print_interface(iface, *, prefix, file, print_boring, only_interface, declarations):
+    name = iface.get('name')
+
+    is_boring = (name in BORING_INTERFACES or
+                 only_interface is not None and name != only_interface)
+
+    if is_boring and print_boring:
+        print(f'''{prefix}interface {name} {{ ... }};''', file=file)
+
+    elif not is_boring and not print_boring:
+        print(f'''{prefix}interface {name} {{''', file=file)
+        prefix2 = prefix + '  '
+
+        for num, elem in enumerate(iface.findall('./method')):
+            if num == 0:
+                print(f'''{prefix2}methods:''', file=file)
+            print_method(declarations, elem, prefix=prefix2 + '  ', file=file)
+
+        for num, elem in enumerate(iface.findall('./signal')):
+            if num == 0:
+                print(f'''{prefix2}signals:''', file=file)
+            print_method(declarations, elem, prefix=prefix2 + '  ', file=file, is_signal=True)
+
+        for num, elem in enumerate(iface.findall('./property')):
+            if num == 0:
+                print(f'''{prefix2}properties:''', file=file)
+            print_property(declarations, elem, prefix=prefix2 + '  ', file=file)
+
+        print(f'''{prefix}}};''', file=file)
+
+def document_has_elem_with_text(document, elem, item_repr):
+    predicate = f".//{elem}" # [text() = 'foo'] doesn't seem supported :(
+    for loc in document.findall(predicate):
+        if loc.text == item_repr:
+            return True
+    else:
+        return False
+
+def check_documented(document, declarations):
+    missing = []
+    for klass, items in declarations.items():
+        for item in items:
+            if klass == 'method':
+                elem = 'function'
+                item_repr = f'{item}()'
+            elif klass == 'signal':
+                elem = 'function'
+                item_repr = item
+            elif klass == 'property':
+                elem = 'varname'
+                item_repr = item
+            else:
+                assert False, (klass, item)
+
+            if not document_has_elem_with_text(document, elem, item_repr):
+                if PRINT_ERRORS:
+                    print(f'{klass} {item} is not documented :(')
+                missing.append((klass, item))
+
+    return missing
+
+def xml_to_text(destination, xml, *, only_interface=None):
+    file = io.StringIO()
+
+    declarations = collections.defaultdict(list)
+    interfaces = []
+
+    print(f'''node {destination} {{''', file=file)
+
+    for print_boring in [False, True]:
+        for iface in xml.findall('./interface'):
+            print_interface(iface, prefix='  ', file=file,
+                            print_boring=print_boring,
+                            only_interface=only_interface,
+                            declarations=declarations)
+            name = iface.get('name')
+            if not name in BORING_INTERFACES:
+                interfaces.append(name)
+
+    print(f'''}};''', file=file)
+
+    return file.getvalue(), declarations, interfaces
+
+def subst_output(document, programlisting):
+    executable = programlisting.get('executable', None)
+    if executable is None:
+        # Not our thing
+        return
+    executable = programlisting.get('executable')
+    node = programlisting.get('node')
+    interface = programlisting.get('interface')
+
+    argv = [f'{build_dir}/{executable}', f'--bus-introspect={interface}']
+    print(f'COMMAND: {shlex.join(argv)}')
+
+    try:
+        out = subprocess.check_output(argv, text=True)
+    except FileNotFoundError:
+        print(f'{executable} not found, ignoring', file=sys.stderr)
+        return
+
+    xml = etree.fromstring(out, parser=PARSER)
+
+    new_text, declarations, interfaces = xml_to_text(node, xml, only_interface=interface)
+    programlisting.text = '\n' + new_text + '    '
+
+    if declarations:
+        missing = check_documented(document, declarations)
+        parent = programlisting.getparent()
+
+        # delete old comments
+        for child in parent:
+            if (child.tag == etree.Comment
+                and 'Autogenerated' in child.text):
+                parent.remove(child)
+            if (child.tag == etree.Comment
+                and 'not documented' in child.text):
+                parent.remove(child)
+            if (child.tag == "variablelist"
+                and child.attrib.get("generated",False) == "True"):
+                parent.remove(child)
+
+        # insert pointer for systemd-directives generation
+        the_tail = programlisting.tail #tail is erased by addnext, so save it here.
+        prev_element = etree.Comment("Autogenerated cross-references for systemd.directives, do not edit")
+        programlisting.addnext(prev_element)
+        programlisting.tail = the_tail
+
+        for interface in interfaces:
+            variablelist = etree.Element("variablelist")
+            variablelist.attrib['class'] = 'dbus-interface'
+            variablelist.attrib['generated'] = 'True'
+            variablelist.attrib['extra-ref'] = interface
+
+            prev_element.addnext(variablelist)
+            prev_element.tail = the_tail
+            prev_element = variablelist
+
+        for decl_type,decl_list in declarations.items():
+            for declaration in decl_list:
+                variablelist = etree.Element("variablelist")
+                variablelist.attrib['class'] = 'dbus-'+decl_type
+                variablelist.attrib['generated'] = 'True'
+                if decl_type == 'method' :
+                    variablelist.attrib['extra-ref'] = declaration + '()'
+                else:
+                    variablelist.attrib['extra-ref'] = declaration
+
+                prev_element.addnext(variablelist)
+                prev_element.tail = the_tail
+                prev_element = variablelist
+
+        last_element = etree.Comment("End of Autogenerated section")
+        prev_element.addnext(last_element)
+        prev_element.tail = the_tail
+        last_element.tail = the_tail
+
+        # insert comments for undocumented items
+        for item in reversed(missing):
+            comment = etree.Comment(f'{item[0]} {item[1]} is not documented!')
+            comment.tail = programlisting.tail
+            parent.insert(parent.index(programlisting) + 1, comment)
+
+def process(page):
+    src = open(page).read()
+    xml = etree.fromstring(src, parser=PARSER)
+
+    # print('parsing {}'.format(name), file=sys.stderr)
+    if xml.tag != 'refentry':
+        return
+
+    pls = xml.findall('.//programlisting')
+    for pl in pls:
+        subst_output(xml, pl)
+
+    out_text = etree.tostring(xml, encoding='unicode')
+    # massage format to avoid some lxml whitespace handling idiosyncrasies
+    # https://bugs.launchpad.net/lxml/+bug/526799
+    out_text = (src[:src.find('<refentryinfo')] +
+                out_text[out_text.find('<refentryinfo'):] +
+                '\n')
+
+    with open(page, 'w') as out:
+        out.write(out_text)
+
+if __name__ == '__main__':
+    pages = sys.argv[1:]
+
+    if pages[0].startswith('--build-dir='):
+        build_dir = pages[0].partition('=')[2]
+        pages = pages[1:]
+    else:
+        build_dir = 'build'
+
+    if not os.path.exists(f'{build_dir}/systemd'):
+        exit(f"{build_dir}/systemd doesn't exist. Use --build-dir=.")
+
+    for page in pages:
+        process(page)
diff --git a/tools/update-man-rules.py b/tools/update-man-rules.py
new file mode 100755 (executable)
index 0000000..f5db691
--- /dev/null
@@ -0,0 +1,86 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: LGPL-2.1+
+
+from __future__ import print_function
+import collections
+import sys
+import pprint
+from os.path import basename
+from xml_helper import xml_parse
+
+def man(page, number):
+    return '{}.{}'.format(page, number)
+
+def add_rules(rules, name):
+    xml = xml_parse(name)
+    # print('parsing {}'.format(name), file=sys.stderr)
+    if xml.getroot().tag != 'refentry':
+        return
+    conditional = xml.getroot().get('conditional') or ''
+    rulegroup = rules[conditional]
+    refmeta = xml.find('./refmeta')
+    title = refmeta.find('./refentrytitle').text
+    number = refmeta.find('./manvolnum').text
+    refnames = xml.findall('./refnamediv/refname')
+    target = man(refnames[0].text, number)
+    if title != refnames[0].text:
+        raise ValueError('refmeta and refnamediv disagree: ' + name)
+    for refname in refnames:
+        assert all(refname not in group
+                   for group in rules.values()), "duplicate page name"
+        alias = man(refname.text, number)
+        rulegroup[alias] = target
+        # print('{} => {} [{}]'.format(alias, target, conditional), file=sys.stderr)
+
+def create_rules(xml_files):
+    " {conditional => {alias-name => source-name}} "
+    rules = collections.defaultdict(dict)
+    for name in xml_files:
+        try:
+            add_rules(rules, name)
+        except Exception:
+            print("Failed to process", name, file=sys.stderr)
+            raise
+    return rules
+
+def mjoin(files):
+    return ' \\\n\t'.join(sorted(files) or '#')
+
+MESON_HEADER = '''\
+# Do not edit. Generated by update-man-rules.py.
+# Update with:
+#     ninja -C build man/update-man-rules
+manpages = ['''
+
+MESON_FOOTER = '''\
+]
+# Really, do not edit.'''
+
+def make_mesonfile(rules, dist_files):
+    # reformat rules as
+    # grouped = [ [name, section, [alias...], condition], ...]
+    #
+    # but first create a dictionary like
+    # lists = { (name, condition) => [alias...]
+    grouped = collections.defaultdict(list)
+    for condition, items in rules.items():
+        for alias, name in items.items():
+            group = grouped[(name, condition)]
+            if name != alias:
+                group.append(alias)
+
+    lines = [ [p[0][:-2], p[0][-1], sorted(a[:-2] for a in aliases), p[1]]
+              for p, aliases in sorted(grouped.items()) ]
+    return '\n'.join((MESON_HEADER, pprint.pformat(lines)[1:-1], MESON_FOOTER))
+
+if __name__ == '__main__':
+    pages = sys.argv[1:]
+    pages = (p for p in pages
+             if basename(p) not in {
+                     'systemd.directives.xml',
+                     'systemd.index.xml',
+                     'directives-template.xml'})
+
+    rules = create_rules(pages)
+    dist_files = (basename(p) for p in pages)
+    print(make_mesonfile(rules, dist_files))
index ac86e6274e0c86f2643a9abd3c44f758971d3a0f..ab8a5b5fb8a0879553afc4f31532fe10e96927c7 100755 (executable)
@@ -9,20 +9,25 @@
 # export CONT_NAME="my-fancy-container"
 # travis-ci/managers/debian.sh SETUP RUN CLEANUP
 
-PHASES=(${@:-SETUP RUN RUN_ASAN CLEANUP})
+PHASES=(${@:-SETUP RUN RUN_ASAN_UBSAN CLEANUP})
 DEBIAN_RELEASE="${DEBIAN_RELEASE:-testing}"
-CONT_NAME="${CONT_NAME:-debian-$DEBIAN_RELEASE-$RANDOM}"
+CONT_NAME="${CONT_NAME:-systemd-debian-$DEBIAN_RELEASE}"
 DOCKER_EXEC="${DOCKER_EXEC:-docker exec -it $CONT_NAME}"
 DOCKER_RUN="${DOCKER_RUN:-docker run}"
 REPO_ROOT="${REPO_ROOT:-$PWD}"
-ADDITIONAL_DEPS=(python3-libevdev
-                 python3-pyparsing
-                 clang
-                 perl
-                 libpwquality-dev
-                 libfdisk-dev
-                 libp11-kit-dev
-                 libssl-dev)
+ADDITIONAL_DEPS=(
+    clang
+    fdisk
+    libfdisk-dev
+    libp11-kit-dev
+    libpwquality-dev
+    libssl-dev
+    libzstd-dev
+    perl
+    python3-libevdev
+    python3-pyparsing
+    zstd
+)
 
 function info() {
     echo -e "\033[33;1m$1\033[0m"
@@ -51,18 +56,22 @@ for phase in "${PHASES[@]}"; do
             $DOCKER_EXEC apt-get -y build-dep systemd
             $DOCKER_EXEC apt-get -y install "${ADDITIONAL_DEPS[@]}"
             ;;
-        RUN|RUN_CLANG)
+        RUN|RUN_GCC|RUN_CLANG)
             if [[ "$phase" = "RUN_CLANG" ]]; then
                 ENV_VARS="-e CC=clang -e CXX=clang++"
+                MESON_ARGS="--optimization=1"
             fi
-            docker exec $ENV_VARS -it $CONT_NAME meson --werror -Dtests=unsafe -Dslow-tests=true -Dsplit-usr=true -Dman=true build
+            docker exec $ENV_VARS -it $CONT_NAME meson --werror -Dtests=unsafe -Dslow-tests=true -Dsplit-usr=true -Dman=true $MESON_ARGS build
             $DOCKER_EXEC ninja -v -C build
             docker exec -e "TRAVIS=$TRAVIS" -it $CONT_NAME ninja -C build test
             ;;
-        RUN_ASAN|RUN_CLANG_ASAN)
-            if [[ "$phase" = "RUN_CLANG_ASAN" ]]; then
+        RUN_ASAN_UBSAN|RUN_GCC_ASAN_UBSAN|RUN_CLANG_ASAN_UBSAN)
+            if [[ "$phase" = "RUN_CLANG_ASAN_UBSAN" ]]; then
                 ENV_VARS="-e CC=clang -e CXX=clang++"
-                MESON_ARGS="-Db_lundef=false" # See https://github.com/mesonbuild/meson/issues/764
+                # Build fuzzer regression tests only with clang (for now),
+                # see: https://github.com/systemd/systemd/pull/15886#issuecomment-632689604
+                # -Db_lundef=false: See https://github.com/mesonbuild/meson/issues/764
+                MESON_ARGS="-Db_lundef=false -Dfuzz-tests=true --optimization=1"
             fi
             docker exec $ENV_VARS -it $CONT_NAME meson --werror -Dtests=unsafe -Db_sanitize=address,undefined -Dsplit-usr=true $MESON_ARGS build
             $DOCKER_EXEC ninja -v -C build
index b0f431aac9e15191a526280b69321a779f905572..3a917564ed103b07432aad533f584caca40c8d5c 100755 (executable)
@@ -9,30 +9,52 @@
 # export CONT_NAME="my-fancy-container"
 # travis-ci/managers/fedora.sh SETUP RUN CLEANUP
 
-PHASES=(${@:-SETUP RUN RUN_ASAN CLEANUP})
+PHASES=(${@:-SETUP RUN RUN_ASAN_UBSAN CLEANUP})
 FEDORA_RELEASE="${FEDORA_RELEASE:-rawhide}"
-CONT_NAME="${CONT_NAME:-fedora-$FEDORA_RELEASE-$RANDOM}"
+CONT_NAME="${CONT_NAME:-systemd-fedora-$FEDORA_RELEASE}"
 DOCKER_EXEC="${DOCKER_EXEC:-docker exec -it $CONT_NAME}"
 DOCKER_RUN="${DOCKER_RUN:-docker run}"
 REPO_ROOT="${REPO_ROOT:-$PWD}"
-ADDITIONAL_DEPS=(dnf-plugins-core
-                 jq iputils
-                 hostname libasan
-                 python3-pyparsing
-                 python3-evdev
-                 libubsan
-                 clang
-                 llvm
-                 perl
-                 libfdisk-devel
-                 libpwquality-devel
-                 openssl-devel
-                 p11-kit-devel)
+ADDITIONAL_DEPS=(
+    clang
+    dnf-plugins-core
+    hostname
+    iputils
+    jq
+    libasan
+    libfdisk-devel
+    libfido2-devel
+    libpwquality-devel
+    libubsan
+    libzstd-devel
+    llvm
+    openssl-devel
+    p11-kit-devel
+    perl
+    python3-evdev
+    python3-pyparsing
+)
 
-function info() {
+info() {
     echo -e "\033[33;1m$1\033[0m"
 }
 
+# Simple wrapper which retries given command up to five times
+_retry() {
+    local EC=1
+
+    for i in {1..5}; do
+        if "$@"; then
+            EC=0
+            break
+        fi
+
+        sleep $((i * 5))
+    done
+
+    return $EC
+}
+
 set -e
 
 source "$(dirname $0)/travis_wait.bash"
@@ -43,20 +65,20 @@ for phase in "${PHASES[@]}"; do
             info "Setup phase"
             info "Using Fedora $FEDORA_RELEASE"
             # Pull a Docker image and start a new container
-            docker pull fedora:$FEDORA_RELEASE
+            printf "FROM fedora:$FEDORA_RELEASE\nRUN bash -c 'dnf install -y systemd'\n" | docker build -t fedora-with-systemd/latest -
             info "Starting container $CONT_NAME"
             $DOCKER_RUN -v $REPO_ROOT:/build:rw \
                         -w /build --privileged=true --name $CONT_NAME \
-                        -dit --net=host fedora:$FEDORA_RELEASE /sbin/init
+                        -dit --net=host fedora-with-systemd/latest /sbin/init
             # Wait for the container to properly boot up, otherwise we were
             # running following dnf commands during the initializing/starting
             # (early/late bootup) phase, which caused nasty race conditions
             $DOCKER_EXEC bash -c 'systemctl is-system-running --wait || :'
-            $DOCKER_EXEC dnf makecache
+            _retry $DOCKER_EXEC dnf makecache
             # Install necessary build/test requirements
-            $DOCKER_EXEC dnf -y --exclude selinux-policy\* upgrade
-            $DOCKER_EXEC dnf -y install "${ADDITIONAL_DEPS[@]}"
-            $DOCKER_EXEC dnf -y builddep systemd
+            _retry $DOCKER_EXEC dnf -y --exclude selinux-policy\* upgrade
+            _retry $DOCKER_EXEC dnf -y install "${ADDITIONAL_DEPS[@]}"
+            _retry $DOCKER_EXEC dnf -y builddep systemd
             ;;
         RUN)
             info "Run phase"
@@ -70,8 +92,8 @@ for phase in "${PHASES[@]}"; do
             $DOCKER_EXEC ninja -v -C build
             $DOCKER_EXEC ninja -C build test
             ;;
-        RUN_ASAN|RUN_CLANG_ASAN)
-            if [[ "$phase" = "RUN_CLANG_ASAN" ]]; then
+        RUN_ASAN|RUN_GCC_ASAN_UBSAN|RUN_CLANG_ASAN_UBSAN)
+            if [[ "$phase" = "RUN_CLANG_ASAN_UBSAN" ]]; then
                 ENV_VARS="-e CC=clang -e CXX=clang++"
                 MESON_ARGS="-Db_lundef=false" # See https://github.com/mesonbuild/meson/issues/764
             fi
@@ -92,7 +114,7 @@ for phase in "${PHASES[@]}"; do
             docker rm -f $CONT_NAME
             ;;
         *)
-            echo >&2 "Unknown phase '$phase'"
+            error "Unknown phase '$phase'"
             exit 1
     esac
 done
index c841af121471ab492e868d8ecf30fe77230963c1..b69197f0b53796584a3f5f11d321a20778cc7fd1 100755 (executable)
@@ -6,7 +6,7 @@ set -u
 
 REPO_ROOT=${REPO_ROOT:-$(pwd)}
 
-sudo bash -c "echo 'deb-src http://archive.ubuntu.com/ubuntu/ xenial main restricted universe multiverse' >>/etc/apt/sources.list"
+sudo bash -c "echo 'deb-src http://archive.ubuntu.com/ubuntu/ $(lsb_release -cs) main restricted universe multiverse' >>/etc/apt/sources.list"
 sudo apt-get update -y
 sudo apt-get build-dep systemd -y
 sudo apt-get install -y ninja-build python3-pip python3-setuptools quota
@@ -19,23 +19,3 @@ export PATH="$HOME/.local/bin/:$PATH"
 tools/oss-fuzz.sh
 ./out/fuzz-unit-file -max_total_time=5
 git clean -dxff
-
-wget https://app.fuzzbuzz.io/releases/cli/latest/linux/fuzzbuzz
-chmod +x fuzzbuzz
-./fuzzbuzz validate
-./fuzzbuzz target test fuzz-unit-file --all
-
-git clone https://github.com/google/oss-fuzz /tmp/oss-fuzz
-cd /tmp/oss-fuzz
-sudo ./infra/helper.py pull_images
-
-# docker doesn't like colons in filenames so let's create a directory
-# whose name can be consumed by the -v option.
-# https://github.com/google/oss-fuzz/issues/2428
-t=$(mktemp -d)
-sudo mount --bind "$REPO_ROOT" "$t"
-
-# helper.py is wrapped in script to trick it into thinking it's "interactive"
-# See https://github.com/systemd/systemd/pull/12542#issuecomment-491563572
-sudo script -e -c "./infra/helper.py build_fuzzers --clean --sanitizer=memory systemd $t"
-sudo script -e -c "./infra/helper.py check_build --sanitizer=memory -e ALLOWED_BROKEN_TARGETS_PERCENTAGE=0 systemd"
diff --git a/travis-ci/managers/fuzzit.sh b/travis-ci/managers/fuzzit.sh
deleted file mode 100755 (executable)
index c3d7613..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-#!/bin/bash
-
-set -e
-set -x
-set -u
-
-# This should help to protect the systemd organization on Fuzzit from forks
-# that are activated on Travis CI.
-[[ "$TRAVIS_REPO_SLUG" = "systemd/systemd" ]] || exit 0
-
-REPO_ROOT=${REPO_ROOT:-$(pwd)}
-
-sudo bash -c "echo 'deb-src http://archive.ubuntu.com/ubuntu/ xenial main restricted universe multiverse' >>/etc/apt/sources.list"
-sudo apt-get update -y
-sudo apt-get build-dep systemd -y
-sudo apt-get install -y ninja-build python3-pip python3-setuptools
-# The following should be dropped when debian packaging has been updated to include them
-sudo apt-get install -y libfdisk-dev libp11-kit-dev libssl-dev libpwquality-dev
-pip3 install meson
-
-cd $REPO_ROOT
-export PATH="$HOME/.local/bin/:$PATH"
-
-# We use a subset of https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html#available-checks instead of "undefined"
-# because our fuzzers crash with "pointer-overflow" and "float-cast-overflow":
-# https://github.com/systemd/systemd/pull/12771#issuecomment-502139157
-# https://github.com/systemd/systemd/pull/12812#issuecomment-502780455
-# TODO: figure out what to do about unsigned-integer-overflow: https://github.com/google/oss-fuzz/issues/910
-export SANITIZER="address -fsanitize=alignment,array-bounds,bool,bounds,builtin,enum,float-divide-by-zero,function,integer-divide-by-zero,nonnull-attribute,null,object-size,return,returns-nonnull-attribute,shift,signed-integer-overflow,unreachable,unsigned-integer-overflow,vla-bound,vptr -fno-sanitize-recover=alignment,array-bounds,bool,bounds,builtin,enum,float-divide-by-zero,function,integer-divide-by-zero,nonnull-attribute,null,object-size,return,returns-nonnull-attribute,shift,signed-integer-overflow,unreachable,vla-bound,vptr"
-tools/oss-fuzz.sh
-
-FUZZING_TYPE=${1:-regression}
-if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then
-    FUZZIT_BRANCH="${TRAVIS_BRANCH}"
-else
-    FUZZIT_BRANCH="PR-${TRAVIS_PULL_REQUEST}"
-fi
-
-# Because we want Fuzzit to run on every pull-request and Travis/Azure doesnt support encrypted keys
-# on pull-request we use a write-only key which is ok for now. maybe there will be a better solution in the future
-export FUZZIT_API_KEY=af6992074353998676713818cc6435ef4a750439932dab58b51e9354d6742c54d740a3cd9fc1fc001db82f51734a24bc
-FUZZIT_ADDITIONAL_FILES="./out/src/shared/libsystemd-shared-*.so"
-
-# ASan options are borrowed almost verbatim from OSS-Fuzz
-ASAN_OPTIONS=redzone=32:print_summary=1:handle_sigill=1:allocator_release_to_os_interval_ms=500:print_suppressions=0:strict_memcmp=1:allow_user_segv_handler=0:allocator_may_return_null=1:use_sigaltstack=1:handle_sigfpe=1:handle_sigbus=1:detect_stack_use_after_return=1:alloc_dealloc_mismatch=0:detect_leaks=1:print_scariness=1:max_uar_stack_size_log=16:handle_abort=1:check_malloc_usable_size=0:quarantine_size_mb=64:detect_odr_violation=0:handle_segv=1:fast_unwind_on_fatal=0
-UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1:silence_unsigned_overflow=1
-FUZZIT_ARGS="--type ${FUZZING_TYPE} --branch ${FUZZIT_BRANCH} --revision ${TRAVIS_COMMIT} -e ASAN_OPTIONS=${ASAN_OPTIONS} -e UBSAN_OPTIONS=${UBSAN_OPTIONS}"
-wget -O fuzzit https://github.com/fuzzitdev/fuzzit/releases/latest/download/fuzzit_Linux_x86_64
-chmod +x fuzzit
-
-find out/ -maxdepth 1 -name 'fuzz-*' -executable -type f -exec basename '{}' \; | xargs --verbose -n1 -I%FUZZER% ./fuzzit create job ${FUZZIT_ARGS} %FUZZER%-asan-ubsan out/%FUZZER% ${FUZZIT_ADDITIONAL_FILES}
-
-export SANITIZER="memory -fsanitize-memory-track-origins"
-FUZZIT_ARGS="--type ${FUZZING_TYPE} --branch ${FUZZIT_BRANCH} --revision ${TRAVIS_COMMIT}"
-tools/oss-fuzz.sh
-
-find out/ -maxdepth 1 -name 'fuzz-*' -executable -type f -exec basename '{}' \; | xargs --verbose -n1 -I%FUZZER% ./fuzzit create job ${FUZZIT_ARGS} %FUZZER%-msan out/%FUZZER% ${FUZZIT_ADDITIONAL_FILES}
index 3c553240a2dbbe6306aef3f84ed492b494003117..7c3992e8e03031157ef7cc70d06cc31b2e34721e 100644 (file)
@@ -30,7 +30,9 @@ UtmpIdentifier=cons
 TTYPath=/dev/console
 TTYReset=yes
 TTYVHangup=yes
+m4_ifdef(`ENABLE_LOGIND',,
 KillMode=process
+)m4_dnl
 IgnoreSIGPIPE=no
 SendSIGHUP=yes
 
index 087ab7f9b10b0e703ab6862d20f51ac9815a3dde..e4f9ae57916c882ac8eb4cd4ff5eb0f290b77f4f 100644 (file)
@@ -36,6 +36,8 @@ UtmpIdentifier=pts/%I
 TTYPath=/dev/pts/%I
 TTYReset=yes
 TTYVHangup=yes
+m4_ifdef(`ENABLE_LOGIND',,
 KillMode=process
+)m4_dnl
 IgnoreSIGPIPE=no
 SendSIGHUP=yes
index d259b6b112ae8a9f41a047264c2e086cae26f104..180d9e6a57c9b7a42cd7916396f0552bf98c9285 100644 (file)
@@ -19,7 +19,7 @@ Before=rescue.service
 [Service]
 Environment=HOME=/root
 WorkingDirectory=-/root
-ExecStartPre=-/bin/plymouth --wait quit
+ExecStartPre=-@rootbindir@/plymouth --wait quit
 ExecStart=-@rootlibexecdir@/systemd-sulogin-shell emergency
 Type=idle
 StandardInput=tty-force
index 80e793bb73df61ffeba6b4da76650881edbd40c1..087d68666605047678ad3b34caa34496ed41585a 100644 (file)
@@ -47,7 +47,9 @@ TTYPath=/dev/%I
 TTYReset=yes
 TTYVHangup=yes
 TTYVTDisallocate=yes
+m4_ifdef(`ENABLE_LOGIND',,
 KillMode=process
+)m4_dnl
 IgnoreSIGPIPE=no
 SendSIGHUP=yes
 
index ad2f2a5b35b74d277ed0f1a2666705f7ba4f38b5..810cf5775e497e6d03df5909c5718c09669f5ea2 100644 (file)
@@ -8,7 +8,7 @@
 #  (at your option) any later version.
 
 [Unit]
-Description=Cleanup udevd DB
+Description=Cleanup udev Database
 DefaultDependencies=no
 ConditionPathExists=/etc/initrd-release
 Conflicts=systemd-udevd.service systemd-udevd-control.socket systemd-udevd-kernel.socket systemd-udev-trigger.service systemd-udev-settle.service
index ea91f0cc9ea7df63e5e75d6a7e096e06ffd7d46f..aa2ed115ea2bcff8a146c4a10800547a76494482 100644 (file)
@@ -1,5 +1,7 @@
 # SPDX-License-Identifier: LGPL-2.1+
 
+with_runlevels = conf.get('HAVE_SYSV_COMPAT') == 1
+
 units = [
         ['basic.target',                        ''],
         ['blockdev@.target',                    ''],
@@ -19,20 +21,20 @@ units = [
          'multi-user.target.wants/'],
         ['getty-pre.target',                    ''],
         ['graphical.target',                    '',
-         'runlevel5.target default.target'],
+         (with_runlevels ? 'runlevel5.target default.target' : 'default.target')],
         ['halt.target',                         ''],
         ['hibernate.target',                    'ENABLE_HIBERNATE'],
         ['hybrid-sleep.target',                 'ENABLE_HIBERNATE'],
         ['suspend-then-hibernate.target',       'ENABLE_HIBERNATE'],
-        ['initrd-cleanup.service',              ''],
-        ['initrd-fs.target',                    ''],
-        ['initrd-parse-etc.service',            ''],
-        ['initrd-root-device.target',           ''],
-        ['initrd-root-fs.target',               ''],
-        ['initrd-switch-root.service',          ''],
-        ['initrd-switch-root.target',           ''],
-        ['initrd-udevadm-cleanup-db.service',   ''],
-        ['initrd.target',                       ''],
+        ['initrd-cleanup.service',              'ENABLE_INITRD'],
+        ['initrd-fs.target',                    'ENABLE_INITRD'],
+        ['initrd-parse-etc.service',            'ENABLE_INITRD'],
+        ['initrd-root-device.target',           'ENABLE_INITRD'],
+        ['initrd-root-fs.target',               'ENABLE_INITRD'],
+        ['initrd-switch-root.service',          'ENABLE_INITRD'],
+        ['initrd-switch-root.target',           'ENABLE_INITRD'],
+        ['initrd-udevadm-cleanup-db.service',   'ENABLE_INITRD'],
+        ['initrd.target',                       'ENABLE_INITRD'],
         ['kexec.target',                        ''],
         ['ldconfig.service',                    'ENABLE_LDCONFIG',
          'sysinit.target.wants/'],
@@ -42,7 +44,7 @@ units = [
         ['machines.target',                     'ENABLE_MACHINED'],
         ['modprobe@.service',                   ''],
         ['multi-user.target',                   '',
-         'runlevel2.target runlevel3.target runlevel4.target'],
+         (with_runlevels ? 'runlevel2.target runlevel3.target runlevel4.target' : '')],
         ['network-online.target',               ''],
         ['network-pre.target',                  ''],
         ['network.target',                      ''],
@@ -50,18 +52,18 @@ units = [
         ['nss-user-lookup.target',              ''],
         ['paths.target',                        ''],
         ['poweroff.target',                     '',
-         'runlevel0.target'],
+         (with_runlevels ? 'runlevel0.target' : '')],
         ['printer.target',                      ''],
         ['proc-sys-fs-binfmt_misc.automount',   'ENABLE_BINFMT',
          'sysinit.target.wants/'],
         ['proc-sys-fs-binfmt_misc.mount',       'ENABLE_BINFMT'],
         ['reboot.target',                       '',
-         'runlevel6.target ctrl-alt-del.target'],
+         (with_runlevels ? 'runlevel6.target ctrl-alt-del.target' : 'ctrl-alt-del.target')],
         ['remote-cryptsetup.target',            'HAVE_LIBCRYPTSETUP'],
         ['remote-fs-pre.target',                ''],
         ['remote-fs.target',                    ''],
         ['rescue.target',                       '',
-         'runlevel1.target'],
+         (with_runlevels ? 'runlevel1.target' : '')],
         ['rpcbind.target',                      ''],
         ['shutdown.target',                     ''],
         ['sigpwr.target',                       ''],
@@ -100,7 +102,7 @@ units = [
         ['systemd-firstboot.service',           'ENABLE_FIRSTBOOT',
          'sysinit.target.wants/'],
         ['systemd-halt.service',                ''],
-        ['systemd-initctl.socket',              '',
+        ['systemd-initctl.socket',              'HAVE_SYSV_COMPAT',
          'sockets.target.wants/'],
         ['systemd-journal-catalog-update.service', '',
          'sysinit.target.wants/'],
@@ -139,8 +141,7 @@ units = [
          'sysinit.target.wants/'],
         ['systemd-udevd-kernel.socket',         '',
          'sockets.target.wants/'],
-        ['systemd-userdbd.socket',              'ENABLE_USERDB',
-         'sockets.target.wants/'],
+        ['systemd-userdbd.socket',              'ENABLE_USERDB'],
         ['time-set.target',                     ''],
         ['time-sync.target',                    ''],
         ['timers.target',                       ''],
@@ -179,7 +180,7 @@ in_units = [
          'sysinit.target.wants/'],
         ['systemd-importd.service',              'ENABLE_IMPORTD',
          'dbus-org.freedesktop.import1.service'],
-        ['systemd-initctl.service',               ''],
+        ['systemd-initctl.service',              'HAVE_SYSV_COMPAT'],
         ['systemd-journal-gatewayd.service',     'ENABLE_REMOTE HAVE_MICROHTTPD'],
         ['systemd-journal-remote.service',       'ENABLE_REMOTE HAVE_MICROHTTPD'],
         ['systemd-journal-upload.service',       'ENABLE_REMOTE HAVE_LIBCURL'],
@@ -201,8 +202,7 @@ in_units = [
         ['systemd-portabled.service',            'ENABLE_PORTABLED',
          'dbus-org.freedesktop.portable1.service'],
         ['systemd-userdbd.service',              'ENABLE_USERDB'],
-        ['systemd-homed.service',                'ENABLE_HOMED',
-         'multi-user.target.wants/ dbus-org.freedesktop.home1.service'],
+        ['systemd-homed.service',                'ENABLE_HOMED'],
         ['systemd-quotacheck.service',           'ENABLE_QUOTACHECK'],
         ['systemd-random-seed.service',          'ENABLE_RANDOMSEED',
          'sysinit.target.wants/'],
@@ -227,7 +227,7 @@ in_units = [
         ['systemd-user-sessions.service',        'HAVE_PAM',
          'multi-user.target.wants/'],
         ['systemd-vconsole-setup.service',       'ENABLE_VCONSOLE'],
-        ['systemd-volatile-root.service',        ''],
+        ['systemd-volatile-root.service',        'ENABLE_INITRD'],
         ['systemd-repart.service',               'ENABLE_REPART',
          'sysinit.target.wants/ initrd-root-fs.target.wants/'],
         ['user-runtime-dir@.service',            ''],
index 2a8f034b94bbc8d656fefbaa91c99994a8520583..4106e2d9f751a873b16d7f2349bde459757abb41 100644 (file)
@@ -18,7 +18,7 @@ Before=shutdown.target
 [Service]
 Environment=HOME=/root
 WorkingDirectory=-/root
-ExecStartPre=-/bin/plymouth --wait quit
+ExecStartPre=-@rootbindir@/plymouth --wait quit
 ExecStart=-@rootlibexecdir@/systemd-sulogin-shell rescue
 Type=idle
 StandardInput=tty-force
index 757b86ab2f62b16d758664c109e4695eaebd4191..cab89fb42688536c0a766f9f4a04aa6aa6094b70 100644 (file)
@@ -40,7 +40,9 @@ UtmpIdentifier=%I
 TTYPath=/dev/%I
 TTYReset=yes
 TTYVHangup=yes
+m4_ifdef(`ENABLE_LOGIND',,
 KillMode=process
+)m4_dnl
 IgnoreSIGPIPE=no
 SendSIGHUP=yes
 
index d3022856f2d7357f1715f25f56b838c246419864..6f7967fd003f8143f56fa2858ff82743a99f4a32 100644 (file)
@@ -12,7 +12,6 @@ Description=Load/Save Screen Backlight Brightness of %i
 Documentation=man:systemd-backlight@.service(8)
 DefaultDependencies=no
 Conflicts=shutdown.target
-After=systemd-remount-fs.service
 Before=sysinit.target shutdown.target
 
 [Service]
index 951faa62a161b7357a85b441e764459eab4c36d2..d1ece270749e4667fa155839840860bf9e0855da 100644 (file)
@@ -12,7 +12,7 @@ Description=Process Core Dump
 Documentation=man:systemd-coredump(8)
 DefaultDependencies=no
 Conflicts=shutdown.target
-After=systemd-remount-fs.service systemd-journald.socket
+After=systemd-journald.socket
 Requires=systemd-journald.socket
 Before=shutdown.target
 
index 0942e22d991dc5b2989211affba702c3ad03e9f2..45dc9306ddabe550d263eeb57456a1a46c5ac31f 100644 (file)
@@ -33,3 +33,8 @@ SystemCallArchitectures=native
 SystemCallErrorNumber=EPERM
 SystemCallFilter=@system-service @mount
 @SERVICE_WATCHDOG@
+
+[Install]
+WantedBy=multi-user.target
+Alias=dbus-org.freedesktop.home1.service
+Also=systemd-userdbd.service
index 6181d15d7776f6a61429b342595501c0c5dbcd0c..334f030caa978c838584eb0479cecdbe14c0382c 100644 (file)
@@ -21,6 +21,7 @@ NoNewPrivileges=yes
 PrivateDevices=yes
 PrivateNetwork=yes
 PrivateTmp=yes
+ProtectClock=yes
 ProtectControlGroups=yes
 ProtectHome=yes
 ProtectHostname=yes
index 5144868bcb711912e5b640f27b6eca8bcca3c767..0cb1bfa3ca7296ab0bb1d696d4a774a65330cc7f 100644 (file)
@@ -25,6 +25,7 @@ LockPersonality=yes
 MemoryDenyWriteExecute=yes
 NoNewPrivileges=yes
 OOMScoreAdjust=-250
+ProtectClock=yes
 Restart=always
 RestartSec=0
 RestrictAddressFamilies=AF_UNIX AF_NETLINK
index 23aa828591c434939679b796166237fde60dcf2e..0147b30e0db91804bf1a6f8ca0b741f0e3ce3320 100644 (file)
@@ -8,7 +8,7 @@
 #  (at your option) any later version.
 
 [Unit]
-Description=Login Service
+Description=User Login Management
 Documentation=man:systemd-logind.service(8) man:logind.conf(5)
 Documentation=https://www.freedesktop.org/wiki/Software/systemd/logind
 Documentation=https://www.freedesktop.org/wiki/Software/systemd/multiseat
@@ -36,6 +36,7 @@ LockPersonality=yes
 MemoryDenyWriteExecute=yes
 NoNewPrivileges=yes
 PrivateTmp=yes
+ProtectClock=yes
 ProtectControlGroups=yes
 ProtectHome=yes
 ProtectHostname=yes
index 070d87e1543f88e7fc72fa724afbd5a2e27adc8a..248a8dc64eba736a411875064a5e95da7f67e12f 100644 (file)
@@ -12,6 +12,7 @@ Description=Generate network units from Kernel command line
 Documentation=man:systemd-network-generator.service(8)
 DefaultDependencies=no
 Before=network-pre.target
+Wants=network-pre.target
 
 [Service]
 Type=oneshot
@@ -19,4 +20,4 @@ RemainAfterExit=yes
 ExecStart=@rootlibexecdir@/systemd-network-generator
 
 [Install]
-WantedBy=network-pre.target
+WantedBy=sysinit.target
index 1b69677496d9e44bc5429fe4b087f087dc1bc337..26731468413d7614d4ed1ed64d16367db591dec8 100644 (file)
@@ -26,6 +26,7 @@ ExecStart=!!@rootlibexecdir@/systemd-networkd
 LockPersonality=yes
 MemoryDenyWriteExecute=yes
 NoNewPrivileges=yes
+ProtectClock=yes
 ProtectControlGroups=yes
 ProtectHome=yes
 ProtectKernelModules=yes
index 445193e8d343bb012893cf8e94fd75b6b9b16d31..bc049e5ade924cae38f72ae6040969e738ac8103 100644 (file)
@@ -17,7 +17,7 @@ Before=sockets.target
 [Socket]
 ReceiveBuffer=128M
 ListenNetlink=route 1361
-PassCredentials=yes
+PassPacketInfo=yes
 
 [Install]
 WantedBy=sockets.target
index 89f34afe3411ccec5dc9e053250384742516b36b..6e4827f03c8632f990a3ded3a5cb7649ff26619d 100644 (file)
@@ -13,8 +13,8 @@ Documentation=man:systemd-pstore(8)
 ConditionDirectoryNotEmpty=/sys/fs/pstore
 ConditionVirtualization=!container
 DefaultDependencies=no
-Wants=systemd-remount-fs.service
-After=systemd-remount-fs.service
+Conflicts=shutdown.target
+Before=sysinit.target shutdown.target
 
 [Service]
 Type=oneshot
@@ -23,4 +23,4 @@ RemainAfterExit=yes
 StateDirectory=systemd/pstore
 
 [Install]
-WantedBy=systemd-remount-fs.service
+WantedBy=sysinit.target
index c64e8a909137ab75f3e74cf3ce67363992219cae..f97f99a966aa6edec4316438313d6893ce1dbf62 100644 (file)
@@ -26,5 +26,5 @@ ExecStop=@rootlibexecdir@/systemd-random-seed save
 # This service waits until the kernel's entropy pool is initialized, and may be
 # used as ordering barrier for service that require an initialized entropy
 # pool. Since initialization can take a while on entropy-starved systems, let's
-# increase the time-out substantially here.
+# increase the timeout substantially here.
 TimeoutSec=10min
index 7ce6aefd29fdb9dde76c05d0be064ccb97d99673..9393a64f001f8bfa70503e5f16d79b16e5d22bcf 100644 (file)
@@ -15,11 +15,15 @@ Conflicts=shutdown.target
 After=sysroot.mount
 Before=initrd-root-fs.target shutdown.target
 ConditionVirtualization=!container
+ConditionDirectoryNotEmpty=|/usr/lib/repart.d
+ConditionDirectoryNotEmpty=|/usr/local/lib/repart.d
+ConditionDirectoryNotEmpty=|/etc/repart.d
+ConditionDirectoryNotEmpty=|/run/repart.d
 
 [Service]
 Type=oneshot
 RemainAfterExit=yes
 ExecStart=@rootbindir@/systemd-repart --dry-run=no
 
-# The tool returns 77 if there's no GPT partition table pre-existing
+# The tool returns 77 if there's no existing GPT partition table
 SuccessExitStatus=77
index f73697832ccec4d3f35a726c2df189329932b40c..5723f1c1e2e6c64078a942814e223778c1d4ef5c 100644 (file)
@@ -28,6 +28,7 @@ MemoryDenyWriteExecute=yes
 NoNewPrivileges=yes
 PrivateDevices=yes
 PrivateTmp=yes
+ProtectClock=yes
 ProtectControlGroups=yes
 ProtectHome=yes
 ProtectKernelModules=yes
index 3abb958310dbb8b09c655d7fde61fa4f32bb1c90..af2042f1e515afbaf71918d0961f6ec3fb42be50 100644 (file)
@@ -13,7 +13,7 @@ Documentation=man:systemd-rfkill.service(8)
 DefaultDependencies=no
 BindsTo=sys-devices-virtual-misc-rfkill.device
 Conflicts=shutdown.target
-After=sys-devices-virtual-misc-rfkill.device systemd-remount-fs.service
+After=sys-devices-virtual-misc-rfkill.device
 Before=shutdown.target
 
 [Service]
index f0486a70ab7def2d2c7225dc9c05ae9e43c8094e..92ee94582cd6196ffc53f6cdd3364dd9c824222e 100644 (file)
@@ -13,7 +13,7 @@ Documentation=man:systemd-timesyncd.service(8)
 ConditionCapability=CAP_SYS_TIME
 ConditionVirtualization=!container
 DefaultDependencies=no
-After=systemd-remount-fs.service systemd-sysusers.service
+After=systemd-sysusers.service
 Before=time-set.target sysinit.target shutdown.target
 Conflicts=shutdown.target
 Wants=time-set.target time-sync.target
index ed6a68b864f2850b4134269f1a09cca9f2c412c1..9352c6f59890388669fbc7a84ea11048fce49025 100644 (file)
@@ -12,7 +12,7 @@
 # expect a populated /dev during bootup.
 
 [Unit]
-Description=udev Wait for Complete Device Initialization
+Description=Wait for udev To Complete Device Initialization
 Documentation=man:systemd-udev-settle.service(8)
 DefaultDependencies=no
 Wants=systemd-udevd.service
index 8a625b630599286874e658cb34409bb6bf2210ee..cfe8d61c2aff83162e5fb7a911f4d453cfe985a4 100644 (file)
@@ -8,7 +8,7 @@
 #  (at your option) any later version.
 
 [Unit]
-Description=udev Coldplug all Devices
+Description=Coldplug All udev Devices
 Documentation=man:udev(7) man:systemd-udevd.service(8)
 DefaultDependencies=no
 Wants=systemd-udevd.service
index 5eee69933bde944c9afc7db3779997891ecb3a06..9ada3a6a7418c7a5ab3338896d150a9514afd789 100644 (file)
@@ -8,7 +8,7 @@
 #  (at your option) any later version.
 
 [Unit]
-Description=udev Kernel Device Manager
+Description=Rule-based Manager for Device Events and Files
 Documentation=man:systemd-udevd.service(8) man:udev(7)
 DefaultDependencies=no
 After=systemd-sysusers.service systemd-hwdb-update.service
@@ -16,6 +16,8 @@ Before=sysinit.target
 ConditionPathIsReadWrite=/sys
 
 [Service]
+DeviceAllow=block-* rwm
+DeviceAllow=char-* rwm
 Type=notify
 # Note that udev also adjusts the OOM score internally and will reset the value internally for its workers
 OOMScoreAdjust=-1000
@@ -27,6 +29,7 @@ ExecReload=udevadm control --reload --timeout 0
 KillMode=mixed
 TasksMax=infinity
 PrivateMounts=yes
+ProtectClock=yes
 ProtectHostname=yes
 MemoryDenyWriteExecute=yes
 RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6
index e30ed2109ed5f85fab2a6352a892bb3a4ecd7569..3b7670537305c66e663aa41d5e5a561e4e7f1ceb 100644 (file)
@@ -39,3 +39,6 @@ SystemCallErrorNumber=EPERM
 SystemCallFilter=@system-service
 Type=notify
 @SERVICE_WATCHDOG@
+
+[Install]
+Also=systemd-userdbd.socket
index 1c749ea1d2321b160ec54489ff1da333827ce163..2b4bb7a87a5ee446e6ab322b9053a97c16a5cc91 100644 (file)
@@ -17,3 +17,6 @@ Before=sockets.target
 ListenStream=/run/systemd/userdb/io.systemd.Multiplexer
 Symlinks=/run/systemd/userdb/io.systemd.NameServiceSwitch
 SocketMode=0666
+
+[Install]
+WantedBy=sockets.target
index 27bd0f235cd621e6b51e03bdf49fb31f7e52338e..cf6837852fc2f930ed50e0fe339ecdcf513c9e3a 100644 (file)
@@ -22,4 +22,4 @@ After=swap.target
 What=tmpfs
 Where=/tmp
 Type=tmpfs
-Options=mode=1777,strictatime,nosuid,nodev
+Options=mode=1777,strictatime,nosuid,nodev,size=50%,nr_inodes=400k
index cb8f630b8cde79b606c7ec37f8f06ff7db5024e6..33732e7d59bac2ad597426ee5afd1772adcd3466 100644 (file)
@@ -20,6 +20,10 @@ units = [
         'timers.target',
 ]
 
+if conf.get('ENABLE_XDG_AUTOSTART') == 1
+        units += [ 'xdg-desktop-autostart.target', ]
+endif
+
 foreach file : units
         install_data(file,
                      install_dir : userunitdir)
diff --git a/units/user/xdg-desktop-autostart.target b/units/user/xdg-desktop-autostart.target
new file mode 100644 (file)
index 0000000..22df5a3
--- /dev/null
@@ -0,0 +1,14 @@
+#  SPDX-License-Identifier: LGPL-2.1+
+#
+#  This file is part of systemd.
+#
+#  systemd is free software; you can redistribute it and/or modify it
+#  under the terms of the GNU Lesser General Public License as published by
+#  the Free Software Foundation; either version 2.1 of the License, or
+#  (at your option) any later version.
+
+[Unit]
+Description=Startup of XDG autostart applications
+Documentation=man:systemd.special(7)
+RefuseManualStart=yes
+StopWhenUnneeded=yes